import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
import java.util.*;

public class A extends JFrame {

	/*
	* This is Tekicars4k. It uses an Image drawn upon a JFrame
	* for graphics.
	*
	* //Morre
	*/

	/*
	* This scenario will bug the program:
	*   ARROWDOWN
	*	  ROAD
	*	  ROAD
	*   SPAWNUP
	*/

	//========================= CONSTANTS AND GLOBAL VARIABLES =============================

	/*
	* WIDTH, HEIGHT specifies frame size.
	*/
	private static final int LEVEL_COUNT = 6;

	private static final int WIDTH = 640;
	private static final int HEIGHT = 480;

	private static final int GRID_SIZE = 11;
	private static final int CELL_SIZE = 32;

	//To get direction, take -(negative number-1)%4: 0 = up, 1 = right, 2 = down, 3 = left

	private static final int GREEN_ENTRY1 = -1;
	private static final int GREEN_ENTRY2 = -2;
	private static final int GREEN_ENTRY3 = -3;
	private static final int GREEN_ENTRY4 = -4;

	private static final int GREEN_EXIT1 = -5;
	private static final int GREEN_EXIT2 = -6;
	private static final int GREEN_EXIT3 = -7;
	private static final int GREEN_EXIT4 = -8;

	private static final int BLUE_ENTRY1 = -9;
	private static final int BLUE_ENTRY2 = -10;
	private static final int BLUE_ENTRY3 = -11;
	private static final int BLUE_ENTRY4 = -12;

	private static final int BLUE_EXIT1 = -13;
	private static final int BLUE_EXIT2 = -14;
	private static final int BLUE_EXIT3 = -15;
	private static final int BLUE_EXIT4 = -16;

	private static final int ROAD = 1;
	private static final int WALL = 2;

	private static final int BOOST1 = 5;
	private static final int BOOST2 = 6;
	private static final int BOOST3 = 7;
	private static final int BOOST4 = 8;

	private static final Font font = new Font("Arial",Font.PLAIN,16);

	/*
	* PreferBLUE frame time in milliseconds. Default is 16, which gives
	* 1000/16 ~= 60 fps.
	*/
	private static final int FRAME_TIME = 16;

	/*
	* Says whether or not a session is running. If this is set to true,
	* the outer loop will restart in the next iteration, restarting game.
	*/
	private boolean running;

	/*
	* Key state array. Indexed with the KeyEvent key constants.
	*/
	private boolean keys[] = new boolean[1024];

	/*
	* Mouse button state array. Indexed with the mouse button numbers.
	*/
	private boolean mouse[] = new boolean[8];
	private int mouseX = 0;
	private int mouseY = 0;

	/*
	* Quality settings boolean, toggled with Q. Defaults to false.
	*/
	//private boolean quality = true;

	private boolean testing = false; //Is a test running?

	//0: x
	//1: y
	//2: color
	//3: rotation
	//4: spawns
	//5: pause
	//6: repeat
	//7: offset
	private ArrayList<int[]> spawners = new ArrayList<int[]>();

	//0: x
	//1: y
	//2: color
	//3: rotation
	private ArrayList<int[]> units = new ArrayList<int[]>();

	//The arrays are 2D - [0] is type, [1] is amount
	private ArrayList<int[]> tileset = new ArrayList<int[]>();

	//The currently held tile
	private int[] heldStack = null;

	private int level = 0;

	private boolean[][] altered = new boolean[GRID_SIZE][GRID_SIZE];
	private int[][] grid = new int[GRID_SIZE][GRID_SIZE];

	private boolean justChanged = true;

	//============================== MAIN METHOD ===================================

	public static void main(String[] args)	{
		new A();
	}

	//============================== CONSTRUCTOR ===================================

	public A() {

		/*
		* Initialize frame, set dimensions, etc.
		*/
		setSize(new Dimension(WIDTH+getInsets().right*getInsets().left,HEIGHT+getInsets().top+getInsets().bottom));
		setLocation(100,100);
		setResizable(false);
		setTitle("Tekicars4k");
		enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
		setVisible(true);

	//============================== OUTER LOOP  ===================================
	//				(runs each time game is played or restarted)

		while(true) {

			/*
			* ileft and itop are the insets of the JFrame; they might be needed
			* if you draw stuff directly to the gFrame (JFrame's) graphics context.
			* They can also be used for translating the buffer image when drawing
			* onto gFrame, if you desire to see the top left corner of the buffer.
			*/
			int ileft = getInsets().left;
			int itop = getInsets().top;

			/*
			* Create graphics contexts. The off image is drawn to the JFrame,
			* making it possible to do pixel operations for the graphics. Also,
			* drawing to g (the buffer's graphics context) will make sure that
			* what you draw is double-buffeBLUE.
			*/
			Image off = createImage(WIDTH, HEIGHT);

			/*
			* Initialize timing and loop logic.
			*/
			running = true;
			long lastFrame=System.currentTimeMillis();
			float yield=10000f;
			float frameAverage=(float)FRAME_TIME;

			/*
			* Initialize variables for the game.
			* fcount is a variable that increases each frame.
			*/
			int fcount = 0;
			int time = 0;

			int gridOffX = 50;
			int gridOffY = 76;

			units.clear();
			spawners.clear();
			heldStack = null;
			testing = false;

			//NEW FORMAT:
			//

			//OLD FORMAT:
			//Format: 0x[   REPEAT    ][OFFSET][PAUSE][SPAWN][COLOR][DIR][0] (for spawners)
			//Format: 0x[                   BLANK                  ][DIR][1] (for exits)
			//Format: 0x[                 PIECE TYPE               ][DIR][2] (for pieces)


			//int exit1 = 0x11;
			//int entry1 = 0xf01;

			if(justChanged) {
				tileset.clear();
				grid = new int[GRID_SIZE][GRID_SIZE];
				altered = new boolean[GRID_SIZE][GRID_SIZE];

				if(level == 0) {

					grid[5][0] = BLUE_EXIT1;
					grid[5][GRID_SIZE-1] = BLUE_ENTRY1;
					grid[0][5] = GREEN_ENTRY2;
					grid[GRID_SIZE-1][5] = GREEN_EXIT2;

					//grid[5][6] = BOOST1;

					int[] roads = {ROAD,2};
					tileset.add(roads);
				}

				if(level == 1) {
					grid[6][0] = BLUE_EXIT1;
					grid[4][GRID_SIZE-1] = GREEN_EXIT3;
					grid[0][5] = GREEN_ENTRY2;
					grid[0][7] = GREEN_ENTRY2;
					grid[GRID_SIZE-1][5] = BLUE_ENTRY4;

					int[] walls = {WALL,2};
					tileset.add(walls);
					int[] roads = {ROAD,2};
					tileset.add(roads);
				}

				/*if(level == ALTERNATEVERSIONOFLEVEL2) {
					grid[4][0] = BLUE_ENTRY3;
					grid[6][0] = BLUE_ENTRY3;
					grid[8][0] = BLUE_ENTRY3;

					grid[0][8] = BLUE_EXIT4;

					grid[8][5] = WALL;
					grid[7][9] = WALL;
					//grid[7][4] = ROAD;
					grid[4][1] = ROAD;
					grid[8][1] = ROAD;
					grid[5][4] = ROAD;
					grid[5][5] = ROAD;

					grid[GRID_SIZE-1][3] = BOOST3;
					grid[GRID_SIZE-1][8] = BOOST4;

					int[] walls = {WALL,2};
					tileset.add(walls);
					int[] roads = {ROAD,1};
					tileset.add(roads);
					int[] boost2 = {BOOST2,2};
					tileset.add(boost2);

				}*/

				if(level == 2) {
					grid[GRID_SIZE-1][4] = GREEN_ENTRY4;
					grid[GRID_SIZE-1][6] = GREEN_ENTRY4;
					grid[8][GRID_SIZE-1] = GREEN_EXIT3;

					grid[8][GRID_SIZE-2] = WALL;
					grid[4][4] = WALL;


					int[] walls = {WALL,3};
					tileset.add(walls);
					int[] roads = {ROAD,2};
					tileset.add(roads);
					int[] boost2 = {BOOST2,1};
					tileset.add(boost2);

				}

				if(level == 3) {
					grid[4][0] = BLUE_ENTRY3;
					grid[6][0] = BLUE_ENTRY3;
					grid[8][0] = BLUE_ENTRY3;

					grid[0][8] = BLUE_EXIT4;

					grid[8][5] = WALL;
					grid[7][9] = WALL;
					//grid[7][4] = ROAD;

					grid[GRID_SIZE-1][3] = BOOST3;
					grid[GRID_SIZE-1][8] = BOOST4;

					int[] walls = {WALL,2};
					tileset.add(walls);
					int[] roads = {ROAD,4};
					tileset.add(roads);
					int[] boost2 = {BOOST2,2};
					tileset.add(boost2);

				}

				if(level == 4) {
					grid[5][0] = GREEN_ENTRY3;
					grid[3][GRID_SIZE-1] = BLUE_ENTRY1;
					grid[7][GRID_SIZE-1] = BLUE_ENTRY1;
					grid[0][4] = BLUE_EXIT4;
					grid[5][GRID_SIZE-1] = GREEN_EXIT3;

					int[] walls = {WALL,4};
					tileset.add(walls);
					int[] roads = {ROAD,4};
					tileset.add(roads);

				}

				if(level == 5) {
					grid[3][GRID_SIZE-1] = BLUE_ENTRY1;
					grid[7][GRID_SIZE-1] = BLUE_ENTRY1;
					grid[0][4] = BLUE_EXIT4;
					grid[0][5] = GREEN_EXIT4;
					grid[4][0] = GREEN_ENTRY3;
					grid[6][0] = GREEN_ENTRY3;
					grid[3][2] = WALL;
					grid[4][7] = WALL;
					grid[6][3] = WALL;
					grid[5][4] = BOOST4;

					grid[7][0] = WALL;
					grid[GRID_SIZE-1][1] = WALL;
					grid[GRID_SIZE-2][5] = WALL;
					/*grid[8][4] = BOOST4;
					grid[7][3] = WALL;
					grid[6][4] = ROAD;
					grid[7][4] = ROAD;
					grid[7][5] = ROAD;*/
					grid[4][GRID_SIZE-2] = WALL;
					int[] walls = {WALL,4};
					tileset.add(walls);
					int[] roads = {ROAD,4};
					tileset.add(roads);
					int[] boost3 = {BOOST3,1};
					tileset.add(boost3);

				}


				justChanged = false;
			}


			/*grid[5][5] = WALL;
			grid[6][6] = ROAD;
			grid[7][6] = ROAD;
			for(int i=6; i<GRID_SIZE-1; i++) {
				grid[5][i] = ROAD;
			}
			grid[5][8] = 0;*/
			/*grid[5][GRID_SIZE-5] = ROAD;
			grid[5][GRID_SIZE-4] = ROAD;*/
			/*for(int i=1; i<GRID_SIZE-1; i++) {
				grid[5][i] = ROAD;
			}
			grid[4][5] = ROAD;
			grid[3][5] = ROAD;*/

			int[] xpos = {0,10,-10};
			int[] ypos = {-6,6,6};
			Polygon arrow = new Polygon(xpos,ypos,3);

			for(int i=0; i<GRID_SIZE; i++) {
				for(int j=0; j<GRID_SIZE; j++) {
					int cell = grid[i][j];
					if(cell < 0 && (-(cell+1)/4)%2 == 0) { //Square is a spawner
						//System.out.println("Spawner found!");
						int rotation = ((-(cell+1))%4);
						int color = (-cell)/9;
						int spawns = 1;
						int pause = 1;
						int repeat = 5;
						int offset = 0;
						int[] spawner = {i,j,color,rotation,spawns,pause,repeat,offset};
						spawners.add(spawner);
					}
				}
			}

			BufferedImage texture = new BufferedImage(CELL_SIZE,CELL_SIZE,BufferedImage.TYPE_INT_RGB);
			Graphics2D gTexture = (Graphics2D)texture.getGraphics();
			//gTexture.setColor(new Color(0, 180, 0));
			gTexture.setColor(new Color(170,130,40));
			gTexture.fillRect(0,0,CELL_SIZE,CELL_SIZE);
			//gTexture.setColor(new Color(30, 130, 10));
			gTexture.setColor(new Color(150,110,40));
			gTexture.setStroke(new BasicStroke(12));
			gTexture.drawLine(-20,40,40,-20);
			gTexture.drawLine(12,40,40,12);

			//System.out.println("test");

	//============================== INNER LOOP  ===================================
	//							(runs each frame)
			while(running)	{

				Graphics2D gFrame = (Graphics2D)getGraphics();
				Graphics2D g = (Graphics2D)off.getGraphics();


				/*
				* Increase frame count.
				*/
				fcount++;

				/*
				* Clear the off buffer.
				*/
				g.setColor(new Color(0x000000));
				g.fillRect(0,0,WIDTH,HEIGHT);

				g.translate(gridOffX,gridOffY);

				g.setColor(new Color(120,90,20));
				for(int i=0; i<GRID_SIZE; i++) {
					for(int j=0; j<GRID_SIZE; j++) {
						if(mouse[1] && mouseX > i*CELL_SIZE+gridOffX && mouseX < i*CELL_SIZE+gridOffX+CELL_SIZE && mouseY > j*CELL_SIZE+gridOffY && mouseY < j*CELL_SIZE+gridOffY+CELL_SIZE) {
							mouse[1] = false;
							if(heldStack != null && grid[i][j] == 0 && heldStack[1] > 0 && !testing) {
								altered[i][j] = true;
								grid[i][j] = heldStack[0];
								heldStack[1]--;
							}
							//heldTile = null;
						}

						g.translate(i*CELL_SIZE,j*CELL_SIZE);
						int cell = grid[i][j];
						if(cell == 0) {
							g.drawImage(texture,0,0,this);
							g.drawRect(0,0,CELL_SIZE,CELL_SIZE);
						}


						g.translate(-i*CELL_SIZE,-j*CELL_SIZE);
					}
				}

				for(int i=0; i<GRID_SIZE; i++) {
					for(int j=0; j<GRID_SIZE; j++) {
						if(altered[i][j]) {
							g.translate(i*CELL_SIZE,j*CELL_SIZE);
							//g.setColor(new Color(200,0,0,50));
							//g.fillRect(-5,-5,CELL_SIZE+10,CELL_SIZE+10);
							g.setColor(new Color(255,255,200,60));
							g.fillRect(-5,-5,CELL_SIZE+10,CELL_SIZE+10);
							//g.fillRect(0,0,CELL_SIZE,CELL_SIZE);
							g.translate(-i*CELL_SIZE,-j*CELL_SIZE);
						}
					}
				}

				g.setStroke(new BasicStroke(12));
				g.setColor(new Color(160,120,30));
				g.fillRect(CELL_SIZE*GRID_SIZE+CELL_SIZE,-5,CELL_SIZE*5,CELL_SIZE*GRID_SIZE+10);
				g.setColor(new Color(100,70,20));
				g.drawRect(CELL_SIZE*GRID_SIZE+CELL_SIZE,-5,CELL_SIZE*5,CELL_SIZE*GRID_SIZE+10);
				g.setStroke(new BasicStroke(1));

				g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);

				//g.setColor(new Color(0xcccccc));
				/*g.drawRect(0,0,GRID_SIZE*CELL_SIZE,GRID_SIZE*CELL_SIZE);
				for(int i=1; i<GRID_SIZE; i++) {
						g.drawLine(i*CELL_SIZE,0,i*CELL_SIZE,GRID_SIZE*CELL_SIZE);
						g.drawLine(0,i*CELL_SIZE,GRID_SIZE*CELL_SIZE,i*CELL_SIZE);
				}*/

				for(int i=0; i<GRID_SIZE; i++) {
					for(int j=0; j<GRID_SIZE; j++) {
						int cell = grid[i][j];

						if(!testing && mouse[3] && altered[i][j] && mouseX > i*CELL_SIZE+gridOffX && mouseX < i*CELL_SIZE+gridOffX+CELL_SIZE && mouseY > j*CELL_SIZE+gridOffY && mouseY < j*CELL_SIZE+gridOffY+CELL_SIZE) {
							//System.out.println("test");
							int type = grid[i][j];
							grid[i][j] = 0;
							for(int[] tilestack : tileset) {
								if(tilestack[0] == type) {
									tilestack[1]++;
								}
							}
							altered[i][j] = false;
						}
						/*if(cell >= 0) {
							int cellswitch = cell & 0xff;
							switch (cellswitch) {
								case 0x01:
									g.setColor(Color.green);
									g.fillRect(i*CELL_SIZE,j*CELL_SIZE,CELL_SIZE,CELL_SIZE);
									break;
								case 0x11:
									g.setColor(Color.green);
									g.fillRect(i*CELL_SIZE,j*CELL_SIZE,CELL_SIZE,CELL_SIZE);
									break;
								case 0x02:
									g.setColor(Color.BLUE);
									g.fillRect(i*CELL_SIZE,j*CELL_SIZE,CELL_SIZE,CELL_SIZE);
									break;
								case 0x12:
									g.setColor(Color.BLUE);
									g.fillRect(i*CELL_SIZE,j*CELL_SIZE,CELL_SIZE,CELL_SIZE);
									break;
							}
						}*/
						/*switch(cell) {
							case GREEN_EXIT1:
							case GREEN_ENTRY1:
								g.setColor(Color.green);
								rotation = 0;
								break;
							case:
						}*/
						g.translate(i*CELL_SIZE,j*CELL_SIZE);

						if(cell < 0) {
							double rotation = Math.PI/2*((-(cell+1))%4);
							/*int color = (-cell)/9;
							switch(color) {
								case 0:
									g.setColor(new Color(0x007700));
									break;
								case 1:
									g.setColor(new Color(0x003377));
									break;
							}*/
							g.setColor(new Color(-cell/9 == 0 ? 0x007700 : 0x003377));
							/*g.setColor(new Color(0x007700));
							if(-cell/9 == 1) {
								g.setColor(new Color(0x003377));
							}*/
							g.fillRect(0,0,CELL_SIZE,CELL_SIZE);
							g.setColor(new Color(0x000000));
							g.drawRect(0,0,CELL_SIZE,CELL_SIZE);
							g.translate(CELL_SIZE/2,CELL_SIZE/2);
							g.rotate(rotation);
							g.setColor(new Color(0xffffff));
							g.fill(arrow);
							g.rotate(-rotation);
							g.translate(-CELL_SIZE/2,-CELL_SIZE/2);
						} else if(cell >= BOOST1 && cell <= BOOST4) {
							double rotation = Math.PI/2*((cell-1)%4);
							g.setColor(new Color(0x7799bb));
							g.fillRect(0,0,CELL_SIZE,CELL_SIZE);
							g.setColor(new Color(0x000000));
							g.drawRect(0,0,CELL_SIZE,CELL_SIZE);
							g.translate(CELL_SIZE/2,CELL_SIZE/2);
							g.rotate(rotation);
							g.translate(0,-5);
							g.setColor(new Color(0xffffff));
							g.fill(arrow);
							g.fillRect(-4,0,8,15);
							g.translate(0,5);
							g.rotate(-rotation);
							g.translate(-CELL_SIZE/2,-CELL_SIZE/2);

						} else if(cell == ROAD) {
							g.setColor(new Color(0xaaaaaa));
							g.fillRect(0,0,CELL_SIZE,CELL_SIZE);

							/*boolean nL = false;
							boolean nR = false;
							boolean nU = false;
							boolean nD = false;*/

							if(i-1 >= 0 && grid[i-1][j] == 0) { //Has empty neighbour left
								//nL = true;
								g.setColor(new Color(0xffffff));
								g.drawLine(5,5,5,CELL_SIZE/2-5);
								g.drawLine(5,CELL_SIZE/2+5,5,CELL_SIZE-5);
								g.setColor(new Color(0x000000));
								g.drawLine(0,0,0,CELL_SIZE);
							}
							if(i+1 < GRID_SIZE && grid[i+1][j] == 0) { //Has empty neighbour right
								//nR = true;
								g.setColor(new Color(0xffffff));
								g.drawLine(CELL_SIZE-5,5,CELL_SIZE-5,CELL_SIZE/2-5);
								g.drawLine(CELL_SIZE-5,CELL_SIZE/2+5,CELL_SIZE-5,CELL_SIZE-5);
								g.setColor(new Color(0x000000));
								g.drawLine(CELL_SIZE,0,CELL_SIZE,CELL_SIZE);
							}
							if(j-1 >= 0 && grid[i][j-1] == 0) { //Has empty neighbour up
								//nU = true;
								g.setColor(new Color(0xffffff));
								g.drawLine(5,5,CELL_SIZE/2-5,5);
								g.drawLine(CELL_SIZE/2+5,5,CELL_SIZE-5,5);
								g.setColor(new Color(0x000000));
								g.drawLine(0,0,CELL_SIZE,0);
							}
							if(j+1 < GRID_SIZE && grid[i][j+1] == 0) { //Has empty neighbour down
								//nD = true;
								g.setColor(new Color(0xffffff));
								g.drawLine(5,CELL_SIZE-5,CELL_SIZE/2-5,CELL_SIZE-5);
								g.drawLine(CELL_SIZE/2+5,CELL_SIZE-5,CELL_SIZE-5,CELL_SIZE-5);
								g.setColor(new Color(0x000000));
								g.drawLine(0,CELL_SIZE,CELL_SIZE,CELL_SIZE);
							}

							/*if(nL) {
								g.setColor(new Color(0xffffff));
								g.drawLine(5,5,5,CELL_SIZE/2-5);
								g.drawLine(5,CELL_SIZE/2+5,5,CELL_SIZE-5);
								g.setColor(new Color(0x000000));
								g.drawLine(0,0,0,CELL_SIZE);
							}
							if(nR) {
								g.setColor(new Color(0xffffff));
								g.drawLine(CELL_SIZE-5,5,CELL_SIZE-5,CELL_SIZE/2-5);
								g.drawLine(CELL_SIZE-5,CELL_SIZE/2+5,CELL_SIZE-5,CELL_SIZE-5);
								g.setColor(new Color(0x000000));
								g.drawLine(CELL_SIZE,0,CELL_SIZE,CELL_SIZE);
							}
							if(nU) {
								g.setColor(new Color(0xffffff));
								g.drawLine(5,5,CELL_SIZE/2-5,5);
								g.drawLine(CELL_SIZE/2+5,5,CELL_SIZE-5,5);
								g.setColor(new Color(0x000000));
								g.drawLine(0,0,CELL_SIZE,0);
							}
							if(nD) {
								g.setColor(new Color(0xffffff));
								g.drawLine(5,CELL_SIZE-5,CELL_SIZE/2-5,CELL_SIZE-5);
								g.drawLine(CELL_SIZE/2+5,CELL_SIZE-5,CELL_SIZE-5,CELL_SIZE-5);
								g.setColor(new Color(0x000000));
								g.drawLine(0,CELL_SIZE,CELL_SIZE,CELL_SIZE);
							}*/
							/*boolean neighborsUD = false;
							boolean neighborsLR = false;
							if((i+1 < GRID_SIZE && grid[i+1][j] != 0) || i-1 >= 0 && grid[i-1][j] != 0) { //Non-empty neighbors left-right
								neighborsLR = true;
							}
							if((j+1 < GRID_SIZE && grid[i][j+1] != 0) || j-1 >= 0 && grid[i][j-1] != 0) { //Non-empty neighbors up-down
								neighborsUD = true;
							}
							g.setColor(new Color(0xffffff));
							if(neighborsUD) {
								g.drawLine(5,0,5,CELL_SIZE);
								g.drawLine(CELL_SIZE-5,0,CELL_SIZE-5,CELL_SIZE);
							}
							if(neighborsLR) {
								g.drawLine(0,5,CELL_SIZE,5);
								g.drawLine(0,CELL_SIZE-5,CELL_SIZE,CELL_SIZE-5);
							}*/
						} else if(cell == WALL) {
							g.setColor(new Color(0x666666));
							g.fillRect(0,0,CELL_SIZE,CELL_SIZE);
							g.setColor(new Color(0x000000));
							g.drawRect(0,0,CELL_SIZE,CELL_SIZE);
						}

						g.translate(-i*CELL_SIZE,-j*CELL_SIZE);
						//System.out.println(Arrays.toString(spawners.get(0)));

					}
				}

				if(testing) {
					int wincount = 0;

					for(int[] unit : units) {
						if(unit[3] == -1) {
							wincount++;
						}

						//unit[4] = fcount%60;

						boolean translate = false;
						boolean road = false;

						int dx = 0;
						int dy = 0;

						if(unit[3] == 0)
							dy--;
						if(unit[3] == 1)
							dx++;
						if(unit[3] == 2)
							dy++;
						if(unit[3] == 3)
							dx--;

						if(unit[0]+dx >= 0 && unit[0]+dx < GRID_SIZE && unit[1]+dy >= 0 && unit[1]+dy < GRID_SIZE) {
							road = grid[unit[0]+dx][unit[1]+dy] == ROAD;
						}

						if(unit[0] >= 0 && unit[0] < GRID_SIZE && unit[1] >= 0 && unit[1] < GRID_SIZE) {
							int cell = grid[unit[0]][unit[1]];
							if(cell >= BOOST1 && cell <= BOOST4) {
								unit[3] = (cell-1)%4;
								dx = dy = 0;
								if(unit[3] == 0)
									dy--;
								if(unit[3] == 1)
									dx++;
								if(unit[3] == 2)
									dy++;
								if(unit[3] == 3)
									dx--;
							}
							if(unit[0]+dx >= 0 && unit[0]+dx < GRID_SIZE && unit[1]+dy >= 0 && unit[1]+dy < GRID_SIZE) {
								while(grid[unit[0] + dx][unit[1] + dy] == WALL) {	//Obstacle ahead!
									unit[3] = (++unit[3])%4;						//Turn clockwise
									dx = dy = 0;
									if(unit[3] == 0)
										dy--;
									if(unit[3] == 1)
										dx++;
									if(unit[3] == 2)
										dy++;
									if(unit[3] == 3)
										dx--;
								}
							}
						}

						if(unit[4]%60 == 0 || (unit[4]%30 == 0 && road)) {
							if(unit[2] == -1) {
								unit[3] = -1;
							}
							unit[0] += dx;
							unit[1] += dy;
							if(unit[0]+dx >= 0 && unit[0]+dx < GRID_SIZE && unit[1]+dy >= 0 && unit[1]+dy < GRID_SIZE) {
								while(grid[unit[0] + dx][unit[1] + dy] == WALL) {	//Obstacle ahead!
									unit[3] = (++unit[3])%4;						//Turn clockwise
									dx = dy = 0;
									if(unit[3] == 0)
										dy--;
									if(unit[3] == 1)
										dx++;
									if(unit[3] == 2)
										dy++;
									if(unit[3] == 3)
										dx--;
								}
							}
							unit[4] = 0;
						} else {
							translate = true;
						}
						/*if(fcount % 60 == 0) {
							if(unit[3] == 0)
								unit[1]--;
							if(unit[3] == 1)
								unit[0]++;
							if(unit[3] == 2)
								unit[1]++;
							if(unit[3] == 3)
								unit[0]--;
						} else {
							g.translate(dx*CELL_SIZE,dy*CELL_SIZE);
						}*/
						switch(unit[2]) {
							case -1:
								g.setColor(new Color(0xcccccc));
								break;
							case 0:
								g.setColor(new Color(0x00cc00));
								break;
							case 1:
								g.setColor(new Color(0x2299cc));
								break;
						}
						g.setClip(0,0,CELL_SIZE*GRID_SIZE,CELL_SIZE*GRID_SIZE);

						//if(unit[0] >= 0 && unit[0] < GRID_SIZE && unit[1] >= 0 && unit[1] < GRID_SIZE) {
						double t = (road?unit[4]/30.0:unit[4]/60.0);
						if(translate)
							g.translate((int)(dx*CELL_SIZE*t),(int)(dy*CELL_SIZE*t));
						//Color c = g.getColor();

						double rotation = Math.PI/2*((unit[3]-1)%4);
						g.translate(unit[0]*CELL_SIZE+CELL_SIZE/2,unit[1]*CELL_SIZE+CELL_SIZE/2);
						g.rotate(rotation);

						g.fillRoundRect(-10,-6,20,12,4,4);
						g.setColor(g.getColor().darker());
						g.fillRoundRect(-5,-6,8,12,4,4);
						g.setColor(new Color(0x000000));
						g.drawRoundRect(-10,-6,20,12,4,4);
						g.drawRoundRect(-5,-6,8,12,4,4);

						g.rotate(-rotation);
						g.translate(-(unit[0]*CELL_SIZE+CELL_SIZE/2),-(unit[1]*CELL_SIZE+CELL_SIZE/2));

						/*g.fillOval(unit[0]*CELL_SIZE+7,unit[1]*CELL_SIZE+7,CELL_SIZE-14,CELL_SIZE-14);
						g.setColor(new Color(0x000000));
						g.drawOval(unit[0]*CELL_SIZE+7,unit[1]*CELL_SIZE+7,CELL_SIZE-14,CELL_SIZE-14);*/
						if(translate)
							g.translate(-(int)(dx*CELL_SIZE*t),-(int)(dy*CELL_SIZE*t));
						//}

						if(unit[0] >= 0 && unit[0] < GRID_SIZE && unit[1] >= 0 && unit[1] < GRID_SIZE) {
							int cell = grid[unit[0]][unit[1]];
							int color = (-cell)/9;
							if(cell < 0 && (-(cell+1-4)/4)%2 == 0 && color == unit[2]) {
								//System.out.println("reachedgoal");
								unit[2] = -1;
								unit[3] = ((-(cell+1))%4);
							}
						}


						unit[4]++;//(++unit[4])%60;

					}
					if(wincount == units.size() && units.size() > 0) {
						justChanged = true;
						level = (++level)%LEVEL_COUNT;
						running = false;
						//System.out.println("Win!");
					}
					for(int[] unit : units) {
						for(int[] u2 : units) {
							if(u2 != unit && unit[0] == u2[0] && unit[1] == u2[1] && u2[3] != -1) {
								running = false;
								//System.out.println("Collision!");
							}
						}
					}

					for(int[] s : spawners) {

						if(fcount % 60 == 0) {
							boolean spawn = ((time-s[7]) % (s[4]+s[5])) < s[4] && (time-s[7]) < (s[4]+s[5])*s[6]; //Spawn unit or not?
							if(spawn) {
								int[] unit = {s[0],s[1],s[2],s[3],1};
								if(unit[3] == 0)
									unit[1]++;
								if(unit[3] == 1)
									unit[0]--;
								if(unit[3] == 2)
									unit[1]--;
								if(unit[3] == 3)
									unit[0]++;
								units.add(unit);
							}
							/*if(spawn) {
								System.out.println("Spawn!");
							} else {
								System.out.println("Nospawn!");
							}*/
						}
					}

					/*double rotation = Math.PI/2*s[3];
					switch(c[3]) {
						case 0:
							g.setColor(new Color(0x007700));
							break;
						case 1:
							g.setColor(new Color(0x770000));
							break;
					}
					g.translate(i*CELL_SIZE,j*CELL_SIZE);
					g.fillRect(0,0,CELL_SIZE,CELL_SIZE);
					g.translate(CELL_SIZE/2,CELL_SIZE/2);
					g.rotate(rotation);
					g.setColor(new Color(0xffffff));
					g.fill(arrow);
					g.rotate(-rotation);
					g.translate(-CELL_SIZE/2,-CELL_SIZE/2);
					g.translate(-i*CELL_SIZE,-j*CELL_SIZE);*/
				}

				g.setClip(-gridOffX,-gridOffY,WIDTH,HEIGHT);

				int counter = 0;
				for(int[] tilestack : tileset) {
					int x = CELL_SIZE*GRID_SIZE + 54 + 40*(counter%3);
					int y = 30+60*(counter/3);
					g.translate(x,y);
					//for(int i=0; i<2; i++) {
						/*if(i == 1 && heldStack != tilestack) {
							continue;
						} else if(i == 1) {
							g.translate(-40*(counter%3),CELL_SIZE*(GRID_SIZE-2)-15-60*(counter/3));
						}*/
						g.setFont(font);
						String str = ""+tilestack[1];

						if(tilestack[0] == ROAD) {
							g.setColor(new Color(0xaaaaaa));
							g.fillRect(0,0,CELL_SIZE,CELL_SIZE);
							//g.setColor(new Color(0x000000));
							//g.setFont(font);
							//g.drawString(str,17-str.length()*5,22);
							//g.drawRect(0,0,CELL_SIZE,CELL_SIZE);
						}
						if(tilestack[0] == WALL) {
							g.setColor(new Color(0x666666));
							g.fillRect(0,0,CELL_SIZE,CELL_SIZE);
							//g.setColor(new Color(0x000000));
							//g.drawString(str,17-str.length()*5,22);
							//g.drawRect(0,0,CELL_SIZE,CELL_SIZE);
						}
						if(tilestack[0] >= BOOST1 && tilestack[0] <= BOOST4) {
							double rotation = Math.PI/2*((tilestack[0]-1)%4);
							g.setColor(new Color(0x7799bb));
							g.fillRect(0,0,CELL_SIZE,CELL_SIZE);
							g.setColor(new Color(0x000000));
							g.drawRect(0,0,CELL_SIZE,CELL_SIZE);
							g.translate(CELL_SIZE/2,CELL_SIZE/2);
							g.rotate(rotation);
							g.translate(0,-5);
							g.setColor(new Color(0xffffff));
							g.fill(arrow);
							g.fillRect(-4,0,8,15);
							g.translate(0,5);
							g.rotate(-rotation);
							g.translate(-CELL_SIZE/2,-CELL_SIZE/2);

						}
						g.setColor(new Color(0x000000));
						g.drawString(str,17-str.length()*5,-6);
						g.drawRect(0,0,CELL_SIZE,CELL_SIZE);
						if(heldStack == tilestack) {
							g.drawRect(-3,-3,CELL_SIZE+6,CELL_SIZE+6);
						}
						/*if(i == 1) {
							g.translate(40*(counter%3),-(CELL_SIZE*(GRID_SIZE-2)-15-60*(counter/3)));
						}*/
					//}
					g.translate(-x,-y);
					if(mouse[1] && mouseX > x+gridOffX && mouseX < x+gridOffX+CELL_SIZE && mouseY > y+gridOffY && mouseY < y+gridOffY+CELL_SIZE) {
						mouse[1] = false;
						if(tilestack[1] > 0) {
							heldStack = tilestack;
						}
					}
					g.setColor(new Color(100,70,20));
					g.fillRect(458,304,CELL_SIZE*2,CELL_SIZE);
					g.setColor(new Color(0x000000));
					g.drawRect(458,304,CELL_SIZE*2,CELL_SIZE);
					g.setColor(new Color(0xffffff));
					if(!testing)
						g.drawString("Go!",477,326);
					else
						g.drawString("Stop",473,326);
					counter++;
				}

				g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);

				g.setColor(new Color(100,70,20));
				g.setStroke(new BasicStroke(12));
				g.drawRect(-5,-5,CELL_SIZE*GRID_SIZE+10,CELL_SIZE*GRID_SIZE+10);
				//g.setStroke(new BasicStroke(1));

				g.setColor(new Color(0xccba89));
				g.drawString(""+(level+1),-32,20);

				g.translate(-gridOffX,-gridOffY);

				if(mouse[1] && mouseX > 458+gridOffX && mouseX < 458+CELL_SIZE*2+gridOffX && mouseY > 304+gridOffY && mouseY < 304+CELL_SIZE+gridOffY) {
					mouse[1] = false;
					if(!testing)
						testing = true;
					else {
						testing = false;
						running = false;
					}
				}


				if(testing && fcount % 60 == 0) {
					time++;
				}
				mouse[1] = false;
				mouse[3] = false;

				/*
				* Draw the off buffer to the frame's graphics context.
				*/
				gFrame.drawImage(off,0,0,null);

				g.dispose();
				gFrame.dispose();

				/*
				* Loop time and timing logic.
				*/
				long timeNow = System.currentTimeMillis();
				//rolling average for the last 20 time inputs
				frameAverage = (frameAverage * 20 + (timeNow - lastFrame)) / 21;
				lastFrame=timeNow;
				//0.1f = damping value
				//and that +0.05f bit is for ensuring that it can grow faster after it ran flat out for a while
				yield+=yield*(((float)FRAME_TIME/frameAverage)-1)*0.1f+0.05f;
				for(int i=0;i<yield;i++)
					Thread.yield();

			}

		}
	}

	/*
	* Handle window closing events and key/mouse events.
	*/
    public void processEvent(AWTEvent e)
    {
        switch (e.getID())
        {
            case WindowEvent.WINDOW_CLOSING:
				System.exit(0);
				break;
            case KeyEvent.KEY_PRESSED:
				keys[((KeyEvent)e).getKeyCode()] = true;
            	int k = ((KeyEvent)e).getKeyCode();
				if(k == KeyEvent.VK_Q) {
					level = (++level)%LEVEL_COUNT;
					running = false;
				}
				if(k == KeyEvent.VK_ESCAPE) {
					System.exit(0);
				}
				if(k == KeyEvent.VK_R) {
					running = false;
				}
				if(k == KeyEvent.VK_ENTER) {
					testing = true;
				}
				break;
            case KeyEvent.KEY_RELEASED:
				keys[((KeyEvent)e).getKeyCode()] = false;
				break;
			case MouseEvent.MOUSE_MOVED:
			case MouseEvent.MOUSE_DRAGGED:
				mouseX = ((MouseEvent)e).getX();
				mouseY = ((MouseEvent)e).getY();
				break;
			case MouseEvent.MOUSE_PRESSED:
				mouse[((MouseEvent)e).getButton()] = true;
				break;
			case MouseEvent.MOUSE_RELEASED:
				//mouse[((MouseEvent)e).getButton()] = false;
				break;
        }
    }

}