import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.io.*;

import javax.imageio.ImageIO;
public class Level {
	public static final byte HIT_TOP	= 0x01;
	public static final byte HIT_LEFT	= 0x02;
	public static final byte HIT_RIGHT	= 0x04;
	public static final byte HIT_BOTTOM	= 0x08;
	public static final byte HIT_INT_H	= 0x10;
	public static final byte HIT_INT_V	= 0x20;
	
	private static final byte TILE_EMPTY = 0;
	private static final byte TILE_WALL_INV = 9;
	private static final byte TILE_ENEMY_A = -1;
	private static final byte TILE_ENEMY_B = -8;
	private static final byte TILE_PLAYER = -2;
	private static final byte TILE_ITEM_JUMP = -3;
	private static final byte TILE_ITEM_WEAPON = -4;
	private static final byte TILE_ITEM_NICKEL = -5;
	private static final byte TILE_CHECKPOINT = -6;
	private static final byte TILE_GOAL = -7;

	public byte[][] levelmap;
	public LevelDec levelInfo;
	
	private ClassLoader cl = HWDisplay.cl;

	HWDisplay frame;
	HWGameState gamestate;
	int levelNum;
	Player player;
	ArrayList<Enemy> enemies;
	ArrayList<Item> items;
	ArrayList<Projectile> projectiles;
	HashMap<Sprite, BufferedImage[][]> spriteCache;
	BufferedImage screen;
	BufferedImage foreground;
	BufferedImage background;
	BufferedImage[] walls;
	int winX, winY;
	
	BufferedImage miniMap;
	
	public Level(HWDisplay hwd, int lNum, LevelDec levelInfo) {
		frame = hwd;
		gamestate = hwd.gamestate;
		levelNum=lNum;
		spriteCache = new HashMap<Sprite, BufferedImage[][]>();
		enemies = new ArrayList<Enemy>();
		items = new ArrayList<Item>();
		projectiles = new ArrayList<Projectile>();
		walls = new BufferedImage[4];
		winX=0;
		winY=0;
		this.levelInfo = levelInfo;
		this.levelmap = levelInfo.levelmap;
		String imgPath = levelInfo.imgPath;
		
		try {
			if(levelNum != 1)
				background = ImageIO.read(cl.getResource(imgPath+"bkg.png"));
			for (int i=0; i<levelInfo.terrainTypeNum; i++)
				walls[i] = ImageIO.read(cl.getResource(imgPath+"terrain"+i+".png"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		Graphics2D g;
		if (foreground == null) {
			foreground = new BufferedImage(levelmap[0].length*32,levelmap.length*32,BufferedImage.TYPE_INT_ARGB);
		}
		g = (Graphics2D)foreground.getGraphics();
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
		g.fillRect(0, 0, 256, 256);
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
		g.setColor(Color.black);
		for (int i=0; i<levelmap.length; i++) {
			for (int j=0; j<levelmap[i].length; j++) {
				if (levelmap[i][j] > TILE_EMPTY) {
					if (levelmap[i][j]!=TILE_WALL_INV)
						g.drawImage(walls[levelmap[i][j]-1],j*32,i*32,null);
				} else if (levelmap[i][j] == TILE_ENEMY_A) {
					enemies.add(new Enemy(this, levelInfo.enemyA_imgName, levelInfo.enemyA_animL, j*32, i*32, levelInfo.enemyA_w, levelInfo.enemyA_h, levelInfo.enemyA_type, levelInfo.enemyA_value));
				} else if (levelmap[i][j] == TILE_ENEMY_B) {
					enemies.add(new Enemy(this, levelInfo.enemyB_imgName, levelInfo.enemyB_animL, j*32, i*32, levelInfo.enemyB_w, levelInfo.enemyB_h, levelInfo.enemyB_type, levelInfo.enemyB_value));
				} else if (levelmap[i][j] == TILE_PLAYER) {
					player = new Player(this, j*32, i*32);
				} else if (levelmap[i][j] == TILE_ITEM_JUMP) {
					int[] animLengths = {1};
					items.add(new Item(this, "img/items/grav", animLengths, j*32, i*32, "Low Grav") {
						public void itemAction(Level l) {
							l.player.grav = .01f;
							timer = 1200;
						}
						public void itemRevert(Level l) {
							l.player.grav = Player.DEF_GRAV;
						}
						public Color getColor() {
							return null;
						}
					});
				} else if (levelmap[i][j] == TILE_ITEM_WEAPON) {
					int[] animLengths = {1};
					items.add(new Item(this, "img/items/weap", animLengths, j*32, i*32, "Weapon") {
						public void itemAction(Level l) {
							l.player.fireEnable++;
							timer = 2400;
						}
						public void itemRevert(Level l) {
							l.player.fireEnable--;
						}
						public Color getColor() {
							return null;
						}
					});
				} else if (levelmap[i][j] == TILE_ITEM_NICKEL) {
					int[] animLengths = {4};
					items.add(new Item(this, "img/items/nickel", animLengths, j*32, i*32, "50") {
						public void itemAction(Level l) {
							gamestate.addPoints(50);
						}
						public void itemRevert(Level l) {}
						public Color getColor() {
							return null;
						}
					});
				} else if (levelmap[i][j] ==  TILE_CHECKPOINT) {
					int[] animLengths = {1};
					items.add(new Item(this, "img/items/checkpoint", animLengths, j*32, i*32, "Checkpoint") {
						public void itemAction(Level l) {
							player.setSpawn(x, y);
						}
						public void itemRevert(Level l) {}
						public Color getColor() {
							return null;
						}
					});
				} else if (levelmap[i][j] == TILE_GOAL){
					int[] animLengths = {2};
					items.add(new Item(this, "img/items/goal", animLengths, j*32, i*32, "") {
						public void itemAction(Level l) {
							//BEAT THE LEVEL
							frame.changeTo(levelNum+1);
						}
						public void itemRevert(Level l) {}
						public Color getColor() {
							return null;
						}
					});
				}
			}
		}
	}
	
	public void update(BufferedImage buf) {
		player.act();
		if (player.isFacingRight())
			winX+=2;
		else
			winX-=2;
		if (player.getX()-64 < winX)
			winX = player.getX()-64;
		else if (player.getX()+64+player.getW()*32 > winX+256)
			winX = player.getX()+64+player.getW()*32 - 256;
		if (player.getY()-64 < winY)
			winY = player.getY()-64;
		else if (player.getY()+64+player.getH()*32 > winY+256)
			winY = player.getY()+64+player.getH()*32 - 256;
		if (winX < 0)
			winX = 0;
		else if (winX+256 > levelmap[0].length*32)
			winX = levelmap[0].length*32-256;
		if (winY < 0)
			winY = 0;
		else if (winY+256 > (levelmap.length-2)*32)
			winY = (levelmap.length-2)*32-256;
		
		Iterator<Enemy> enemyIt = enemies.iterator();
		Enemy e;
		while (enemyIt.hasNext()) {
			e = enemyIt.next();
			e.act();
			if (e.isSetToDestroy())
				enemyIt.remove();
		}
		Iterator<Item> itemIt = items.iterator();
		Item i;
		while (itemIt.hasNext()) {
			i = itemIt.next();
			i.act();
			if (i.isSetToDestroy())
				itemIt.remove();
		}
		Iterator<Projectile> projIt = projectiles.iterator();
		Projectile p;
		while (projIt.hasNext()) {
			p = projIt.next();
			p.act();
			if (p.isSetToDestroy())
				projIt.remove();
		}
		
		draw(buf);
	}
	
	public byte hitDetect(int x, int y, int w, int h, float vx, float vy) {
		byte state = 0;
		int ix = x+w;
		int iy = y+h;
		boolean[] hit = new boolean[4];
		hit[0] = levelmap[y/32][x/32] > 0;
		hit[1] = levelmap[y/32][ix/32] > 0;
		hit[2] = levelmap[iy/32][ix/32] > 0;
		hit[3] = levelmap[iy/32][x/32] > 0;
	
		for (int n=x+32; n<ix; n+=32) {
			if (levelmap[y/32][n/32] > 0)
				state |= HIT_TOP;
			if (levelmap[iy/32][n/32] >0)
				state |= HIT_BOTTOM;
		}
		for (int n=y+32; n<iy; n+=32) {
			if (levelmap[n/32][x/32] > 0)
				state |= HIT_LEFT;
			if (levelmap[n/32][ix/32] > 0)
				state |= HIT_RIGHT;
		}
		
		if (hit[0] && hit[1])
			state |= HIT_TOP;
		if (hit[1] && hit[2])
			state |= HIT_RIGHT;
		if (hit[2] && hit[3])
			state |= HIT_BOTTOM;
		if (hit[3] && hit[0])
			state |= HIT_LEFT;
		if (state == 0) {
			if (hit[0]) {
				if (32-x%32 <= 32-y%32)
					state |= HIT_LEFT;
				else
					state |= HIT_TOP;
			} else if (hit[1]) {
				if (x%32 <= 32-y%32)
					state |= HIT_RIGHT;
				else
					state |= HIT_TOP;
			} else if (hit[2]) {
				if (x%32 <= y%32)
					state |= HIT_RIGHT;
				else
					state |= HIT_BOTTOM;
			} else if (hit[3]) {
				if (32-x%32 <= y%32)
					state |= HIT_LEFT;
				else
					state |= HIT_BOTTOM;
			}
		}
		
		return state;
	}
	
	public boolean onScreen(Sprite spr) {
		return (spr.getX()+32*spr.getW()>winX && spr.getX()<winX+256 && 
				spr.getY()+32*spr.getH()>winY && spr.getY()<winY+256);
	}
	public boolean onMap(Sprite spr) {
		return (spr.getY()+spr.getH()*32<+levelmap.length*32);
	}
	
	public BufferedImage draw(BufferedImage screen) {
		Graphics2D g;
		g = (Graphics2D)screen.getGraphics();
		g.clearRect(0, 0, 256, 256);
		if (levelmap[0].length > levelmap.length)
			g.drawImage(background, 0, 0, 256, 256, winX/2+64, 0, winX/2+320, 256, null);
		else
			g.drawImage(background, 0, 0, 256, 256, winX, winY, winX+256, winY+256, null);
		g.drawImage(foreground, 0, 0, 256, 256, winX, winY, winX+256, winY+256, null);
		player.draw(g, player.getX()-winX, player.getY()-winY);
		for (Enemy e : enemies) {
			if (onScreen(e)) {
				e.draw(g, e.getX()-winX, e.getY()-winY);
			}
		}
		for (Item item : items) {
			if (onScreen(item)) {
				item.draw(g, item.getX()-winX, item.getY()-winY);
			}
		}
		for (Projectile proj : projectiles) {
			if (onScreen(proj)) {
				proj.draw(g, proj.getX()-winX, proj.getY()-winY);
			}
		}
		return screen;
	}
}
