package net.ranides.assira.reflection.walker;

import net.ranides.assira.collection.prototype.PrototypeMap;
import net.ranides.assira.generic.TypeToken;
import net.ranides.assira.reflection.*;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * ResolveContext interfaces.
 *
 */
public class WalkerContexts {

    /**
     * Base interface describing visited field.
     * It stores information both declared and actual type, value and others.
     *
     * ObjectContext implements ResolveContext and stores custom properties.
     * Additionaly, every context should use data from visitor as prototype.
     *
     * If you use ResolveFormat or ResolvePattern, you can use those custom properties.
     *
     * Please note that custom properties have higher priority than methods declared by ObjectContext.
     * For example you can put value under "name" key, and then this value will be returned instead of calling method.
     *
     * @param <T> T
     */
    public interface ObjectContext<T> extends ResolveContext {

        /**
         * IClass representing this interface.
         * This value is returned as "kind" by every ObjectContext implementation.
         */
        IClass<ObjectContext<?>> KIND = new TypeToken<ObjectContext<?>>() {};

        /**
         * This method executes provided visitor.
         * Marks field value as "visited" to break cyclic references.
         *
         * It continues recursive scan, if visitor returned "true"
         *
         * @param visitor visitor
         */
        void accept(ObjectVisitor visitor);

        /**
         * Returns description of enclosing object.
         *
         * @return ObjectContext
         */
        ObjectContext<?> parent();

        /**
         * Returns short name for declared type of object
         *
         * @return String
         */
        String typeName();

        /**
         * Returns declared name of field
         *
         * @return String
         */
        String name();

        /**
         * Returns declared type of field
         *
         * @return IClass
         */
        IClass<? extends T> type();

        /**
         * Returns actual type of field.
         * Please note, that actual type can have more details about subclasses,
         * but less information about generic parameters.
         *
         * @return IClass
         */
        IClass<? extends T> actual();

        /**
         * Returns actual value of field
         *
         * @return Object
         */
        T value();

        /**
         * Returns synthetic id of field.
         * Every visited field receives "id" auto-generated by walker.
         *
         * @return long
         */
        long id();

        /**
         * Returns synthetic id of value.
         * Every visited non-primitive value receives "id" auto-generated by walker.
         *
         * "ref" can be used to avoid cycles. If two different fields have identical value,
         * their ids will be the different, but refs will be the same.
         *
         * @return long
         */
        long ref();

        long hash();

        /**
         * Returns depth of recursive scan
         *
         * @return int
         */
        int depth();

        /**
         * Returns slash separated list of declared type and all enclosing types.
         *
         * @return string
         */
        String typePath();

        /**
         * Returns list of declared type and all enclosing types.
         * Type of root object is first, current type is last.
         *
         * @return list
         */
        List<IClass<?>> typeList();

        /**
         * Returns slash separated list of field name all enclosing fields.
         *
         * @return string
         */
        String namePath();

        /**
         * Returns list of field name all enclosing fields.
         *
         * @return list
         */
        List<String> nameList();

        /**
         * Returns dot separated list of field id all enclosing fields.
         *
         * @return string
         */
        String idPath();

        /**
         * Returns true if this context represents root object, passed to ObjectWalker.
         *
         * @return boolean
         */
        boolean isRoot();

        /**
         * Returns true if this context represents field.
         * Some scanned values are not real fields: for example map entries.
         *
         * @return boolean
         */
        boolean isField();

        /**
         * Returns true if this context represents field and it is transient.
         *
         * @return boolean
         */
        boolean isTransient();

        /**
         * Returns true if this context contains value which was already visited.
         *
         * @return boolean
         */
        boolean isVisited();

        /**
         * Returns true if this context contains readable value
         *
         * @return boolean
         */
        boolean isDefined();

        /**
         * Returns true if this context contains primitive value
         *
         * @return boolean
         */
        boolean isPrimitive();

        /**
         * Returns true if this context contains null value
         *
         * @return boolean
         */
        boolean isNull();

        /**
         * Returns true if this context contains actual value more specific than declared
         *
         * @return boolean
         */
        boolean isSubclass();

        /**
         * Returns true if this context contains actual value of provided type
         *
         * @param type type
         * @return boolean
         */
        boolean isSubclass(IClass<?> type);

        /**
         * Returns interface implemented by context.
         *
         * It is similar to this.getClass() but it does not return specific implementation.
         * It returns just one of interface declared inside WalkerContexts.
         *
         * @return type
         */
        IClass<? extends ObjectContext<?>> kind();

        /**
         * Creates new nested context for field found inside this object.
         *
         * @param field field
         * @return context
         */
        ObjectContext<?> context(IField field);

        /**
         * Creates new nested context for value found inside this object.
         * It shouldn't be field, for example it could be collection item.
         *
         * @param name name
         * @param declared declared
         * @param value value
         * @param <S> S
         * @return context
         */
        <S> ObjectContext<S> context(String name, IClass<S> declared, Object value);

        /**
         * Inserts provided value into custom properties associated with this context.
         *
         * @param values values
         */
        void custom(Map<String, ?> values);

        /**
         * Inserts provided value into custom properties associated with this context.
         *
         * @param key key
         * @param value value
         */
        void custom(String key, Object value);

    }

    /**
     * Specialized context describing arrays.
     *
     * @param <T> T
     */
    public interface ArrayContext<T> extends ObjectContext<T> {

        /**
         * IClass representing this interface.
         * This value is returned as "kind" by every ArrayContext implementation.
         */
        IClass<ArrayContext<?>> KIND = new TypeToken<ArrayContext<?>>() {};

        /**
         * Returns declared component type of array
         *
         * @return type
         */
        IClass<?> component();

        /**
         * Returns size of array
         *
         * @return int
         */
        int size();

        /**
         * Returns array element at specified index
         *
         * @param index index
         * @return Object
         */
        Object at(int index);

    }

    /**
     * Specialized context describing List
     *
     * @param <T> T
     */
    public interface ListContext<T extends List<?>> extends ObjectContext<T> {

        /**
         * IClass representing this interface.
         * This value is returned as "kind" by every ListContext implementation.
         */
        IClass<ListContext<?>> KIND = new TypeToken<ListContext<?>>() {};

        /**
         * Returns declared component type of list
         *
         * @return type
         */
        IClass<?> component();

        /**
         * Returns size of list
         *
         * @return int
         */
        int size();

        /**
         * Returns list element at specified index
         *
         * @param index index
         * @return Object
         */
        Object at(int index);

        /**
         * Returns iterator with elements contained inside list
         *
         * @return iterator
         */
        Iterator<?> iterator();
    }

    /**
     * Specialized context describing Collection
     * It is similar to List, but does not support random access.
     *
     * @param <T> T
     */
    public interface CollectionContext<T extends Collection<?>> extends ObjectContext<T> {

        /**
         * IClass representing this interface.
         * This value is returned as "kind" by every CollectionContext implementation.
         */
        IClass<CollectionContext<?>> KIND = new TypeToken<CollectionContext<?>>() {};

        /**
         * Returns declared component type of collection
         *
         * @return type
         */
        IClass<?> component();

        /**
         * Returns size of collection
         *
         * @return int
         */
        int size();

        /**
         * Returns iterator with elements contained inside collection
         *
         * @return iterator
         */
        Iterator<?> iterator();

    }

    /**
     * Specialized context describing Map
     *
     * @param <T> T
     */
    public interface MapContext<T extends Map<?,?>> extends ObjectContext<T> {

        /**
         * IClass representing this interface.
         * This value is returned as "kind" by every MapContext implementation.
         */
        IClass<MapContext<?>> KIND = new TypeToken<MapContext<?>>() {};

        /**
         * Returns declared key type of map
         *
         * @return type
         */
        IClass<?> keyComponent();

        /**
         * Returns declared value type of map
         *
         * @return type
         */
        IClass<?> valueComponent();

        /**
         * Returns size of map
         *
         * @return int
         */
        int size();

        /**
         * Returns set of entries contained inside map
         *
         * @return set
         */
        Set<? extends Map.Entry<?, ?>> entries();

    }
}
