package dev.rollczi.litecommands.meta;

import dev.rollczi.litecommands.reflect.type.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public interface MetaType<T> {

    T cast(Object value);

    default T in(T value) {
        return value;
    }

    default T out(T value) {
        return value;
    }

    static <E> MetaType<List<E>> list() {
        return new MetaTypeList<>();
    }

    static <E> MetaType<Set<E>> set() {
        return new MetaTypeSet<>();
    }

    static <T> MetaType<T> of(Class<T> type) {
        return new MetaTypeImpl<>(TypeToken.of(type));
    }

    static <T> MetaType<T> of(TypeToken<T> type) {
        return new MetaTypeImpl<>(type);
    }

    class MetaTypeImpl<T> implements MetaType<T> {

        private final TypeToken<T> type;

        public MetaTypeImpl(TypeToken<T> type) {
            this.type = type;
        }

        @Override
        public T cast(Object value) {
            return this.type.getRawType().cast(value);
        }

        @Override
        public String toString() {
            return this.type.getRawType().getTypeName();
        }
    }

    class MetaTypeList<T> implements MetaType<List<T>> {

        @Override
        @SuppressWarnings("unchecked")
        public List<T> cast(Object value) {
            return (List<T>) value;
        }

        @Override
        public List<T> in(List<T> value) {
            return new ArrayList<>(value);
        }

        @Override
        public List<T> out(List<T> value) {
            return new ArrayList<>(value);
        }

        @Override
        public String toString() {
            return "List<T>";
        }
    }

    class MetaTypeSet<T> implements MetaType<Set<T>> {

        @Override
        @SuppressWarnings("unchecked")
        public Set<T> cast(Object value) {
            return (Set<T>) value;
        }

        @Override
        public Set<T> in(Set<T> value) {
            return new HashSet<>(value);
        }

        @Override
        public Set<T> out(Set<T> value) {
            return new HashSet<>(value);
        }

        @Override
        public String toString() {
            return "Set<T>";
        }
    }

}
