/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira.drafts
 */
package net.ranides.assira.collection.prototype;

import net.ranides.assira.reflection.IClass;

import java.util.Objects;

/**
 * Interface representing entry inside GenericMap.
 * Entries inside GenericMap define strictly key type, but not value type
 * because every entry could contain different value.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @param <K> K
 */
public interface GenericEntry<K> {

    /**
     * Returns current type of stored value
     *
     * @return type
     */
    IClass<?> getType();

    /**
     * Returns current key component of this entry
     *
     * @return key
     */
    GenericKey<K,?> getKey();

    /**
     * Returns current value of this entry. Declared type of value is unknown.
     *
     * @return value
     */
    Object getValue();

    /**
     * Changes value assigned to current key.
     * If value type is not compatible with type allowed by current key,
     * then ClassCastException is thrown
     *
     * @param value value
     * @throws ClassCastException if invalid value
     */
    void setValue(Object value);

    /**
     * Simple, mutable, detached entry.
     * It does not support modification of container GenericMap.
     *
     * @param <K> K
     */
    class Simple<K> implements GenericEntry<K> {

        private final GenericKey<K, ?> key;
        private Object value;

        /**
         * Creates new mutable, detached entry.
         * It is not associated with any map.
         *
         * @param key key
         * @param value value
         */
        public Simple(GenericKey<K, ?> key, Object value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public IClass<?> getType() {
            return key.type();
        }

        @Override
        public GenericKey<K, ?> getKey() {
            return key;
        }

        @Override
        public Object getValue() {
            return value;
        }

        @Override
        public void setValue(Object value) {
            if(!key.type().isInstance(value)) {
                throw new ClassCastException("Assignment is not allowed because " + value + " is not an instance of " + key.type());
            }
            this.value = value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof GenericEntry<?>)) return false;
            GenericEntry<?> simple = (GenericEntry<?>) o;
            return key.equals(simple.getKey()) &&
                    Objects.equals(value, simple.getValue());
        }

        @Override
        public int hashCode() {
            return Objects.hash(key, value);
        }
    }
    
}
