package tc.oc.pgm.xml.parser;

import com.google.inject.TypeLiteral;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.EntityType;
import org.bukkit.material.MaterialData;
import org.bukkit.scoreboard.Team;
import org.bukkit.util.Vector;
import java.time.Duration;
import tc.oc.commons.bukkit.localization.MessageTemplate;
import tc.oc.commons.core.inject.SingletonManifest;
import tc.oc.commons.core.reflect.ResolvableType;
import tc.oc.commons.core.reflect.TypeArgument;
import tc.oc.commons.core.reflect.Types;
import tc.oc.commons.core.util.NumberFactory;
import tc.oc.pgm.xml.property.DurationProperty;
import tc.oc.pgm.xml.property.MessageTemplateProperty;
import tc.oc.pgm.xml.property.NumberProperty;
import tc.oc.pgm.xml.property.PercentagePropertyFactory;
import tc.oc.pgm.xml.property.PropertyManifest;

/**
 * Configure {@link Parser}s
 */
public class ParserManifest extends SingletonManifest implements ParserBinders {

    @Override
    protected void configure() {
        NumberFactory.numberTypes().forEach(type -> bindNumber((Class) type));

        bindPrimitiveParser(Boolean.class).to(BooleanParser.class);
        bindPrimitiveParser(String.class).to(StringParser.class);
        bindPrimitiveParser(Duration.class).to(DurationParser.class);
        bindPrimitiveParser(ImVector.class).to(new TypeLiteral<VectorParser<Double>>(){});
        bindPrimitiveParser(Vector.class).to((TypeLiteral) new TypeLiteral<PrimitiveParser<ImVector>>(){});
        bindPrimitiveParser(Team.OptionStatus.class).to(TeamRelationParser.class);
        bindPrimitiveParser(MessageTemplate.class).to(MessageTemplateParser.class);
        bindPrimitiveParser(Material.class).to(MaterialParser.class);
        bindPrimitiveParser(MaterialData.class).to(MaterialDataParser.class);
        bindPrimitiveParser(Attribute.class).to(AttributeParser.class);

        bind(PercentageParser.class);
        bind(PercentagePropertyFactory.class);

        install(new EnumPropertyManifest<ChatColor>(){});
        install(new EnumPropertyManifest<EntityType>(){});
        install(new EnumPropertyManifest<DyeColor>(){});
        // etc...

        install(new PropertyManifest<>(Boolean.class));
        install(new PropertyManifest<>(String.class));
        install(new PropertyManifest<>(Duration.class, DurationProperty.class));
        install(new PropertyManifest<>(ImVector.class));
        install(new PropertyManifest<>(Vector.class));
        install(new PropertyManifest<>(MessageTemplate.class, MessageTemplateProperty.class));
    }

    private <T extends Number & Comparable<T>> void bindNumber(Class<T> rawType) {
        final TypeLiteral<T> type = TypeLiteral.get(rawType);
        final TypeArgument<T> typeArg = new TypeArgument<T>(type){};

        final TypeLiteral<NumberParser<T>> parserType = new ResolvableType<NumberParser<T>>(){}.with(typeArg);
        bind(parserType);
        bind(new ResolvableType<TransfiniteParser<T>>(){}.with(typeArg)).to(parserType);
        bind(new ResolvableType<PrimitiveParser<T>>(){}.with(typeArg)).to(parserType);
        bind(new ResolvableType<Parser<T>>(){}.with(typeArg)).to(parserType);

        final TypeLiteral<VectorParser<T>> vectorParserType = new ResolvableType<VectorParser<T>>(){}.with(typeArg);
        bind(vectorParserType);

        install(new PropertyManifest<>(type, new ResolvableType<NumberProperty<T>>(){}.with(typeArg)));

        if(Types.isAssignable(Comparable.class, type)) {
            install(new RangeParserManifest(type));
        }
    }
}
