package com.zivilon.cinder_loe.world.event;

import com.zivilon.cinder_loe.droptables.DropTable;
import com.zivilon.cinder_loe.droptables.DropContext;
import com.zivilon.cinder_loe.droptables.DropTable.SingleItemDrop;
import com.zivilon.cinder_loe.entity.*;
import com.zivilon.cinder_loe.util.*;
import com.zivilon.cinder_loe.world.event.WarbandFaction.Troop;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.nbt.*;

import net.minecraft.server.MinecraftServer;
import net.minecraft.util.*;
import net.minecraft.world.World;

import cpw.mods.fml.common.FMLCommonHandler;

import lotr.common.LOTRDimension;
import lotr.common.LOTRMod;
import lotr.common.entity.npc.LOTRSpeech;
import lotr.common.entity.npc.LOTREntityNPC;
import lotr.common.fac.LOTRFaction;
import lotr.common.world.biome.LOTRBiome;
import lotr.common.world.map.LOTRWaypoint;
import lotr.common.world.map.LOTRConquestGrid;
import lotr.common.world.map.LOTRConquestZone;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;

public class Warband {
    public static Random random = new Random();
    public static World world;
    public static long last_warband_timestamp = System.currentTimeMillis() / 1000L - (60*60*2); // Initialize at 2 hour cooldown on server startup

    static {
        if (FMLCommonHandler.instance().getSide().isServer()) {
            world = MinecraftServer.getServer().worldServerForDimension(LOTRDimension.MIDDLE_EARTH.dimensionID);
        }
    }

    public int x = 0;
    public int z = 0;
    public String direction = null;
    public String waypoint_name = null;
    public WarbandFaction faction = null;
    public UUID warband_uuid = null;

    public Warband() {
        return;
    }

    public static LOTRWaypoint get_random_waypoint() {
        LOTRWaypoint[] waypoints = LOTRWaypoint.values();
        return waypoints[random.nextInt(waypoints.length)];
    }

    public static void initialize_warband() {
        LOTRWaypoint waypoint = get_random_waypoint();
        initialize_warband(null, waypoint, true, null, null);
    }
    public static void initialize_warband(WarbandFaction faction, LOTRWaypoint waypoint) {
        initialize_warband(faction, waypoint, true, null, null);
    }
    public static void initialize_warband(WarbandFaction faction, Integer x, Integer z) {
        initialize_warband(faction, null, false, x, z);
    }
    public static void initialize_warband(WarbandFaction faction, LOTRWaypoint waypoint, Integer x, Integer z) {
        initialize_warband(faction, waypoint, false, x, z);
    }
    public static void initialize_warband(WarbandFaction faction, LOTRWaypoint waypoint, boolean randomize, Integer x, Integer z) {
        if (MinecraftServer.getServer().getConfigurationManager().playerEntityList.size() < 1)
            return;

        Warband warband = new Warband();

        if (waypoint != null) warband.waypoint_name = "lotr.waypoint." + waypoint.getCodeName();

        if ((x == null || z == null) && waypoint == null) waypoint = get_random_waypoint();
        if (x == null) x = waypoint.getXCoord();
        if (z == null) z = waypoint.getZCoord();
        warband.x = x;
        warband.z = z;

        if (randomize) {
            switch (random.nextInt(8)) {
                case 0: warband.direction = "North";      warband.z += random.nextInt(701) - 1000; break;
                case 1: warband.direction = "South";      warband.z += random.nextInt(701) + 300;  break;
                case 2: warband.direction = "East";       warband.x += random.nextInt(701) + 300;  break;
                case 3: warband.direction = "West";       warband.x += random.nextInt(701) - 1000; break;
                case 4: warband.direction = "North-East"; warband.z += random.nextInt(701) - 1000; warband.x += random.nextInt(701) + 300;  break;
                case 5: warband.direction = "North-West"; warband.z += random.nextInt(701) - 1000; warband.x += random.nextInt(701) - 1000; break;
                case 6: warband.direction = "South-East"; warband.z += random.nextInt(701) + 300;  warband.x += random.nextInt(701) + 300;  break;
                case 7: warband.direction = "South-West"; warband.z += random.nextInt(701) + 300;  warband.x += random.nextInt(701) - 1000; break;
                default: warband.direction = "none"; break;
            }
        }
        load_chunks_at(warband.x, warband.z);
        if (faction == null) faction = get_warband_faction_by_biome(warband);
        warband.faction = faction;
        spawn_warband(warband);
        WarbandTracker.add(warband);
    }

    public static void load_chunks_at(int block_x, int block_z) {
        int chunk_x = block_x >> 4;
        int chunk_z = block_z >> 4;

        for (int x = chunk_x - 1; x <= chunk_x + 1; x++) {
            for (int z = chunk_z - 1; z <= chunk_z + 1; z++) {
                world.getChunkProvider().loadChunk(x, z);
            }
        }
    }

    public static WarbandFaction get_warband_faction_by_biome(Warband warband) {
        LOTRBiome biome = (LOTRBiome)world.getBiomeGenForCoords(warband.x, warband.z);
        List<WarbandFaction> valid_factions = new ArrayList<>();
        for (WarbandFaction faction : WarbandFaction.values()) {
            if (faction.valid_biomes.contains(biome))
                valid_factions.add(faction);
        }
        if (valid_factions.size() < 1)
            return WarbandFaction.RENEGADE;
        return valid_factions.get(random.nextInt(valid_factions.size()));
    }

    public static void spawn_warband(Warband warband) {
        if (warband.faction == null) return;
        int x = warband.x;
        int z = warband.z;
        int y = world.getTopSolidOrLiquidBlock(x, z) + 2;
        spawn_boss_entity(warband, x, y, z);
        System.out.println("Warband at " + x + "," + y + "," + z);
        new Thread(() -> {
            for (Troop troop : warband.faction.troops) {
                int DEBUG_spawn_count = 0;
                for (int i = 0; i < troop.amount; i++) {
                    DEBUG_spawn_count++;
                    spawn_entity_by_class(troop.entity_class, world, x, y, z, warband);
                }
                System.out.println("Summoned " + DEBUG_spawn_count + " of " + troop.entity_class.getName());
            }
        }).start();
        broadcast_warband(world, warband.faction.warband_name, warband.direction, warband.waypoint_name);
        do_radial_conquest(world, warband.x, warband.z, warband.faction.faction, 50);
        last_warband_timestamp = System.currentTimeMillis() / 1000L;
    }
    public static void spawn_boss_entity(Warband warband, int x, int y, int z) {
        Entity boss_entity = WarbandFaction.get_boss_entity(warband.faction, world);
        warband.warband_uuid = boss_entity.getUniqueID();
        boss_entity.setLocationAndAngles(x, y, z, world.rand.nextFloat() * 360F, 0F);
        ((IEntityLivingBase)boss_entity).set_despawn_timer(12000);
        ((IEntityLivingBase)boss_entity).set_warband_uuid(warband.warband_uuid);
        if (boss_entity instanceof EntityLiving) {
            ((EntityLiving)boss_entity).setHealth(((EntityLiving)boss_entity).getMaxHealth());
            ((EntityLiving)boss_entity).func_110163_bv();
        }
        if (boss_entity instanceof LOTREntityNPC) {
            ((LOTREntityNPC)boss_entity).isNPCPersistent = true;
            DropTable table = new DropTable();
            String modifier = "";
            switch (random.nextInt(4)) {
                case 0:
                    modifier = "fireRepair";
                    break;
                case 1:
                    modifier = "stealth";
                    break;
                case 2:
                    modifier = "mountArmor";
                    break;
                case 3:
                    modifier = "swiftness";
                    break;
            }
            NBTTagCompound nbt = new NBTTagCompound();
            nbt.setString("ScrollModifier", modifier);
            table.drop_list.add(new SingleItemDrop(LOTRMod.modTemplate, nbt, 1.0F, new DropContext[] {DropContext.KILLED}));
            try {
                ((ILootableEntity)(Object)boss_entity).set_drop_table(table);
            } catch (Exception e) {
            }
        }
        world.spawnEntityInWorld(boss_entity);
    }
    public static Entity spawn_entity_by_class(Class<? extends Entity> entity_class, World world, double x, double y, double z, Warband warband) {
        try {
            Entity entity = entity_class.getConstructor(World.class).newInstance(world);
            entity.setLocationAndAngles(x, y, z, world.rand.nextFloat() * 360F, 0F);

            if (entity instanceof EntityLiving) {
                EntityLiving living = (EntityLiving) entity;

                // This triggers onSpawnWithEgg, applying armor/weapon logic
                living.onSpawnWithEgg((IEntityLivingData) null);

                living.func_110163_bv(); // mark as persistent
                living.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(50.0D);
            }

            if (entity instanceof Renegade)
                WarbandFaction.equip_renegade((Renegade)entity);
            if (entity instanceof LOTREntityNPC)
                ((LOTREntityNPC)entity).isNPCPersistent = true;

            ((IEntityLivingBase)entity).set_despawn_timer(12000);
            ((IEntityLivingBase)entity).set_warband_uuid(warband.warband_uuid);
            world.spawnEntityInWorld(entity);
            return entity;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void broadcast_warband(World world, String faction_key, String direction, String waypoint_key) {
        IChatComponent message;
        if (waypoint_key != null && direction != null) {
            message = new ChatComponentTranslation(
                "warband.found",
                new ChatComponentTranslation(faction_key).setChatStyle(new ChatStyle().setColor(EnumChatFormatting.GOLD)),
                new ChatComponentText(direction).setChatStyle(new ChatStyle().setColor(EnumChatFormatting.YELLOW)),
                new ChatComponentTranslation(waypoint_key).setChatStyle(new ChatStyle().setColor(EnumChatFormatting.AQUA))
            );
        } else if (waypoint_key == null){
            message = new ChatComponentTranslation(
                "warband.found.no_waypoint",
                new ChatComponentTranslation(faction_key).setChatStyle(new ChatStyle().setColor(EnumChatFormatting.GOLD))
            );
        } else {
            message = new ChatComponentTranslation(
                "warband.found.no_direction",
                new ChatComponentTranslation(faction_key).setChatStyle(new ChatStyle().setColor(EnumChatFormatting.GOLD)),
                new ChatComponentTranslation(waypoint_key).setChatStyle(new ChatStyle().setColor(EnumChatFormatting.AQUA))
            );
        }

        LOTRSpeech.messageAllPlayersInWorld(world, message);
    }

    public static void do_radial_conquest(World world, int posX, int posZ, LOTRFaction faction, float amount) {
        if (!LOTRConquestGrid.conquestEnabled(world)) {
            return;
        }

        LOTRConquestZone zone = LOTRConquestGrid.getZoneByWorldCoords(posX, posZ);
        if (zone == null || zone.isDummyZone) {
            return;
        }

        float current_strength = zone.getConquestStrength(faction, world);
        float target_strength = current_strength + amount;
        if (target_strength > 100000.0F) {
            amount = 100000.0F - current_strength;
        } else if (target_strength < 0.0F) {
            amount = -current_strength;
        }

        if (amount == 0.0F) {
            return;
        }

        if (amount < 0.0F) {
            LOTRConquestGrid.doRadialConquest(world, zone, null, null, faction, -amount, -amount);
        } else {
            LOTRConquestGrid.doRadialConquest(world, zone, null, faction, null, amount, amount);
        }
    }
}
