package gregtech.common.bees;

import forestry.api.apiculture.EnumBeeChromosome;
import forestry.api.arboriculture.EnumTreeChromosome;
import forestry.api.genetics.*;
import forestry.api.lepidopterology.EnumButterflyChromosome;
import forestry.apiculture.flowers.FlowerProvider;
import forestry.core.config.Constants;
import forestry.core.genetics.alleles.*;
import forestry.core.utils.vect.IVect;
import forestry.plugins.PluginManager;
import gregtech.GT_Mod;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class GT_AlleleHelper extends AlleleHelper {

    private static final String modId = Constants.ID;

    private Map<Class<?>, Map<?, ? extends IAllele>> alleleMaps = new HashMap<>();

    public void init() {
        if (PluginManager.Module.APICULTURE.isEnabled()) {
            createAlleles(EnumAllele.Fertility.class, EnumBeeChromosome.FERTILITY);
            createAlleles(EnumAllele.Flowering.class, EnumBeeChromosome.FLOWERING);
        }

        if (PluginManager.Module.APICULTURE.isEnabled() || PluginManager.Module.ARBORICULTURE.isEnabled()) {
            createAlleles(EnumAllele.Territory.class,
                    EnumBeeChromosome.TERRITORY,
                    EnumTreeChromosome.TERRITORY
            );

            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.territoryDefault", get(EnumAllele.Territory.AVERAGE));
        }

        if (PluginManager.Module.APICULTURE.isEnabled() || PluginManager.Module.LEPIDOPTEROLOGY.isEnabled()) {
            createAlleles(EnumAllele.Speed.class,
                    EnumBeeChromosome.SPEED,
                    EnumButterflyChromosome.SPEED
            );
            createAlleles(EnumAllele.Lifespan.class,
                    EnumBeeChromosome.LIFESPAN,
                    EnumButterflyChromosome.LIFESPAN
            );
            createAlleles(EnumAllele.Tolerance.class,
                    EnumBeeChromosome.TEMPERATURE_TOLERANCE,
                    EnumBeeChromosome.HUMIDITY_TOLERANCE,
                    EnumButterflyChromosome.TEMPERATURE_TOLERANCE,
                    EnumButterflyChromosome.HUMIDITY_TOLERANCE
            );
            createAlleles(EnumAllele.Flowers.class,
                    EnumBeeChromosome.FLOWER_PROVIDER,
                    EnumButterflyChromosome.FLOWER_PROVIDER
            );

            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.speedNorm", get(EnumAllele.Speed.NORMAL));
        }

        if (PluginManager.Module.ARBORICULTURE.isEnabled()) {
            createAlleles(EnumAllele.Height.class, EnumTreeChromosome.HEIGHT);
            createAlleles(EnumAllele.Saplings.class, EnumTreeChromosome.FERTILITY);
            createAlleles(EnumAllele.Yield.class, EnumTreeChromosome.YIELD);
            createAlleles(EnumAllele.Fireproof.class, EnumTreeChromosome.FIREPROOF);
            createAlleles(EnumAllele.Maturation.class, EnumTreeChromosome.MATURATION);
            createAlleles(EnumAllele.Sappiness.class, EnumTreeChromosome.SAPPINESS);

            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.heightMax10", get(EnumAllele.Height.AVERAGE));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.saplingsDefault", get(EnumAllele.Saplings.AVERAGE));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.saplingsDouble", get(EnumAllele.Saplings.HIGH));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.saplingsTriple", get(EnumAllele.Saplings.HIGHER));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.yieldDefault", get(EnumAllele.Yield.AVERAGE));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.maturitySlowest", get(EnumAllele.Maturation.SLOWEST));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.maturitySlower", get(EnumAllele.Maturation.SLOWER));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.maturitySlow", get(EnumAllele.Maturation.SLOW));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.maturityAverage", get(EnumAllele.Maturation.AVERAGE));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.maturityFast", get(EnumAllele.Maturation.FAST));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.maturityFaster", get(EnumAllele.Maturation.FASTER));
            AlleleManager.alleleRegistry.registerDeprecatedAlleleReplacement("forestry.maturityFastest", get(EnumAllele.Maturation.FASTEST));
        }

        if (PluginManager.Module.LEPIDOPTEROLOGY.isEnabled()) {
            createAlleles(EnumAllele.Size.class, EnumButterflyChromosome.SIZE);
        }

        Map<Integer, IAlleleInteger> integers = new HashMap<>();
        for (int i = 1; i <= 10; i++) {
            IAlleleInteger alleleInteger = new AlleleInteger(modId, "i", i + "d", i, true);
            AlleleManager.alleleRegistry.registerAllele(alleleInteger,
                    EnumTreeChromosome.GIRTH,
                    EnumButterflyChromosome.METABOLISM,
                    EnumButterflyChromosome.FERTILITY
            );
            integers.put(i, alleleInteger);
        }
        alleleMaps.put(Integer.class, integers);

        Map<Boolean, IAlleleBoolean> booleans = new HashMap<>();
        booleans.put(true, new AlleleBoolean(modId, "bool", true, false));
        booleans.put(false, new AlleleBoolean(modId, "bool", false, false));
        for (IAlleleBoolean alleleBoolean : booleans.values()) {
            AlleleManager.alleleRegistry.registerAllele(alleleBoolean,
                    EnumBeeChromosome.NOCTURNAL,
                    EnumBeeChromosome.TOLERANT_FLYER,
                    EnumBeeChromosome.CAVE_DWELLING,
                    EnumButterflyChromosome.NOCTURNAL,
                    EnumButterflyChromosome.TOLERANT_FLYER,
                    EnumButterflyChromosome.FIRE_RESIST
            );
        }
        alleleMaps.put(Boolean.class, booleans);
    }
    @SuppressWarnings("unchecked")
    public static void initialisation(){
        GT_AlleleHelper helper = new GT_AlleleHelper();

        try {
            helper.alleleMaps = (Map<Class<?>, Map<?, ? extends IAllele>>)
                    FieldUtils.readField(
                        FieldUtils.getField(AlleleHelper.class,"alleleMaps",true),
                        AlleleHelper.instance,
                        true
                    );
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        AlleleHelper.instance = helper;
        //AlleleHelper.instance.init();
    }

    @Override
    public <T extends Enum<T> & IChromosomeType> void set(IAllele[] alleles, T chromosomeType, IAllele allele) {

        if (allele == null) {
            GT_Mod.GT_FML_LOGGER.info("Allele is null!");
            return;
        }

        if (!chromosomeType.getAlleleClass().isInstance(allele)) {
            GT_Mod.GT_FML_LOGGER.info("chromosomeType is not an instance of allele!"+allele.getName());
            return;
        }

        // uncomment this once all addon mods are using the allele registration with IChromosomeType
        //		Collection<IChromosomeType> validTypes = AlleleManager.alleleRegistry.getChromosomeTypes(allele);
        //		if (validTypes.size() > 0 && !validTypes.contains(chromosomeType)) {
        //			throw new IllegalArgumentException("Allele can't applied to this Chromosome type. Expected: " + validTypes + " Got: " + chromosomeType);
        //		}

        alleles[chromosomeType.ordinal()] = allele;
    }

    @Override
    public <T extends Enum<T> & IChromosomeType> void set(IAllele[] alleles, T chromosomeType, IAlleleValue value) {
        set(alleles, chromosomeType, get(value));
    }

    @Override
    public <T extends Enum<T> & IChromosomeType> void set(IAllele[] alleles, T chromosomeType, boolean value) {
        set(alleles, chromosomeType, get(value));
    }

    @Override
    public <T extends Enum<T> & IChromosomeType> void set(IAllele[] alleles, T chromosomeType, int value) {
        set(alleles, chromosomeType, get(value));
    }

    private IAllele get(Object value) {
        Class<?> valueClass = value.getClass();
        Map<?, ? extends IAllele> map = alleleMaps.get(valueClass);
        if (map == null) {
            throw new IllegalArgumentException("There is no IAllele type for: " + valueClass + ' ' + value);
        }
        IAllele allele = map.get(value);
        if (allele == null) {
            allele = new IAllele() {
                @Override
                public String getUID() {
                    return "NOT_FOUND";
                }

                @Override
                public boolean isDominant() {
                    return false;
                }

                @Override
                public String getName() {
                    return "NOT_FOUND";
                }

                @Override
                public String getUnlocalizedName() {
                    return "NOT_FOUND";
                }
            };
        }
        return allele;
    }

    private <K extends Enum<K> & IAlleleValue<V>, V> void createAlleles(Class<K> enumClass, IChromosomeType... types) {
        String category = enumClass.getSimpleName().toLowerCase(Locale.ENGLISH);
        EnumMap<K, IAllele> map = new EnumMap<>(enumClass);
        for (K enumValue : enumClass.getEnumConstants()) {
            IAllele allele = createAllele(category, enumValue, types);
            map.put(enumValue, allele);
        }
        alleleMaps.put(enumClass, map);
    }
    private static <K extends IAlleleValue<V>, V> IAllele createAllele(String category, K enumValue, IChromosomeType... types) {
        V value = enumValue.getValue();
        boolean isDominant = enumValue.isDominant();
        String name = enumValue.toString().toLowerCase(Locale.ENGLISH);

        Class<?> valueClass = value.getClass();
        if (Float.class.isAssignableFrom(valueClass)) {
            return AlleleManager.alleleFactory.createFloat(modId, category, name, (Float) value, isDominant, types);
        } else if (Integer.class.isAssignableFrom(valueClass)) {
            return AlleleManager.alleleFactory.createInteger(modId, category, name, (Integer) value, isDominant, types);
        } else if (IVect.class.isAssignableFrom(valueClass)) {
            IVect area = (IVect) value;
            return AlleleManager.alleleFactory.createArea(modId, category, name, area.getX(), area.getY(), area.getZ(), isDominant, types);
        } else if (Boolean.class.isAssignableFrom(valueClass)) {
            return AlleleManager.alleleFactory.createBoolean(modId, category, (Boolean) value, isDominant, types);
        } else if (EnumTolerance.class.isAssignableFrom(valueClass)) {
            IAlleleTolerance alleleTolerance = new AlleleTolerance(modId, category, name, (EnumTolerance) value, isDominant);
            AlleleManager.alleleRegistry.registerAllele(alleleTolerance, types);
            return alleleTolerance;
        } else if (FlowerProvider.class.isAssignableFrom(valueClass)) {
            return AlleleManager.alleleFactory.createFlowers(modId, category, name, (FlowerProvider) value, isDominant, types);
        }
        throw new RuntimeException("could not create allele for category: " + category + " and value " + valueClass);
    }
}