package me.km.world;

import net.minecraft.world.Difficulty;
import net.minecraft.world.storage.DerivedWorldInfo;
import net.minecraft.world.storage.WorldInfo;
import me.hammerle.snuviscript.config.SnuviConfig;
import me.km.ObjectRegistry;
import me.km.Server;
import me.km.utils.ReflectionUtils;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.GameRules;
import static net.minecraft.world.GameRules.*;
import net.minecraft.world.WorldType;
import net.minecraft.world.biome.provider.BiomeProviderType;
import net.minecraft.world.biome.provider.OverworldBiomeProviderSettings;

public class ModWorldInfo extends DerivedWorldInfo {
    public static enum Type {
        VANILLA, VOID, DESERT, SNOW;

        public BiomeProviderType<OverworldBiomeProviderSettings, ModBiomeProvider> getProvider() {
            switch(this) {
                case DESERT:
                    return ObjectRegistry.DESERT_BIOME_PROVIDER;
                case SNOW:
                    return ObjectRegistry.SNOW_BIOME_PROVIDER;
            }
            return null;
        }
    }

    private final SnuviConfig config;

    private long gameTime;
    private long dayTime;
    private int clearWeatherTime;
    private boolean raining;
    private int rainTime;
    private boolean thundering;
    private int thunderTime;
    private Difficulty difficulty;
    private final GameRules gameRules = new GameRules();
    private int spawnX;
    private int spawnY;
    private int spawnZ;
    private long seed = -1;

    private Type type;

    public ModWorldInfo(WorldInfo info, String name, MinecraftServer server) {
        super(info);

        config = new SnuviConfig("worlddata", name);
        if(config.exists()) {
            config.load();
        }
        onLoad(info, server);

        org.apache.logging.log4j.LogManager.getLogger().warn("status: " + type);
    }

    private void onLoad(WorldInfo info, MinecraftServer server) {
        String configSeed = config.getString("seed", null);
        if(configSeed != null) {
            seed = configSeed.hashCode();
        }
        spawnX = config.getInt("spawnX", info.getSpawnX());
        spawnY = config.getInt("spawnY", info.getSpawnY());
        spawnZ = config.getInt("spawnZ", info.getSpawnZ());
        gameTime = config.getLong("gameTime", info.getGameTime());
        dayTime = config.getLong("dayTime", info.getDayTime());
        clearWeatherTime = config.getInt("clearWeatherTime", info.getClearWeatherTime());
        raining = config.getBoolean("raining", info.isRaining());
        rainTime = config.getInt("rainTime", info.getRainTime());
        thundering = config.getBoolean("thundering", info.isThundering());
        thunderTime = config.getInt("thunderTime", info.getThunderTime());

        String diffi = config.getString("difficulty");
        if(diffi == null) {
            difficulty = info.getDifficulty();
        } else {
            try {
                difficulty = Difficulty.valueOf(diffi);
            } catch(Exception ex) {
                difficulty = info.getDifficulty();
            }
        }

        GameRules rules = info.getGameRulesInstance();
        gameRules.get(DO_FIRE_TICK).set(config.getBoolean("doFireTick", rules.getBoolean(DO_FIRE_TICK)), server);
        gameRules.get(MOB_GRIEFING).set(config.getBoolean("mobGriefing", rules.getBoolean(MOB_GRIEFING)), server);
        gameRules.get(KEEP_INVENTORY).set(config.getBoolean("keepInventory", rules.getBoolean(KEEP_INVENTORY)), server);
        gameRules.get(DO_MOB_SPAWNING).set(config.getBoolean("doMobSpawning", rules.getBoolean(DO_MOB_SPAWNING)), server);
        gameRules.get(DO_MOB_LOOT).set(config.getBoolean("doMobLoot", rules.getBoolean(DO_MOB_LOOT)), server);
        gameRules.get(DO_TILE_DROPS).set(config.getBoolean("doTileDrops", rules.getBoolean(DO_TILE_DROPS)), server);
        gameRules.get(DO_ENTITY_DROPS).set(config.getBoolean("doEntityDrops", rules.getBoolean(DO_ENTITY_DROPS)), server);
        gameRules.get(COMMAND_BLOCK_OUTPUT).set(config.getBoolean("commandBlockOutput", rules.getBoolean(COMMAND_BLOCK_OUTPUT)), server);
        gameRules.get(NATURAL_REGENERATION).set(config.getBoolean("naturalRegeneration", rules.getBoolean(NATURAL_REGENERATION)), server);
        gameRules.get(DO_DAYLIGHT_CYCLE).set(config.getBoolean("doDaylightCycle", rules.getBoolean(DO_DAYLIGHT_CYCLE)), server);
        gameRules.get(LOG_ADMIN_COMMANDS).set(config.getBoolean("logAdminCommands", rules.getBoolean(LOG_ADMIN_COMMANDS)), server);
        gameRules.get(SHOW_DEATH_MESSAGES).set(config.getBoolean("showDeathMessages", rules.getBoolean(SHOW_DEATH_MESSAGES)), server);
        ReflectionUtils.setIntegerValue(gameRules.get(RANDOM_TICK_SPEED), config.getInt("randomTickSpeed", rules.getInt(RANDOM_TICK_SPEED)));
        gameRules.get(SEND_COMMAND_FEEDBACK).set(config.getBoolean("sendCommandFeedback", rules.getBoolean(SEND_COMMAND_FEEDBACK)), server);
        gameRules.get(REDUCED_DEBUG_INFO).set(config.getBoolean("reducedDebugInfo", rules.getBoolean(REDUCED_DEBUG_INFO)), server);
        gameRules.get(SPECTATORS_GENERATE_CHUNKS).set(config.getBoolean("spectatorsGenerateChunks", rules.getBoolean(SPECTATORS_GENERATE_CHUNKS)), server);
        ReflectionUtils.setIntegerValue(gameRules.get(SPAWN_RADIUS), config.getInt("spawnRadius", rules.getInt(SPAWN_RADIUS)));
        gameRules.get(DISABLE_ELYTRA_MOVEMENT_CHECK).set(config.getBoolean("disableElytraMovementCheck", rules.getBoolean(DISABLE_ELYTRA_MOVEMENT_CHECK)), server);
        ReflectionUtils.setIntegerValue(gameRules.get(MAX_ENTITY_CRAMMING), config.getInt("maxEntityCramming", rules.getInt(MAX_ENTITY_CRAMMING)));
        gameRules.get(DO_WEATHER_CYCLE).set(config.getBoolean("doWeatherCycle", rules.getBoolean(DO_WEATHER_CYCLE)), server);
        gameRules.get(DO_LIMITED_CRAFTING).set(config.getBoolean("doLimitedCrafting", rules.getBoolean(DO_LIMITED_CRAFTING)), server);
        ReflectionUtils.setIntegerValue(gameRules.get(MAX_COMMAND_CHAIN_LENGTH), config.getInt("maxCommandChainLength", rules.getInt(MAX_COMMAND_CHAIN_LENGTH)));
        gameRules.get(ANNOUNCE_ADVANCEMENTS).set(config.getBoolean("announceAdvancements", rules.getBoolean(ANNOUNCE_ADVANCEMENTS)), server);
        gameRules.get(DISABLE_RAIDS).set(config.getBoolean("disableRaids", rules.getBoolean(DISABLE_RAIDS)), server);
        gameRules.get(DO_INSOMNIA).set(config.getBoolean("doInsomnia", rules.getBoolean(DO_INSOMNIA)), server);
        gameRules.get(DO_IMMEDIATE_RESPAWN).set(config.getBoolean("doImmediateRespawn", rules.getBoolean(DO_IMMEDIATE_RESPAWN)), server);

        try {
            type = Type.valueOf(config.getString("type", ""));
        } catch(IllegalArgumentException ex) {
            type = Type.VOID;
        }
    }

    public void onSave() {
        config.set("spawnX", spawnX);
        config.set("spawnY", spawnY);
        config.set("spawnZ", spawnZ);
        config.set("gameTime", gameTime);
        config.set("dayTime", dayTime);
        config.set("clearWeatherTime", clearWeatherTime);
        config.set("raining", raining);
        config.set("rainTime", rainTime);
        config.set("thundering", thundering);
        config.set("thunderTime", thunderTime);
        config.set("difficulty", difficulty);

        config.set("doFireTick", gameRules.getBoolean(DO_FIRE_TICK));
        config.set("mobGriefing", gameRules.getBoolean(MOB_GRIEFING));
        config.set("keepInventory", gameRules.getBoolean(KEEP_INVENTORY));
        config.set("doMobSpawning", gameRules.getBoolean(DO_MOB_SPAWNING));
        config.set("doMobLoot", gameRules.getBoolean(DO_MOB_LOOT));
        config.set("doTileDrops", gameRules.getBoolean(DO_TILE_DROPS));
        config.set("doEntityDrops", gameRules.getBoolean(DO_ENTITY_DROPS));
        config.set("commandBlockOutput", gameRules.getBoolean(COMMAND_BLOCK_OUTPUT));
        config.set("naturalRegeneration", gameRules.getBoolean(NATURAL_REGENERATION));
        config.set("doDaylightCycle", gameRules.getBoolean(DO_DAYLIGHT_CYCLE));
        config.set("logAdminCommands", gameRules.getBoolean(LOG_ADMIN_COMMANDS));
        config.set("showDeathMessages", gameRules.getBoolean(SHOW_DEATH_MESSAGES));
        config.set("randomTickSpeed", gameRules.getInt(RANDOM_TICK_SPEED));
        config.set("sendCommandFeedback", gameRules.getBoolean(SEND_COMMAND_FEEDBACK));
        config.set("reducedDebugInfo", gameRules.getBoolean(REDUCED_DEBUG_INFO));
        config.set("spectatorsGenerateChunks", gameRules.getBoolean(SPECTATORS_GENERATE_CHUNKS));
        config.set("spawnRadius", gameRules.getInt(SPAWN_RADIUS));
        config.set("disableElytraMovementCheck", gameRules.getBoolean(DISABLE_ELYTRA_MOVEMENT_CHECK));
        config.set("maxEntityCramming", gameRules.getInt(MAX_ENTITY_CRAMMING));
        config.set("doWeatherCycle", gameRules.getBoolean(DO_WEATHER_CYCLE));
        config.set("doLimitedCrafting", gameRules.getBoolean(DO_LIMITED_CRAFTING));
        config.set("maxCommandChainLength", gameRules.getInt(MAX_COMMAND_CHAIN_LENGTH));
        config.set("announceAdvancements", gameRules.getBoolean(ANNOUNCE_ADVANCEMENTS));
        config.set("disableRaids", gameRules.getBoolean(DISABLE_RAIDS));
        config.set("doInsomnia", gameRules.getBoolean(DO_INSOMNIA));
        config.set("doImmediateRespawn", gameRules.getBoolean(DO_IMMEDIATE_RESPAWN));

        config.set("type", type);

        config.save();
    }

    @Override
    public long getGameTime() {
        return this.gameTime;
    }

    @Override
    public long getDayTime() {
        return this.dayTime;
    }

    @Override
    public void setGameTime(long time) {
        this.gameTime = time;
    }

    @Override
    public void setDayTime(long time) {
        this.dayTime = time;
    }

    @Override
    public int getClearWeatherTime() {
        return this.clearWeatherTime;
    }

    @Override
    public void setClearWeatherTime(int cleanWeatherTimeIn) {
        this.clearWeatherTime = cleanWeatherTimeIn;
    }

    @Override
    public boolean isThundering() {
        return this.thundering;
    }

    @Override
    public void setThundering(boolean thunderingIn) {
        this.thundering = thunderingIn;
    }

    @Override
    public int getThunderTime() {
        return this.thunderTime;
    }

    @Override
    public void setThunderTime(int time) {
        this.thunderTime = time;
    }

    @Override
    public boolean isRaining() {
        return this.raining;
    }

    @Override
    public void setRaining(boolean isRaining) {
        this.raining = isRaining;
    }

    @Override
    public int getRainTime() {
        return this.rainTime;
    }

    @Override
    public void setRainTime(int time) {
        this.rainTime = time;
    }

    @Override
    public Difficulty getDifficulty() {
        return this.difficulty;
    }

    @Override
    public void setDifficulty(Difficulty newDifficulty) {
        this.difficulty = newDifficulty;
    }

    @Override
    public GameRules getGameRulesInstance() {
        return gameRules;
    }

    @Override
    public WorldType getGenerator() {
        if(type == Type.VOID) {
            return WorldType.FLAT;
        }
        return super.getGenerator();
    }

    public Type getType() {
        return type;
    }

    @Override
    public CompoundNBT getGeneratorOptions() {
        if(type == Type.VOID) {
            CompoundNBT base = new CompoundNBT();

            base.putString("biome", "minecraft:the_void");

            ListNBT list = new ListNBT();

            base.put("layers", list);

            CompoundNBT layer = new CompoundNBT();
            layer.putString("block", "minecraft:air");
            layer.putByte("height", (byte) 1);

            list.add(layer);

            return base;
        }
        return super.getGeneratorOptions();
    }

    @Override
    public void setSpawn(BlockPos spawnPoint) {
        spawnX = spawnPoint.getX();
        spawnY = spawnPoint.getY();
        spawnZ = spawnPoint.getZ();
    }

    @Override
    public int getSpawnX() {
        return spawnX;
    }

    @Override
    public int getSpawnY() {
        return spawnY;
    }

    @Override
    public int getSpawnZ() {
        return spawnZ;
    }

    @Override
    public long getSeed() {
        if(seed != -1) {
            return seed;
        }
        return super.getSeed();
    }
}
