package projectzombie.entity;

import java.util.Random;

import bdf.types.BdfNamedList;
import bdf.types.BdfObject;
import gl_engine.MathHelpers;
import gl_engine.vec.Vec2d;
import gl_engine.vec.Vec2i;
import gl_engine.vec.Vec3d;
import projectzombie.Main;
import projectzombie.display.bossbar.BossBars;
import projectzombie.display.bossbar.IBossBar;
import projectzombie.init.Items;
import projectzombie.init.Models;
import projectzombie.init.Tiles;
import projectzombie.items.modifier.ItemModifierMeta;
import projectzombie.model.Model;
import projectzombie.time.GameTimer;
import projectzombie.util.ItemStack;
import projectzombie.util.math.random.OpenSimplexNoise;
import projectzombie.util.math.random.RandomHelpers;
import projectzombie.world.chunk.Chunk;
import projectzombie.world.layer.Layer;

public class EntityBoss extends Entity implements IBossBar, EntityKillWithParticles
{
	private boolean moving = false;
	private boolean firing = false;
	private double max_health = 25000;
	private double health = max_health;
	private int bullet_frequency = 0;
	private int spawn_frequency = 0;
	private double time = 0;
	
	private OpenSimplexNoise noise_gun;
	private OpenSimplexNoise noise_walk;
	private OpenSimplexNoise noise_spawn;
	protected OpenSimplexNoise noise_target_x;
	protected OpenSimplexNoise noise_target_y;
	
	private long seed;
	
	public EntityBoss(BdfObject bdf) {
		BdfClassLoad(bdf);
	}
	
	@Override
	public void BdfClassLoad(BdfObject bdf) {
		super.BdfClassLoad(bdf);
		
		BdfNamedList nl = bdf.getNamedList();
		health = nl.get("health").getDouble();
		bullet_frequency = nl.get("bullet_freq").getInteger();
		spawn_frequency = nl.get("spawn_frq").getInteger();
		
		this.isSolid = true;
		this.hitbox = 2;
		
		long seed = nl.get("seed").getLong();
		Random rand = new Random(seed);
		this.noise_gun = new OpenSimplexNoise(rand.nextLong());
		this.noise_walk = new OpenSimplexNoise(rand.nextLong());
		this.noise_spawn = new OpenSimplexNoise(rand.nextLong());
		this.noise_target_x = new OpenSimplexNoise(rand.nextLong());
		this.noise_target_y = new OpenSimplexNoise(rand.nextLong());
		
		BossBars.register(this);
	}
	
	@Override
	public void BdfClassSave(BdfObject bdf) {
		super.BdfClassSave(bdf);
		
		BdfNamedList nl = bdf.getNamedList();
		nl.set("health", bdf.newObject().setDouble(health));
		nl.set("bullet_freq", bdf.newObject().setInteger(bullet_frequency));
		nl.set("spawn_freq", bdf.newObject().setInteger(spawn_frequency));
		nl.set("seed", bdf.newObject().setLong(seed));
	}
	
	public EntityBoss(Vec3d pos, Vec3d velocity) {
		super(pos, velocity);
		
		this.isSolid = true;
		this.hitbox = 2;
		
		long seed = rand.nextLong();
		Random rand = new Random(seed);
		this.noise_gun = new OpenSimplexNoise(rand.nextLong());
		this.noise_walk = new OpenSimplexNoise(rand.nextLong());
		this.noise_spawn = new OpenSimplexNoise(rand.nextLong());
		this.noise_target_x = new OpenSimplexNoise(rand.nextLong());
		this.noise_target_y = new OpenSimplexNoise(rand.nextLong());
		
		BossBars.register(this);
	}
	
	@Override
	public void tick(Chunk chunk, Layer layer) {
		super.tick(chunk, layer);
		
		Vec3d bpos = getPos();
		Vec3d ppos = Main.player.getPos();
		
		double angle = Math.atan2(
				bpos.x - ppos.x + noise_target_x.eval(
						time*2, ppos.x/25, ppos.z/25)*10,
				bpos.z - ppos.z + noise_target_y.eval(
						time*2, ppos.x/25, ppos.z/25)*10);
		angle = Math.toDegrees(angle) + 180;
		
		if(this.noise_spawn.eval(GameTimer.getTime() / 500.0, 0) > 0.2) {
			if(spawn_frequency == 0) {
				int r = 2, m = 1000;
				Vec2d zombie_pos = new Vec2d(
						RandomHelpers.randrange(rand, -r*m, r*m)/(double)m,
						RandomHelpers.randrange(rand, -r*m, r*m)/(double)m);
				Vec2i zombie_tpos = zombie_pos.toInt();
				if(
						layer.getBackTile(zombie_tpos).tile == layer.layergen.getTileDestroyed(layer, zombie_tpos).tile &&
						layer.getFrontTile(zombie_tpos).tile == Tiles.VOID) {
					layer.spawnEntity(new EntityZombie(bpos.add(zombie_pos.xny()), getVelocity()));
				}
				spawn_frequency = 50;
			}
			spawn_frequency -= 1;
		}
		
		if(this.noise_gun.eval(GameTimer.getTime() / 500.0, 0) > 0 && !Main.player.dead && !Main.player.in_animation) {
			
			if(bullet_frequency == 0) {
				chunk.spawnEntity(new EntityBullet(bpos, bpos.subtract(ppos).normalize(), this, 20, 1000));
				bullet_frequency = 10;
			}
			
			bullet_frequency -= 1;
		}
		
		if(this.noise_gun.eval(GameTimer.getTime() / 500.0, 0) > -0.2) {
			this.firing = true;
		}
		
		else {
			this.firing = false;
		}
		
		if(this.noise_walk.eval(GameTimer.getTime() / 500.0, 0) > -0.4) {
			this.moveTowards(angle);
			this.moving = true;
		}
		
		else {
			this.moving = false;
		}
		
		// Increase time
		time += 0.001;
	}

	@Override
	public void addHealth(double amount) {
		this.health += amount;
	}

	@Override
	public void addDamage(double amount) {
		this.health -= amount;
	}
	
	@Override
	public void addFireDamage(double amount) {
		
	}
	
	@Override
	public void addBlastDamage(double amount) {
		addDamage(amount);
	}

	@Override
	public double getHealth() {
		return health;
	}

	@Override
	public void resetHealth() {
		this.health = max_health;
	}

	@Override
	public void clearHealth() {
		this.health = 0;
	}

	@Override
	public double maxHealth() {
		return max_health;
	}

	@Override
	public void setHealth(double health) {
		this.health = health;
	}

	@Override
	public int bloodParticles() {
		return 5;
	}

	@Override
	public String getName() {
		return "Final Boss";
	}
	
	@Override
	public boolean displayBossBar() {
		return health >= 0;
	}
	
	public void moveTowards(double angle) {
		this.push(MathHelpers.moveTowards2(0.04, Math.toRadians(angle)).xny());
	}
	
	@Override
	public void onDeath(Layer layer) {
		IBossBar.super.onDeath(layer);
		
		/*
			Drop some powerful loot
		*/
		
		// Spawn the loot
		layer.spawnEntity(new EntityItem(getPos(), getVelocity(), new ItemStack(
				Items.HEALTH_POTION, RandomHelpers.randrange(rand, 20), new ItemModifierMeta(50))));
		layer.spawnEntity(new EntityItem(getPos(), getVelocity(), new ItemStack(
				Items.AMMO, RandomHelpers.randrange(rand, 200), new ItemModifierMeta(50))));
		layer.spawnEntity(new EntityItem(getPos(), getVelocity(), new ItemStack(
				Items.GRAPPLING_HOOK, 1, new ItemModifierMeta(2))));
	}
	
	@Override
	protected void moveAwayFromSolidEntities(Layer layer) {
	}

	@Override
	public void killWithParticles() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Model getModel()
	{
		if(moving && firing) {
			return Models.ENTITY_BOSS_WALKING_AND_FIRING;
		} else if(moving) {
			return Models.ENTITY_BOSS_WALKING;
		} else if(firing) {
			return Models.ENTITY_BOSS_FIRING;
		} else {
			return Models.ENTITY_BOSS_IDLE;
		}
	}
}
