import java.net.*;
import java.util.*;
import java.io.*;
import java.lang.*;
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.*;

public abstract class BaseGame implements KeyListener{
	protected GroupCommunicator comm;
	// protected ArrayList<GameElement> you;
	protected HashMap<InetAddress, HashMap<Integer, GameElement>> world;
	protected boolean[] localIDs;

	public static final short MAX_Y = 600; // ?
	public static final short MAX_X = 800; // ?

	JFrame frame = null;
	BufferStrategy bufferStrategy;
	Rectangle bounds;

	abstract void updateGame();

	// check for network data, add it to world
	// update your own GameObjects based on user input

	public BaseGame(String nicName, int multicastPort, String multicastAddress){
		comm = new GroupCommunicator(nicName, multicastPort, multicastAddress);
		// you = new ArrayList<GameElement>();
		// you.add(new GameElement());
		world = new HashMap<InetAddress, HashMap<Integer, GameElement>>();
		world.put(comm.getInetAddress(), new HashMap<Integer, GameElement>());

		//
		localIDs = new boolean[256];
		Arrays.fill(localIDs, false);

		initGraphics();
	}

	// Keyboard Input
	public void keyTyped(KeyEvent e){
		// DO NOTHING
	}

	// Move when key is Pressed
	public void keyPressed(KeyEvent e){
		processKey(e);
	}

	public void keyReleased(KeyEvent e){
		// DO NOTHING
	}

	//Key Bindings
	public void processKey(KeyEvent e){
		System.out.println("FOO");
		int keyCode = e.getKeyCode();
		HashMap<Integer, GameElement> foo = getMyGameElements();
		GameElement player = getMyGameElements().get(-128);
		for(Integer i : foo.keySet()){
			System.out.println(i + " = " + foo.get(i));
		}
		switch(keyCode){
		//Escape quits
		case KeyEvent.VK_ESCAPE:
			System.exit(0);
		//Arrow Keys
		case KeyEvent.VK_UP:
			player.moveUp();
			break;
		case KeyEvent.VK_DOWN:
			player.moveDown();
			break;
		case KeyEvent.VK_LEFT:
			player.moveLeft();
			break;
		case KeyEvent.VK_RIGHT:
			player.moveRight();
			break;
		//WSAD
		case KeyEvent.VK_W:
			player.moveUp();
			break;
		case KeyEvent.VK_S:
			player.moveDown();
			break;
		case KeyEvent.VK_A:
			player.moveLeft();
			break;
		case KeyEvent.VK_D:
			player.moveRight();
			break;
		}
	}

	protected void initGraphics(){
		KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
		// Get the GraphicsDevice from your machine. The main monitor.
		GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment
				.getLocalGraphicsEnvironment();
		GraphicsDevice graphicsDevice = graphicsEnvironment
				.getDefaultScreenDevice();

		// Create the frame which will become full screen.
		frame = new JFrame();
		frame.setUndecorated(true);
		frame.setIgnoreRepaint(true);
		frame.setBackground(Color.BLACK);
		frame.setFocusable(true);

		// Set the frame to fullscreen
		graphicsDevice.setFullScreenWindow(frame);
		if(graphicsDevice.isDisplayChangeSupported()){
			graphicsDevice.setDisplayMode(new DisplayMode(800, 600, 16, 0));
		}

		// I don't know what this does, but it's important
		frame.createBufferStrategy(2); // 2 buffers
		bounds = frame.getBounds();
		bufferStrategy = frame.getBufferStrategy();

		// Create and use a transparent cursor
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		int[] pixels = new int[16 * 16];
		Image image = toolkit.createImage(new MemoryImageSource(16, 16, pixels,
				0, 16));
		Cursor transparentCursor = toolkit.createCustomCursor(image, new Point(
				0, 0), "invisiblecursor");
		frame.setCursor(transparentCursor);
		frame.requestFocus();
		frame.addKeyListener(this);
		
		Component compFocusOwner =
		        KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
		System.out.println(compFocusOwner);
	}

	public void start(){
		createGameElement();
		try{
			while(true){
				Thread.currentThread().sleep(34);
				applyGDeltas(comm.flushBuffer());
				updateGame();
				sendMyGDeltas();
			}
		}
		catch(InterruptedException e){

		}
	}

	public GameElement createGameElement(){
		int i = -128;
		for(; i < 128; i++){
			if(localIDs[i+128] == false){
				break;
			}
		}
		localIDs[i+128] = true;

		GameElement g = new GameElement();
		g.setType((byte) 1);
		//g.setHealth((byte) 127);
		Random gen = new Random();
		/*
		g.setX((short) (gen.nextDouble() * MAX_X));
		g.setY((short) (gen.nextDouble() * MAX_Y));
		*/
		g.setX((short)200);
		g.setY((short)200);
		world.get(comm.getInetAddress()).put(i, g);
		return g;
	}

	public GameElement getGameElement(InetAddress addr, Integer localID){
		if(!world.containsKey(addr))
			world.put(addr, new HashMap<Integer, GameElement>());
		if(world.get(addr).containsKey(localID))
			return world.get(addr).get(localID);
		return null;
	}

	public void applyGDeltas(java.util.List<GDelta> deltas){
		System.out.println(deltas.size());
		for(GDelta delta: deltas){
			//System.out.println(delta);
			GameElement g = getGameElement(delta.source, delta.localID());
			if(g == null){
				g = new GameElement(delta);
				world.get(delta.source).put((int) (delta.localID() - 128), g);
			}
			else if(delta.health == (byte) -128){
				world.get(delta.source).remove(delta.localID());
			}
			else{
				g.applyDelta(delta);
			}
		}
	}

	public HashMap<Integer, GameElement> getMyGameElements(){
		return world.get(comm.getInetAddress());
	}

	public HashMap<InetAddress, HashMap<Integer, GameElement>> getAllObstacles(){
		HashMap<InetAddress, HashMap<Integer, GameElement>> obstacles;
		obstacles = new HashMap<InetAddress, HashMap<Integer, GameElement>>();
		for(InetAddress i: world.keySet()){
			obstacles.put(i, world.get(i));
			for(Integer j: world.get(i).keySet()){
				if((int) world.get(i).get(j).getType() > 1){
					obstacles.get(i).put(j, world.get(i).get(j));
				}
			}
		}
		return obstacles;
	}

	public HashMap<InetAddress, GameElement> getOtherPlayers(){
		HashMap<InetAddress, GameElement> players;
		players = new HashMap<InetAddress, GameElement>();
		for(InetAddress i: world.keySet()){
			if(!i.equals(comm.getInetAddress())){
				for(Integer j: world.get(i).keySet()){
					if((int) world.get(i).get(j).getType() < 2){
						players.put(i, world.get(i).get(j));
						break;
					}
				}
			}
		}
		return players;
	}

	public void sendMyGDeltas(){
		HashMap<Integer, GameElement> myGameElements = getMyGameElements();
		for(Integer i: myGameElements.keySet()){
			GDelta d = myGameElements.get(i).getGDelta();
			d.source = comm.getInetAddress();
			d.localID = (byte) (i - 128);
			comm.sendData(d);
			if(d.health == (byte) -128){
				myGameElements.remove(i);
			}
		}
	}

}
