package org.newdawn.slick.tests;

import java.io.IOException;

import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.Bootstrap;
import org.newdawn.slick.util.ResourceLoader;
import org.newdawn.slick.util.pathfinding.Mover;
import org.newdawn.slick.util.pathfinding.PathFindingContext;
import org.newdawn.slick.util.pathfinding.TileBasedMap;
import org.newdawn.slick.util.pathfinding.navmesh.Link;
import org.newdawn.slick.util.pathfinding.navmesh.NavMesh;
import org.newdawn.slick.util.pathfinding.navmesh.NavMeshBuilder;
import org.newdawn.slick.util.pathfinding.navmesh.NavPath;
import org.newdawn.slick.util.pathfinding.navmesh.Space;

/**
 * A test to show nav-mesh generation on tile based maps.
 * 
 * @author kevin
 */
public class NavMeshTest extends BasicGame implements PathFindingContext {
	/** The mesh built for this map */
	private NavMesh navMesh;
	/** The builder used to create the nav-mesh from the tile based map */
	private NavMeshBuilder builder;
	/** True if we're showing the open spaces from the mesh */
	private boolean showSpaces = true;
	/** True if we're showing the linking points */
	private boolean showLinks = true;
	/** The path if there is one current found between the two points */
	private NavPath path;
	
	/** The x coordinate of the start of the search */
	private float sx;
	/** The y coordinate of the start of the search */
	private float sy;
	/** The x coordinate of the end of the search */
	private float ex;
	/** The y coordinate of the end of the search */
	private float ey;
	/** The tile based map we're searching across - loaded from a raw file */
	private DataMap dataMap;
	
	/**
	 * Create a new test
	 */
	public NavMeshTest() {
		super("Nav-mesh Test");
	}

	/**
	 * Initialise resources and the map data
	 * 
	 * @param container the container the game is running in 
	 */
	public void init(GameContainer container) throws SlickException {
		container.setShowFPS(false);

		try {
			dataMap = new DataMap("testdata/map.dat");
		} catch (IOException e) {
			throw new SlickException("Failed to load map data", e);
		}
		builder = new NavMeshBuilder();
		navMesh = builder.build(dataMap);
		
		System.out.println("Navmesh shapes: "+navMesh.getSpaceCount());
	}
	
	/**
	 * Update data map etc
	 */
	public void update(GameContainer container, int delta)
			throws SlickException {
		if (container.getInput().isKeyPressed(Input.KEY_1)) {
			showLinks = !showLinks;
		}
		if (container.getInput().isKeyPressed(Input.KEY_2)) {
			showSpaces = !showSpaces;
		}
	}

	/**
	 * Render the game - in this case render the map and diagnostic data
	 * 
	 * @param container The container we're running the game in
	 * @param g The graphics context on which to render
	 */
	public void render(GameContainer container, Graphics g)
			throws SlickException {
		g.translate(50,50);
		for (int x=0;x<50;x++) {
			for (int y=0;y<50;y++) {
				if (dataMap.blocked(this, x, y)) {
					g.setColor(Color.gray);
					g.fillRect((x*10)+1,(y*10)+1,8,8);
				}
			}
		}
		
		if (showSpaces) {
			for (int i=0;i<navMesh.getSpaceCount();i++) {
				Space space = navMesh.getSpace(i);
				if (builder.clear(dataMap, space)) {
					g.setColor(new Color(1,1,0,0.5f));
					g.fillRect(space.getX()*10, space.getY()*10, space.getWidth()*10, space.getHeight()*10);
				}
				g.setColor(Color.yellow);
				g.drawRect(space.getX()*10, space.getY()*10, space.getWidth()*10, space.getHeight()*10);

				if (showLinks) {
					int links = space.getLinkCount();
					for (int j=0;j<links;j++) {
						Link link = space.getLink(j);
						g.setColor(Color.red);
						g.fillRect((link.getX()*10)-2, (link.getY()*10)-2,5,5);
					}
				}
			}
		}
		
		if (path != null) {
			g.setColor(Color.white);
			for (int i=0;i<path.length()-1;i++) {
				g.drawLine(path.getX(i)*10, path.getY(i)*10, path.getX(i+1)*10, path.getY(i+1)*10);
			}
		}
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.newdawn.slick.util.pathfinding.PathFindingContext#getMover()
	 */
	public Mover getMover() {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * @see org.newdawn.slick.util.pathfinding.PathFindingContext#getSearchDistance()
	 */
	public int getSearchDistance() {
		return 0;
	}

	/*
	 * (non-Javadoc)
	 * @see org.newdawn.slick.util.pathfinding.PathFindingContext#getSourceX()
	 */
	public int getSourceX() {
		return 0;
	}

	/*
	 * (non-Javadoc)
	 * @see org.newdawn.slick.util.pathfinding.PathFindingContext#getSourceY()
	 */
	public int getSourceY() {
		return 0;
	}

	/*
	 * (non-Javadoc)
	 * @see org.newdawn.slick.BasicGame#mousePressed(int, int, int)
	 */
	public void mousePressed(int button, int x, int y) {
		float mx = (x - 50) / 10.0f;
		float my = (y - 50) / 10.0f;
		
		if (button == 0) {
			sx = mx;
			sy = my;
		} else {
			ex = mx;
			ey = my;
		}

		path = navMesh.findPath(sx,sy,ex,ey,true);
	}

	/**
	 * A simple raw map implementation for testing purposes
	 * 
	 * @author kevin
	 */
	private class DataMap implements TileBasedMap {
		/** The map data */
		private byte[] map = new byte[50*50];
	
		/**
		 * Create a new map loading it from a file
		 * 
		 * @param ref The location to load the map from
		 * @throws IOException Indicatese a failure to access map data
		 */
		public DataMap(String ref) throws IOException {
			ResourceLoader.getResourceAsStream(ref).read(map);
		}

		/*
		 * (non-Javadoc)
		 * @see org.newdawn.slick.util.pathfinding.TileBasedMap#blocked(org.newdawn.slick.util.pathfinding.PathFindingContext, int, int)
		 */
		public boolean blocked(PathFindingContext context, int tx, int ty) {
			if ((tx < 0) || (ty < 0) || (tx >= 50) || (ty >= 50)) {
				return false;
			}
			
			return map[tx+(ty*50)] != 0;
		}
		
		/*
		 * (non-Javadoc)
		 * @see org.newdawn.slick.util.pathfinding.TileBasedMap#getCost(org.newdawn.slick.util.pathfinding.PathFindingContext, int, int)
		 */
		public float getCost(PathFindingContext context, int tx, int ty) {
			return 1;
		}

		/*
		 * (non-Javadoc)
		 * @see org.newdawn.slick.util.pathfinding.TileBasedMap#getHeightInTiles()
		 */
		public int getHeightInTiles() {
			return 50;
		}

		/*
		 * (non-Javadoc)
		 * @see org.newdawn.slick.util.pathfinding.TileBasedMap#getWidthInTiles()
		 */
		public int getWidthInTiles() {
			return 50;
		}

		/*
		 * (non-Javadoc)
		 * @see org.newdawn.slick.util.pathfinding.TileBasedMap#pathFinderVisited(int, int)
		 */
		public void pathFinderVisited(int x, int y) {
		}
	}
	
	/**
	 * Entry point to out application
	 * 
	 * @param argv The arguments passed to the application
	 */
	public static void main(String[] argv) {
		Bootstrap.runAsApplication(new NavMeshTest(), 600, 600, false);
	}
}