package projectzombie.entity;

import java.util.Random;

import bdf.types.BdfNamedList;
import bdf.types.BdfObject;
import gl_engine.vec.Vec2i;
import gl_engine.vec.Vec3d;
import projectzombie.Main;
import projectzombie.init.Models;
import projectzombie.model.Model;
import projectzombie.util.math.astar.AStar;
import projectzombie.util.math.astar.AStarSearcher;
import projectzombie.util.math.random.OpenSimplexNoise;
import projectzombie.world.chunk.Chunk;
import projectzombie.world.layer.Layer;

public class EntityZombie extends Entity implements EntityAlive, EntityKillWithParticles
{
	protected OpenSimplexNoise noise_movement;
	protected OpenSimplexNoise noise_gun_fire;
	protected OpenSimplexNoise noise_gun_angle;
	protected OpenSimplexNoise noise_target_x;
	protected OpenSimplexNoise noise_target_y;
	
	protected long seed;
	
	protected double time;
	protected double health_max = 100;
	protected double health = health_max;
	protected int gun_interval = 0;
	protected int gun_level = 0;
	
	private Vec3d walk_to;
	private int walk_scan_cooldown = 0;
	private boolean can_see_player = false;
	protected boolean crossUnwalkable = false;
	private int walking_for = 0;
	
	public EntityZombie(BdfObject bdf)
	{
		// Set some settings
		hitbox = 0.5;
		isSolid = true;
		
		BdfClassLoad(bdf);
	}
	
	@Override
	public void BdfClassLoad(BdfObject bdf) {
		super.BdfClassLoad(bdf);
		
		BdfNamedList nl = bdf.getNamedList();
		health_max = nl.get("max_health").getDouble();
		health = nl.get("health").getDouble();
		gun_interval = nl.get("gun_interval").getInteger();
		gun_level = nl.get("gun_level").getInteger();
		seed = nl.get("seed").getLong();
		
		
		Random rand = new Random(seed);
		
		noise_movement = new OpenSimplexNoise(rand.nextLong());
		noise_gun_fire = new OpenSimplexNoise(rand.nextLong());
		noise_gun_angle = new OpenSimplexNoise(rand.nextLong());
		noise_target_x = new OpenSimplexNoise(rand.nextLong());
		noise_target_y = new OpenSimplexNoise(rand.nextLong());
	}
	
	@Override
	public void BdfClassSave(BdfObject bdf) {
		super.BdfClassSave(bdf);
		
		BdfNamedList nl = bdf.getNamedList();
		nl.set("max_health", bdf.newObject().setDouble(health_max));
		nl.set("health", bdf.newObject().setDouble(health));
		nl.set("gun_interval", bdf.newObject().setInteger(gun_interval));
		nl.set("gun_level", bdf.newObject().setInteger(gun_level));
		nl.set("seed", bdf.newObject().setLong(seed));
		
		
	}
	
	public EntityZombie(Vec3d pos, Vec3d velocity) {
		super(pos, velocity);
		
		seed = rand.nextLong();
		Random rand = new Random(seed);
		
		noise_movement = new OpenSimplexNoise(rand.nextLong());
		noise_gun_fire = new OpenSimplexNoise(rand.nextLong());
		noise_gun_angle = new OpenSimplexNoise(rand.nextLong());
		noise_target_x = new OpenSimplexNoise(rand.nextLong());
		noise_target_y = new OpenSimplexNoise(rand.nextLong());
		time = 0;
		
		// Set some settings
		hitbox = 0.5;
		isSolid = true;
	}
	
	@Override
	public void tick(Chunk chunk, Layer layer) {
		super.tick(chunk, layer);
		
		//System.out.println(walk_direction != null ? (walk_direction.x + ", " + walk_direction.y + ": " + pos.toInt().x + ", " + pos.toInt().y) : "null");
		
		if(walk_to == null) {
			walk_scan_cooldown -= 1;
		}
		
		double player_distance = getPos().squareDistance(Main.player.getPos());
		
		// Only pathfind if in range of the player
		if(player_distance < 16)
		{
			if(
					(walk_to != null && getPos().toInt().equal(walk_to.toInt()) &&
					player_distance > 2) || walk_scan_cooldown < 1 || walking_for > 200)
			{
				AStar astar = new AStar(getPos().xz().toInt(), 16, new AStarSearcher(layer));
				Vec2i path[] = astar.getPath(Main.player.getPos().xz().toInt());
				
				walk_scan_cooldown = 100;
				walking_for = 0;
				
				if(path != null && path.length > 1) {
					walk_to = new Vec3d(path[0].x, 0, path[0].y);
				} else {
					walk_to = null;
				}
				
				can_see_player = (path != null);
			}
			
			// Walk towards the player
			if(walk_to != null)
			{
				push(walk_to.subtract(getPos()).normalize().multiply(0.064));
				
				walking_for += 1;
			}
			
			if(can_see_player && noise_gun_fire.eval(time, 0) > 0 && !Main.player.dead && !Main.player.in_animation)
			{
				gun_interval += 1;
				gun_interval %= 10;
				
				if(gun_interval == 0)
				{
					// Fire the gun
					int d = (int)(1 + gun_level / 5.0);
					Vec3d bullet_velocity = getVelocity().add(Main.player.getPos().subtract(getPos())).normalize().multiply(0.2);
					Vec3d bullet_pos = getPos().add(new Vec3d(0, 0.4, 0));
					Main.world.getLayer().spawnEntity(new EntityBullet(bullet_pos, bullet_velocity, this, 20*d*d, 60));
				}
			}
		}
		
		// Despawn
		if(player_distance > 128) {
			kill();
		}
		
		// Increase time
		time += 0.001;
	}
	
	@Override
	public Model getModel() {
		return Models.ENTITY_ZOMBIE_F;
	}
	
	@Override
	public void addHealth(double amount) {
		health += amount;
	}

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

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

	@Override
	public void resetHealth() {
		health = health_max;
	}

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

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

	@Override
	public void setHealth(double health) {
		this.health = health;
	}
	
	@Override
	public int bloodParticles() {
		return 12;
	}

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

}
