package edu.berkeley.nlp.starcraft.drawing;

import org.bwapi.proxy.model.Color;
import org.bwapi.proxy.model.CoordinateType;
import org.bwapi.proxy.model.Game;
import org.bwapi.proxy.model.Unit;

import algorithms.MathUtils;

import edu.berkeley.nlp.starcraft.map.MapUtils;

/**
 * 
 * @author Eugene Ma
 */
public class DrawUtils {
	// Drawing for build tiles.
	public static void drawBuildTileRect(DrawingContext context,
			int tileLeft, int tileTop, int tileWidth, int tileHeight,
			int insetSize, Color color, boolean isSolidFlag)
	{
		final int left = MapUtils.BUILD_TILE_SIZE * tileLeft;
		final int top = MapUtils.BUILD_TILE_SIZE * tileTop;
		final int right = MapUtils.BUILD_TILE_SIZE * (tileLeft + tileWidth);
		final int bottom = MapUtils.BUILD_TILE_SIZE * (tileTop + tileHeight);
		context.drawBox(CoordinateType.MAP,
				left + insetSize, top + insetSize,
				right - insetSize, bottom - insetSize,
				color, isSolidFlag);
	}
	public static void drawRectAtBuildTile(DrawingContext context, int tileX, int tileY,
			int insetSize, Color color, boolean isSolidFlag)
	{
		drawBuildTileRect(context, tileX, tileY, 1, 1, insetSize, color, isSolidFlag);
	}
	public static void drawCircleAtBuildTile(DrawingContext context, int tileX, int tileY,
			int insetSize, Color color, boolean isSolidFlag)
	{
		final int x = MapUtils.BUILD_TILE_SIZE * tileX + MapUtils.BUILD_TILE_SIZE / 2;
		final int y = MapUtils.BUILD_TILE_SIZE * tileY + MapUtils.BUILD_TILE_SIZE / 2;
		context.drawCircle(CoordinateType.MAP, x, y, MapUtils.BUILD_TILE_SIZE / 2 - insetSize,
				color, isSolidFlag);
	}
	public static void drawXAtBuildTile(DrawingContext context,
			int tileX, int tileY, int insetSize, Color color)
	{
		final int left = MapUtils.BUILD_TILE_SIZE * tileX;
		final int top = MapUtils.BUILD_TILE_SIZE * tileY;
		final int right = left + MapUtils.BUILD_TILE_SIZE;
		final int bottom = top + MapUtils.BUILD_TILE_SIZE;
		context.drawLine(CoordinateType.MAP,
				left + insetSize, top + insetSize,
				right - insetSize, bottom - insetSize,
				color);
		context.drawLine(CoordinateType.MAP,
				right - insetSize, top + insetSize,
				left + insetSize, bottom - insetSize,
				color);
	}
	public static void drawTextAtBuildTile(DrawingContext context,
			int tileX, int tileY, int offsetX, int offsetY, String text)
	{
		context.drawText(CoordinateType.MAP,
				MapUtils.BUILD_TILE_SIZE * tileX + offsetX,
				MapUtils.BUILD_TILE_SIZE * tileY + offsetY,
				text);
	}
	
	// *****************************************************************************************
	
	// Drawing for walk tiles.
	public static void drawWalkTileRect(DrawingContext context,
			int tileLeft, int tileTop, int tileWidth, int tileHeight,
			int insetSize, Color color, boolean isSolidFlag)
	{
		final int left = MapUtils.WALK_TILE_SIZE * tileLeft;
		final int top = MapUtils.WALK_TILE_SIZE * tileTop;
		final int right = MapUtils.WALK_TILE_SIZE * (tileLeft + tileWidth);
		final int bottom = MapUtils.WALK_TILE_SIZE * (tileTop + tileHeight);
		context.drawBox(CoordinateType.MAP,
				left + insetSize, top + insetSize,
				right - insetSize, bottom - insetSize,
				color, isSolidFlag);
	}
	public static void drawRectAtWalkTile(DrawingContext context, int tileX, int tileY,
			int insetSize, Color color, boolean isSolidFlag)
	{
		drawWalkTileRect(context, tileX, tileY, 1, 1, insetSize, color, isSolidFlag);
	}
	public static void drawCircleAtWalkTile(DrawingContext context, int tileX, int tileY,
			int insetSize, Color color, boolean isSolidFlag)
	{
		final int x = MapUtils.WALK_TILE_SIZE * tileX + MapUtils.WALK_TILE_SIZE / 2;
		final int y = MapUtils.WALK_TILE_SIZE * tileY + MapUtils.WALK_TILE_SIZE / 2;
		context.drawCircle(CoordinateType.MAP, x, y, MapUtils.WALK_TILE_SIZE / 2 - insetSize,
				color, isSolidFlag);
	}
	public static void drawXAtWalkTile(DrawingContext context,
			int tileX, int tileY, int insetSize, Color color)
	{
		final int left = MapUtils.WALK_TILE_SIZE * tileX;
		final int top = MapUtils.WALK_TILE_SIZE * tileY;
		final int right = left + MapUtils.WALK_TILE_SIZE;
		final int bottom = top + MapUtils.WALK_TILE_SIZE;
		context.drawLine(CoordinateType.MAP,
				left + insetSize, top + insetSize,
				right - insetSize, bottom - insetSize,
				color);
		context.drawLine(CoordinateType.MAP,
				right - insetSize, top + insetSize,
				left + insetSize, bottom - insetSize,
				color);
	}
	
	// *****************************************************************************************
	
	// Drawing text.
	public static void drawCoordinates(DrawingContext context, int interval) {
		for (int y = 0; y < Game.getInstance().mapHeight(); y += interval) {
			for (int x = 0; x < Game.getInstance().mapWidth(); x += interval) {
				context.drawTextMap(
						(int) ((x + 0.5) * MapUtils.BUILD_TILE_SIZE),
						(int) ((y + 0.5) * MapUtils.BUILD_TILE_SIZE),
						"(" + x + ", " + y + ")");
			}
		}
	}
	
	// *****************************************************************************************
		
	// Drawing shapes.

	public static void drawXAt(DrawingContext context,
			CoordinateType ctype, double x, double y, double radius, Color color)
	{
		// If the radius is the hypotenuse of a 45-45-90 triangle, get the length of its sides.
		final double sideLength = radius * Math.sqrt(2) / 2.0;
		
		context.drawLine(ctype,
				MathUtils.round(x - sideLength), MathUtils.round(y - sideLength),
				MathUtils.round(x + sideLength), MathUtils.round(y + sideLength),
				color);
		context.drawLine(ctype,
				MathUtils.round(x + sideLength), MathUtils.round(y - sideLength),
				MathUtils.round(x - sideLength), MathUtils.round(y + sideLength),
				color);
	}
	
	// -----------------------------------------------------------------------------------------

	// Draw arcs.
	public static void drawArc(DrawingContext context,
			CoordinateType ctype, double x, double y, double radius, double startAngle,
			double endAngle, double maxSegmentLength, Color color)
	{
		if (startAngle == endAngle) {
			context.drawDot(ctype,
					MathUtils.round(x + radius * Math.cos(startAngle)),
					MathUtils.round(y + radius * Math.sin(startAngle)),
					color);
		}
		else {
			// If we think of this as an isosceles triangle
			// where the base is a segment and the sides are the radius,
			// we are calculating the angle of the apex.
			final double maxSectionAngle = 2 * Math.asin(maxSegmentLength / (2 * radius));
			
			// Calculate the number of sections so that we do not exceed the max segment length.
			final double angleDifference = endAngle - startAngle;
			final double decimalNumSections = angleDifference / maxSectionAngle;
			int numSections = (int) decimalNumSections;
			if (Math.IEEEremainder(endAngle - startAngle, maxSectionAngle) > 0) {
				numSections += 1;
			}
			
			// Draw the arc segment by segment.
			int originX = MathUtils.round(x + radius * Math.cos(startAngle));
			int originY = MathUtils.round(y + radius * Math.sin(startAngle));
			for (int section = 1; section <= numSections; ++section) {
				double terminusAngle = startAngle + angleDifference * (section / (double) numSections);
				int terminusX = MathUtils.round(x + radius * Math.cos(terminusAngle));
				int terminusY = MathUtils.round(y + radius * Math.sin(terminusAngle));
				
				context.drawLine(ctype, originX, originY, terminusX, terminusY, color);
				
				originX = terminusX;
				originY = terminusY;
			}
		}
	}
	
	// *****************************************************************************************
	
	// Drawing for units.
	
	public static void drawBuildTileRect(DrawingContext context,
			Unit unit, int insetSize, Color color, boolean isSolidFlag)
	{
		drawBuildTileRect(context, unit.getTilePosition().x(), unit.getTilePosition().y(),
				unit.getType().tileWidth(), unit.getType().tileHeight(),
				insetSize, color, isSolidFlag);
	}

	public static void drawUnitDimension(DrawingContext context,
			Unit unit, int insetSize, Color color, boolean isSolidFlag)
	{
		context.drawBox(CoordinateType.MAP,
				unit.getPosition().x() - unit.getType().dimensionLeft(),
				unit.getPosition().y() - unit.getType().dimensionUp(),
				unit.getPosition().x() + unit.getType().dimensionRight() + 1,
				unit.getPosition().y() + unit.getType().dimensionDown() + 1,
				color, isSolidFlag);
	}
	
	// *****************************************************************************************
	
	public static void drawBordersOfFogOfWar(DrawingContext context, Color color) {
		// Draw the borders of the fog of war.
		for (int buildTileX = 0; buildTileX < Game.getInstance().mapWidth(); ++buildTileX) {
			for (int buildTileY = 0; buildTileY < Game.getInstance().mapHeight(); ++buildTileY) {
				if (MapUtils.buildTilePositionIsOnMap(buildTileX, buildTileY)) {
					if (MapUtils.buildTilePositionIsOnMap(buildTileX + 1, buildTileY)) {
						if (Game.getInstance().isVisible(buildTileX, buildTileY)
								!= Game.getInstance().isVisible(buildTileX + 1, buildTileY))
						{
							final int x = (buildTileX + 1) * MapUtils.BUILD_TILE_SIZE;
							final int y = buildTileY * MapUtils.BUILD_TILE_SIZE;
							context.drawLineMap(x, y, x, y + MapUtils.BUILD_TILE_SIZE, color);
						}
					}
					if (MapUtils.buildTilePositionIsOnMap(buildTileX, buildTileY + 1)) {
						if (Game.getInstance().isVisible(buildTileX, buildTileY)
								!= Game.getInstance().isVisible(buildTileX, buildTileY + 1))
						{
							final int x = buildTileX * MapUtils.BUILD_TILE_SIZE;
							final int y = (buildTileY + 1) * MapUtils.BUILD_TILE_SIZE;
							context.drawLineMap(x, y, x + MapUtils.BUILD_TILE_SIZE, y, color);
						}
					}
				}
			}
		}
	}
}
