package holo.bolt.world;

import holo.bolt.BoltMain;
import holo.bolt.entity.Entity;
import holo.bolt.entity.EntityFactory;
import holo.bolt.entity.EntityPlayer;
import holo.bolt.entity.Hitbox;
import holo.bolt.entity.IImmobile;
import holo.bolt.entity.IProjectile;
import holo.bolt.states.GameState;
import holo.bolt.world.map.BaseLevel;
import holo.bolt.world.map.Level;
import holo.bolt.world.map.tile.InteractableTile;
import holo.bolt.world.map.tile.Tile;
import holo.bolt.world.map.tile.TileFactory;

import java.util.ArrayList;
import java.util.HashMap;

import org.newdawn.slick.geom.Vector2f;

public class World
{
	 public int currentLevel;
	 public ArrayList<Entity> entityList = new ArrayList<Entity>();
	 public HashMap<Hitbox, Object> hitboxes = new HashMap<Hitbox, Object>();
	 public HashMap<Hitbox, InteractableTile> interactables = new HashMap<Hitbox, InteractableTile>();
	 public Tile[][] tileTable;
	 public GameState state;
	 public ArrayList<Entity> entitiesToRemove = new ArrayList<Entity>();
	 public ArrayList<Entity> entitiesToAdd = new ArrayList<Entity>();
	 int n;
	 public boolean flagNextLevel;
	 
	 public Level[] levels;
	 
	 public World(GameState state, boolean load)
	 {
		 this.state = state;
		 levels = new Level[]{new BaseLevel(1), new BaseLevel(2), new BaseLevel(3), new BaseLevel(4)};
		 flagNextLevel = false;
		 if (load)
		 {
			 
		 }
		 else
		 {
			 loadLevel(state, levels[currentLevel], currentLevel);
		 }
	 }
	 
	 public void loadLevel(GameState state, Level level, int levelID)
	 {
		 currentLevel = ++levelID;
		 entityList.clear();
		 state.renderList.clear();
		 hitboxes.clear();
		 interactables.clear();
		 entitiesToRemove.clear();
		 entitiesToAdd.clear();
		 
		 this.initPlayer();
		 this.state.levelScore = this.state.score;
		 
		 tileTable = new Tile[level.getSize()[1]][level.getSize()[0]];
		 
		 for (int i = 0; i < level.getSize()[1]; ++i)
		 {
			 for (int j = 0; j < level.getSize()[0]; ++j)
			 {
				 float[] pos = {j * 64, i * 64};
				 Tile tile = TileFactory.makeTile(level.getIdFromCoords(i, j), pos);
				 tileTable[i][j] = tile;
				 state.renderList.add(tile.getRenderer());
				 if(tile.collidable())
				 {
					 if(tile instanceof InteractableTile)
					 {
						 interactables.put(tile.getHitbox(), (InteractableTile)tile);
					 }
					 else
						 hitboxes.put(tile.getHitbox(), tile);
				 }
			 }
		 }
		 
		 for(Integer[] i : level.entities)
		 {
			 this.registerEntity(EntityFactory.makeEntity(i[0], new float[]{i[1],i[2]}, this));
		 }
	 }
	 
	 public void onUpdate(int delta)
	 {
		 for(Entity ent: entityList)
		 {
			 ent.onUpdate(delta);
		 }
		 
		 for(Hitbox box1 : hitboxes.keySet())
		 {
			 for(Hitbox box2 : hitboxes.keySet())
			 {
				 if(box1 != box2 && !(hitboxes.get(box1) instanceof Tile && hitboxes.get(box2) instanceof Tile))
				 {
					 if (box1.isIntersecting(box2))
						 calculateCollision(hitboxes.get(box1), hitboxes.get(box2));
				 }
			 }
		 }
		 
		 for(Entity ent : entitiesToRemove)
		 {
			 entityList.remove(ent);
			 hitboxes.remove(ent.box);
			 state.renderList.remove(ent.getRender());
		 }
		 
		 for(Entity ent : entitiesToAdd)
		 {
			 entityList.add(ent);
		 }
		 
		 entitiesToRemove.clear();
		 entitiesToAdd.clear();
		 
		 if(flagNextLevel)
		 {
			 flagNextLevel = false;
			 this.loadLevel(state, levels[currentLevel], currentLevel);
		 }
	 }
	 
	 public void calculateCollision(Object object1, Object object2)
	 {
		 
		 if (object1 instanceof Entity && object2 instanceof Tile)
			 calculateEntityTileCollision((Entity)object1, (Tile)object2);
		 else if (object1 instanceof Tile && object2 instanceof Entity)
			 calculateEntityTileCollision((Entity)object2, (Tile)object1);
		 else if (object1 instanceof Entity && object2 instanceof Entity)
			 calculateEntityEntityCollision((Entity)object1, (Entity)object2);
			 
	 }
	 
	 public void calculateEntityTileCollision(Entity ent, Tile t)
	 {
//		 System.out.println("Collision! (" + t.box.pos[0] + " " + t.box.pos[1] + ") (" + t.box.size[0] + " " + t.box.size[1] + ") (" + ent.box.pos[0] + " " + ent.box.pos[1] + ") (" + ent.box.size[0] + " " + t.box.size[1] + ")");
		 Vector2f vec = new Vector2f((ent.pos[0] + ent.box.size[0] / 2) - (t.pos[0] + t.box.size[0] / 2), (ent.pos[1] + ent.box.size[1] / 2) - (t.pos[1] + t.box.size[1] / 2));
		 vec.normalise();
		 ent.setVelocity(vec.scale(0.04F));
		 t.onCollideWithEntity(ent);
		 
		 ent.onCollideWithTile(t);
	 }
	 
	 public void calculateEntityEntityCollision(Entity ent1, Entity ent2)
	 {
		 if((ent1 instanceof IProjectile) || (ent2 instanceof IProjectile) || (ent1 instanceof IImmobile) || (ent2 instanceof IImmobile))
			 ;
		 else
		 {
			 Vector2f vec = new Vector2f((ent1.pos[0] + ent1.box.size[0] / 2) - (ent2.pos[0] + ent2.box.size[0] / 2), (ent1.pos[1] + ent1.box.size[1] / 2) - (ent2.pos[1] + ent2.box.size[1] / 2));
			 vec.normalise();
			 ent1.setVelocity(vec.scale(0.04F));
			 ent2.setVelocity(vec.negate());
		 }
		 ent1.onCollideWithEntity(ent2);
		 ent2.onCollideWithEntity(ent1);
	 }
	 
	 public boolean checkCollision(Hitbox box)
	 {
		 for(Hitbox checkBox : hitboxes.keySet())
		 {
			 if(box.isIntersecting(checkBox))
				 return true;
		 }
		 return false;
	 }
	 
	 public Object getCollision(Hitbox box)
	 {
		 if(checkCollision(box))
			 for(Hitbox checkBox : hitboxes.keySet())
			 {
				 if(box.isIntersecting(checkBox))
					 return checkBox;
			 }
		 return null;
	 }
	 
	 public boolean canInteract(EntityPlayer player)
	 {
		 for(Hitbox box : interactables.keySet())
		 {
			 if(box.isIntersecting(player.box))
			 {
				 return true;
			 }
		 }
		 return false;
	 }
	 
	 public InteractableTile getInteractable(EntityPlayer player)
	 {
		 for(Hitbox box : interactables.keySet())
		 {
			 if(box.isIntersecting(player.box))
				 return interactables.get(box);
		 }
		 return null;
	 }
	 
	 public void registerEntity(Entity entity)
	 {
		 entitiesToAdd.add(entity);
		 hitboxes.put(entity.box, entity);
		 state.renderList.add(entity.getRender());
	 }
	 
	 public boolean checkEntity(Entity ent)
	 {
		 return entityList.contains(ent);
	 }
	 
	 public void removeEntity(Entity ent)
	 {
		 entitiesToRemove.add(ent);
	 }
	 
	 public Entity getPlayer()
	 {
		 if(state.player != null)
			 return state.player;
		 return null;
	 }
	 
	 public void nextLevel()
	 {
		 if(currentLevel + 1 <= levels.length)
		 {
			 flagNextLevel = true;
		 }
		 else
		 {
			 this.state.game.enterState(BoltMain.VICTORY_ID);
		 }
	 }
	 
	 public void initPlayer()
	 {
		 EntityPlayer player = new EntityPlayer(new float[] {85, 85}, this);
		 if(this.state.player != null)
		 {
			 float playerHp = this.state.player.health;
			 boolean playerHasBolt = this.state.player.hasBolt;
			 player.health = playerHp;
			 player.hasBolt = playerHasBolt;
		 }
		 this.registerEntity(player);
		 this.state.initPlayer(player);
	 }
	 
	 public void updateScore(int score)
	 {
		 state.score += score;
	 }
}