package edu.wisc.cs.games10.chrononaut;

import java.util.*;

import processing.core.*; 


public abstract class Level {
	
	public Level() {
		pastBullets = new ArrayList<Bullet>();
		presentBullets = new ArrayList<Bullet>();
		futureBullets = new ArrayList<Bullet>();

		killedEnemies = new LinkedList<Enemy>();
		enemies = new HashSet<Enemy>();
		creatures = new LinkedList<Creature>();
	}
	
	public void reloadLevel(PApplet canvas) {
		loadEnemies(canvas);
		loadCreatures(canvas);
	}

	/** Loads enemies from a file. */
	@SuppressWarnings("unchecked")
	private void loadEnemies(PApplet canvas) {
		killedEnemies = new LinkedList<Enemy>();
		enemies = new HashSet<Enemy>();
		
		// load enemies from a file
		try {
			Scanner enemiesScanner = new Scanner(getClass().getResourceAsStream("/data/levels/" + levelname + "/enemies.txt"));
			while (enemiesScanner.hasNextInt()) {
				int enemyCode = enemiesScanner.nextInt();

				// there had better be coordinates!
				int enemyX = enemiesScanner.nextInt();
				int enemyY = enemiesScanner.nextInt();
				Vector2D enemyLoc = new Vector2D(enemyX, enemyY);
				
				switch (enemyCode) {
				case 0:
				{
					enemies.add(new HatEnemy(this, enemyLoc, canvas));
				}
				break;
				case 1:
				{
					enemies.add(new BranderBotEnemy(this, enemyLoc, canvas));
				}
				break;
				case 2:
				{
					enemies.add(new SpectreEnemy(this, enemyLoc, canvas));
				}
				break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		enemyTiles = new LinkedList[width][height];
		for(int i = 0; i < width; i++)
			for(int j = 0; j < height; j++)
				enemyTiles[i][j] = new LinkedList<Enemy>();
	}
	
	private void loadCreatures(PApplet canvas) {
		//load creatures from a file
		creatures = new LinkedList<Creature>();
		try {
			Scanner creaturesScanner = new Scanner(getClass().getResourceAsStream("/data/levels/" + levelname + "/creatures.txt"));
			// <creaturecode> <x-coord> <y-coord> <eracode>
			while (creaturesScanner.hasNext()) {
				String creaturePath = creaturesScanner.next();

				// there had better be coordinates!
				int creatureX = creaturesScanner.nextInt();
				int creatureY = creaturesScanner.nextInt();
				Vector2D creatureLoc = new Vector2D(creatureX, creatureY);
				
				// better be an era as well!
				Era era = Era.valueOf(creaturesScanner.next());
				
				// TODO: move body part creation to Creature
				BodyPart bp = null;
				if(creaturePath.equals("runfast")) {
					PImage legImage = canvas.loadImage("player/legs/cheetah.png");
					bp = new Leg("Cheetah Legs", legImage, 1, false, 1.5f);
				} else if(creaturePath.equals("tiggerbounce")) {
					PImage bouncyTail = canvas.loadImage("player/tail/02.png");
					bp = new Tail("Tigger Tail", bouncyTail, 1.3f, false);
				} else if(creaturePath.equals("mariopound")) {
					PImage legImage = canvas.loadImage("player/legs/elephant.png");
					bp = new Leg("Elephant Legs", legImage, 0.8f, true, 0.7f);
				}else if(creaturePath.equals("glide")) {
					PImage arm = canvas.loadImage("player/arms/batwing.png");
					bp = new Arm("Bat Wings", arm, false, true, false);
				}else if(creaturePath.equals("snakearms")) {
					PImage arm = canvas.loadImage("player/arms/snakearms.png");
					bp = new Arm("Octopus Arms", arm, false, false, true);
				}else if(creaturePath.equals("turtleshell")) {
					bp = new Tail("Turtle Shell", null, 1f, true);
					
				}
				creatures.add(new Creature(creatureLoc, era, creaturePath, canvas, bp));

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		originalNumCreatures = creatures.size();
	}
	
	public Era getStartEra() {
		// TODO: load this location from file
		return Era.PRESENT;
	}
	
	/** returns next level (-1 is not finished) */
	public abstract int isLevelFinished(Player p);
	public abstract Vector2D getStartLocation();
	
	public int getWidth() {
		return width;
	}
	
	public int getHeight() {
		return height;
	}
	
	public int getOriginalCreatureNum() {
		return originalNumCreatures;
	}
	
	public void renderCreatures(PApplet canvas, Era era) {
		for(Creature creature : creatures) {
			if(creature.getEra() == era)
				creature.render(canvas);
		}
	}
	
	public void addBullet(Bullet b, Era era) {
		if(era == Era.PAST) {
			pastBullets.add(b);
		} else if(era == Era.PRESENT) {
			presentBullets.add(b);
		}else {
			futureBullets.add(b);
		} 
	}
	
	public void renderBullets(PApplet canvas, Era era) {
		if(era == Era.PAST) {
			for(Bullet bullet : pastBullets) {
				bullet.render(canvas);
			}
		} else if(era == Era.PRESENT) {
			for(Bullet bullet : presentBullets) {
				bullet.render(canvas);
			}
		}else {
			for(Bullet bullet : futureBullets) {
				bullet.render(canvas);
			}
		} 
	}
	
	public void renderEnemies(PApplet canvas) {
		for (Enemy enemy : enemies) {
			enemy.render(canvas);
		}
	}
	
	public void update(float frametime, Player p, Era era) {
		updateEnemies(frametime, p, era);
		updateCreatures(frametime, era);
		updateBullets(frametime, era);
	}
	
	private void updateBullets(float frametime, Era era) {
		if(era == Era.PAST) {
			int bulletSize = pastBullets.size();
			for(int i=0; i<bulletSize; i++) {
				pastBullets.get(i).update(frametime);
				if(pastBullets.get(i).getTimeLeft()<=0) {
					pastBullets.remove(i);
					i--;
					bulletSize--;
				}
			}
		} else if(era == Era.PRESENT) {
			int bulletSize = presentBullets.size();
			for(int i=0; i<bulletSize; i++) {
				presentBullets.get(i).update(frametime);
				if(presentBullets.get(i).getTimeLeft()<=0) {
					presentBullets.remove(i);
					i--;
					bulletSize--;
				}
			}
		}else {
			int bulletSize = futureBullets.size();
			for(int i=0; i<bulletSize; i++) {
				futureBullets.get(i).update(frametime);
				if(futureBullets.get(i).getTimeLeft()<=0) {
					futureBullets.remove(i);
					i--;
					bulletSize--;
				}
			}
		} 
	}
	
	private void updateEnemies(float time, Player p, Era era) {
		// remove any enemies that should no longer exist
		for (Enemy enemy : killedEnemies) {
			enemies.remove(enemy);
		}
		killedEnemies.clear();
		
		for (Enemy enemy : enemies) {
			enemy.attack(era, p, time);
			enemy.update(time, era);
		}
		
	}
	
	private void updateCreatures(float time, Era era) {
		ListIterator<Creature> iterCreature = creatures.listIterator();
		while (iterCreature.hasNext()) {
			Creature c = iterCreature.next();
			if (c.dead) {
				iterCreature.remove();
			} else if (c.era == era) {
				c.update(time);
			}
		}
	}
	
	public Iterable<Enemy> getEnemies() {
		return enemies;
	}
	
	public void killEnemy(Enemy enemy) {
		Vector2D loc = enemy.getLocation();
		for(int i = 0; i < enemy.getTileWidth(); i++)
			for(int j = 0; j < enemy.getTileWidth(); j++)
				if((int)Math.floor(loc.x) + i < width && (int)Math.floor(loc.x) + i > 0 && (int)Math.floor(loc.y) + j < height && (int)Math.floor(loc.y) + j > 0)
					enemyTiles[(int)Math.floor(loc.x) + i][(int)Math.floor(loc.y) + j].remove(enemy);
		killedEnemies.add(enemy);
	}
	
	public Tile getTile(Era era, int x, int y) {
		if (x < 0 || x >= width)
			return Tile.SOLID;
		else if (y < 0 || y >= height)
			return Tile.EMPTY;
		else if (era == Era.PAST) {
			return pasttiles[x][y];
		} else if (era == Era.PRESENT) {
			return presenttiles[x][y];
		} else {
			return futuretiles[x][y];
		}
	}
	

	
	public abstract PImage getBackground(Era era);
	public abstract PImage getForeground(Era era);
	

	
	protected static Tile[][] loadEraFromImage(String levelname, String era, PApplet canvas) {
		PImage map = canvas.loadImage("levels/" + levelname + "/" + era + ".png");
        if(map == null) 
        { 
            System.out.println("Failed at loading " + levelname + " in era " + era + "."); 
        }
		Tile[][] tiles = new Tile[map.width][map.height];
		
		for (int x=0; x < map.width; x++) {
			for (int y=0; y < map.height; y++) {
				if (canvas.green(map.pixels[y * map.width + x]) == 255) {
					tiles[x][y] = Tile.EMPTY;
				} else {
					tiles[x][y] = Tile.SOLID;
				}
			}
		}
		
		return tiles;
	}
	
	protected static Square[][] loadEraFromImage2(String levelname, String era, PApplet canvas) {
		PImage map = canvas.loadImage("levels/" + levelname + "/" + era + ".png");
        if(map == null) 
        { 
            System.out.println("Failed at loading " + levelname + " in era " + era + "."); 
        }
		Square[][] squares = new Square[map.width][map.height];
		
		for (int x=0; x < map.width; x++) {
			for (int y=0; y < map.height; y++) {
				if (canvas.blue(map.pixels[y * map.width + x]) == 0) { 			/* SOLID LAND STUFF HERE */
					squares[x][y] = Square.SOLID_GROUND;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 13) {
					squares[x][y] = Square.CLIFF_LEFT;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 14) {
					squares[x][y] = Square.CLIFF_RIGHT;
				} else if (canvas.blue(map.pixels[y * map.width + x]) == 11) {
					squares[x][y] = Square.CLIFF_UPPER_LEFT;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 12) {
					squares[x][y] = Square.CLIFF_UPPER_RIGHT;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 1) {
					squares[x][y] = Square.SOLID_UPPER_LEFT;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 2) {
					squares[x][y] = Square.SOLID_UPPER_MIDDLE;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 3) {
					squares[x][y] = Square.SOLID_UPPER_RIGHT;
				} else if (canvas.blue(map.pixels[y * map.width + x]) == 100) { 	/* TREE STUFF HERE */
					squares[x][y] = Square.TREE_UPPER_LEFT;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 101) {
					squares[x][y] = Square.TREE_UPPER_MIDDLE;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 102) {
					squares[x][y] = Square.TREE_UPPER_RIGHT;
				} else if (canvas.blue(map.pixels[y * map.width + x]) == 110) {
					squares[x][y] = Square.TREE_LOWER_LEFT;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 111) {
					squares[x][y] = Square.TREE_LOWER_MIDDLE;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 112) {
					squares[x][y] = Square.TREE_LOWER_RIGHT;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 113) {
					squares[x][y] = Square.TREE_LOWER_TRUNK;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 114) {
					squares[x][y] = Square.TREE_TRUNK;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 115) {
					squares[x][y] = Square.TREE_MIDDLE;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 116) {
					squares[x][y] = Square.TREE_MIDDLE_LEFT;
				} else  if (canvas.blue(map.pixels[y * map.width + x]) == 117) {
					squares[x][y] = Square.TREE_MIDDLE_RIGHT;
				} else if (canvas.blue(map.pixels[y * map.width + x]) == 255) {
					squares[x][y] = Square.BLANK_TILE;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 51) {
					squares[x][y] = Square.GRASS_LEFT;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 52) {
					squares[x][y] = Square.GRASS_RIGHT;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 53) {
					squares[x][y] = Square.GRASS_CLIFF_LEFT;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 54) {
					squares[x][y] = Square.GRASS_CLIFF_RIGHT;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 55) {
					squares[x][y] = Square.GRASS_TREE_TRUNK;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 122) {
					squares[x][y] = Square.TREE_TRUNK_LPIECE_LEFT;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 123) {
					squares[x][y] = Square.TREE_TRUNK_LPIECE_RIGHT;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 124) {
					squares[x][y] = Square.TREE_TRUNK_SIDEWAYS;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 125) {
					squares[x][y] = Square.TREE_TRUNK_INTERSECT_LEFT;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 126) {
					squares[x][y] = Square.TREE_TRUNK_INTERSECT_RIGHT;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 50) {
					squares[x][y] = Square.GRASS;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 15) {
					squares[x][y] = Square.CLIFF_FOOT_LEFT;
				}else if (canvas.blue(map.pixels[y * map.width + x]) == 16) {
					squares[x][y] = Square.CLIFF_FOOT_RIGHT;
				}else if(canvas.blue(map.pixels[y * map.width + x]) == 250)
				{
					squares[x][y] = Square.FLAG_BASE;
				}else if(canvas.blue(map.pixels[y * map.width + x]) == 251)
				{
					squares[x][y] = Square.FLAG_TOP;
				}
			}
		}
		
		return squares;
	}
	
	String levelname;
	
	int width;
	int height;
	Tile[][] pasttiles;
	Tile[][] presenttiles;
	Tile[][] futuretiles;
	
	Square[][]pastSquares;
	Square[][]presentSquares;
	Square[][]futureSquares;
	
	PImage[] squareImages;

	
	HashSet<Enemy> enemies;
	Queue<Enemy> killedEnemies;
	List<Creature> creatures;
	List<Bullet> pastBullets;
	List<Bullet> presentBullets;
	List<Bullet> futureBullets;
	LinkedList<Enemy>[][] enemyTiles;
	
	int originalNumCreatures;
}
