package edu.berkeley.nlp.starcraft.bots;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.bwapi.proxy.ProxyBot;
import org.bwapi.proxy.model.Color;
import org.bwapi.proxy.model.Game;
import org.bwapi.proxy.model.Order;
import org.bwapi.proxy.model.Player;
import org.bwapi.proxy.model.Position;
import org.bwapi.proxy.model.Unit;

import collections.bitmap.Bitmap;

import edu.berkeley.nlp.starcraft.drawing.DrawUtils;
import edu.berkeley.nlp.starcraft.drawing.GameDrawingContext;
import edu.berkeley.nlp.starcraft.map.ConnectedComponentsMapData;
import edu.berkeley.nlp.starcraft.map.StaticMapData;

/**
 * Test bot for observing what Starcraft does (like what orders are given to units, etc.).
 * 
 * @author Eugene Ma
 */
public class DisplayTestBot implements ProxyBot {
	protected Map<Unit, Integer> myUnitsToIds = new HashMap<Unit, Integer>();
	protected Map<Unit, Order> myUnitsToOrders = new HashMap<Unit, Order>();
	protected int myNewId = 0;
	
	protected StaticMapData myStaticMapData = new StaticMapData();
	protected ConnectedComponentsMapData myConnectedComponentsMapData = new ConnectedComponentsMapData();

	protected List<String> myLoggedMessages = new LinkedList<String>();
	
	protected void printMessage(String message) {
		Game.getInstance().printf(message);
		myLoggedMessages.add(message);
	}
	
	@Override
	public void onStart() {
		Game.getInstance().enableFlag(org.bwapi.proxy.model.Flag.USER_INPUT);
		
		myStaticMapData.onStart();
		myConnectedComponentsMapData.onStart();
		
		///*
		// TODO: Comment out this code to avoid printing the bitmap every time.
		final String connectedComponentsBitmapFileName = "Connected Components.bmp";
		try {
			Bitmap.save(myConnectedComponentsMapData.toBitmap(), new File(connectedComponentsBitmapFileName));
		}
		catch (Exception x) {
			printMessage("An error occurred while saving \"" + connectedComponentsBitmapFileName + "\"");
			printMessage(x.getMessage());
		}
		//*/
	}

	@Override
	public void onFrame() {
		myStaticMapData.draw(GameDrawingContext.INSTANCE);
		//myConnectedComponentsMapData.draw(GameDrawingContext.INSTANCE);	// This is slow.
		
		DrawUtils.drawCoordinates(GameDrawingContext.INSTANCE, 5);

		for (Unit unit : Game.getInstance().getAllUnits()) {
			// Make sure every unit has an ID.
			Integer id = myUnitsToIds.get(unit);
			if (id == null) {
				myUnitsToIds.put(unit, myNewId++);
			}
			
			// Track when orders change.
			Order order = myUnitsToOrders.get(unit);
			if (order == null) {
				// Initial order.
				myUnitsToOrders.put(unit, unit.getOrder());
				String message = "Initial order for " + unit.getType().getName()
						+ " with ID " + myUnitsToIds.get(unit) + ": "
						+ unit.getOrder().getName();
				
				printMessage(message);
			}
			else if (!order.equals(unit.getOrder())) {
				// Changed order.
				myUnitsToOrders.put(unit, unit.getOrder());
				String message = "New order for " + unit.getType().getName()
						+ " with ID "  + myUnitsToIds.get(unit) + ": "
						+ unit.getOrder().getName();
						
				printMessage(message);
				
				/*
				printMessage((unit.isConstructing()? "is constructing":"is not constructing")
						+ ", " + (unit.isBeingConstructed()? "is being constructed":"is not being constructed")
						+ ", " + (unit.isCompleted()? "is completed":"is not completed"));
				//*/
			}
		}
		
		///*
		// Draw a box around units.
		for (Unit unit : Game.getInstance().getAllUnits()) {
			if (unit.exists()) {
				DrawUtils.drawUnitDimension(GameDrawingContext.INSTANCE, unit, 0, getColor(unit), false);
				
				if (unit.getType().isBuilding()) {
					DrawUtils.drawBuildTileRect(GameDrawingContext.INSTANCE, unit, 0, getColor(unit), false);
				}
			}
		}
		//*/
		
		///*
		// Draw the orders of units.
		for (Unit unit : Game.getInstance().getAllUnits()) {
			if (unit.exists()) {
				Game.getInstance().drawTextMap(unit.getPosition().x(), unit.getPosition().y(),
						myUnitsToIds.get(unit) + ": " + unit.getOrder().getName());
			}
		}
		//*/
		
		DrawUtils.drawBordersOfFogOfWar(GameDrawingContext.INSTANCE, Color.WHITE);
		
		Game.getInstance().drawTextScreen(0, 0, "Minerals: " + Game.getInstance().self().minerals());
		Game.getInstance().drawTextScreen(0, 10, "Gas: " + Game.getInstance().self().gas());
		
		/*
		// TODO: Remove debug code.
		// Giving units multiple orders on the same frame slows down Starcraft.
		// How do you crash Starcraft?
		for (Unit unit : Game.getInstance().self().getUnits()) {
			unit.stop();
			unit.rightClick(new Position(100, 100));
			unit.stop();
			unit.rightClick(new Position(500, 500));
		}
		//*/
	}

	@Override
	public void onSendText(String text) {
		//myLoggedMessages.add(text);
	}

	@Override
	public void onEnd(boolean isWinner) {
		try {
			final FileOutputStream outputStream = new FileOutputStream(new File("Display Test Bot Output.txt"));
			final OutputStreamWriter outputWriter = new OutputStreamWriter(outputStream);
			final BufferedWriter out = new BufferedWriter(outputWriter);
			
			for (String loggedMessage : myLoggedMessages) {
				out.write(loggedMessage);
				out.newLine();
			}
			
			out.close();
		}
		catch (Exception x) {
			// TODO: Handle.
		}
	}

	@Override
	public void onNukeDetect(Position arg0) {
		
	}

	@Override
	public void onPlayerLeft(Player arg0) {
		
	}

	@Override
	public void onUnitCreate(Unit unit) {
		Game.getInstance().printf(unit.getType().getName() + " of " + getPlayerName(unit) + " is created.");
	}
	
	@Override
	public void onUnitDestroy(Unit unit) {
		Game.getInstance().printf(unit.getType().getName() + " of " + getPlayerName(unit) + " is destroyed.");
	}

	@Override
	public void onUnitHide(Unit unit) {
		Game.getInstance().printf(unit.getType().getName() + " of " + getPlayerName(unit) + " is hidden.");
	}

	@Override
	public void onUnitMorph(Unit unit) {
		Game.getInstance().printf(unit.getType().getName() + " of " + getPlayerName(unit) + " is morphing.");
	}

	@Override
	public void onUnitRenegade(Unit unit) {
		Game.getInstance().printf(unit.getType().getName() + " of " + getPlayerName(unit) + " is a renegade.");
	}

	@Override
	public void onUnitShow(Unit unit) {
		Game.getInstance().printf(unit.getType().getName() + " of " + getPlayerName(unit) + " is shown.");
	}
	
	protected String getPlayerName(Unit unit) {
		if (unit.getPlayer().isEnemy(Game.getInstance().self())) {
			return "enemy";
		}
		else if (unit.getPlayer().equals(Game.getInstance().self())) {
			return "self";
		}
		else {
			return "none";
		}
	}
	
	protected Color getColor(Unit unit) {
		if (unit.getPlayer().isEnemy(Game.getInstance().self())) {
			return Color.RED;
		}
		else if (unit.getPlayer().equals(Game.getInstance().self())) {
			return Color.GREEN;
		}
		else {
			return Color.YELLOW;
		}
	}

	@Override
  public void onReceiveText(Player player, String text) {
	  // TODO Auto-generated method stub
	  
  }

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