package com.fundynamic.dune2themaker.game.map;

import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import com.fundynamic.dune2themaker.game.TerrainFactory;
import com.fundynamic.dune2themaker.game.terrain.Terrain;
import com.fundynamic.dune2themaker.game.terrain.TerrainFacing;

public class Map {

	private final TerrainFactory terrainFactory;
	private final int height, width;
	private Image mapImage;

	private boolean initialized;

	private Cell[][] cells;

	public Map(TerrainFactory terrainFactory, int width, int height) throws SlickException {
		this.terrainFactory = terrainFactory;
		this.height = height;
		this.width = width;
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	public Cell getCell(int x, int y) {
		return cells[x][y];
	}

	public Image getMapImage() throws SlickException {
		if (this.mapImage == null) {
			mapImage = new MapRenderer().render(this);
		}
		return mapImage;
	}

	public void init() throws SlickException {
		if (!initialized) {
			// this does not work when we move the code in the constructor?
			initializeEmptyMap(width, height);
			putTerrainOnMap();
            smooth();
            initialized = true;
		}
	}

	private void initializeEmptyMap(int width, int height) {
		this.cells = new Cell[width + 2][height + 2];
		for (int x = 0; x < this.width + 2; x++) {
			for (int y = 0; y < this.height + 2; y++) {
				cells[x][y] = new Cell(terrainFactory.createEmptyTerrain());
			}
		}
	}

	// @TODO: move this to a MapLoader / MapCreator / MapFactory / MapRepository
	private void putTerrainOnMap() {
		for (int x = 1; x <= this.width; x++) {
			for (int y = 1; y <= this.height; y++) {
				final Cell cell = cells[x][y];
				final Terrain terrain = terrainFactory.create((int) (Math.random() * 7), cell);
				cell.changeTerrain(terrain);
			}
		}
	}

	public void smooth() {
		for (int x = 1; x <= this.width; x++) {
            for (int y = 1; y <= this.height; y++) {
                final SquareCell cell = new SquareCell(x, y);
                final Terrain terrain = cell.getTerrain();
                terrain.setFacing(cell.determineFacing());
			}
		}
	}



    private class SquareCell {
        private final int x, y;

        private SquareCell(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public Terrain getTerrain() {
            return cells[x][y].getTerrain();
        }

        private TerrainFacing determineFacing() {
            Terrain terrain = getTerrain();
            FacingDeterminer facingDeterminer = new FacingDeterminer();
            final Cell topNeighbour = getCellTopNeighbour();
            final Cell rightNeighbour = getCellRightNeighbour();
            final Cell bottomNeighbour = getCellBottomNeighbour();
            final Cell leftNeighbour = getCellLeftNeighbour();
            facingDeterminer.setTopSame(terrain.isSame(topNeighbour.getTerrain()));
            facingDeterminer.setRightSame(terrain.isSame(rightNeighbour.getTerrain()));
            facingDeterminer.setBottomSame(terrain.isSame(bottomNeighbour.getTerrain()));
            facingDeterminer.setLeftSame(terrain.isSame(leftNeighbour.getTerrain()));
            return facingDeterminer.getFacing();
        }

        private Cell getCellLeftNeighbour() {
            return cells[x-1][y];
        }

        private Cell getCellBottomNeighbour() {
            return cells[x][y+1];
        }

        private Cell getCellRightNeighbour() {
            return cells[x+1][y];
        }

        private Cell getCellTopNeighbour() {
            return cells[x][y-1];
        }
    }



}

@@ -2257,20 +2257,16 @@
 %0D%0A%09%7D%0D%0A%0D%0A
-%0D%0A%0D%0A
     priv
@@ -2702,268 +2702,8 @@
 );%0D%0A
-            final Cell topNeighbour = getCellTopNeighbour();%0D%0A            final Cell rightNeighbour = getCellRightNeighbour();%0D%0A            final Cell bottomNeighbour = getCellBottomNeighbour();%0D%0A            final Cell leftNeighbour = getCellLeftNeighbour();%0D%0A
     
@@ -2757,31 +2757,30 @@
 ame(
-topNeighbour.getTerrain
+getTerrainTopNeighbour
 ()))
@@ -2839,17 +2839,27 @@
 .isSame(
-r
+getTerrainR
 ightNeig
@@ -2859,35 +2859,24 @@
 ghtNeighbour
-.getTerrain
 ()));%0D%0A     
@@ -2928,17 +2928,27 @@
 .isSame(
-b
+getTerrainB
 ottomNei
@@ -2949,35 +2949,24 @@
 tomNeighbour
-.getTerrain
 ()));%0D%0A     
@@ -3016,17 +3016,27 @@
 .isSame(
-l
+getTerrainL
 eftNeigh
@@ -3039,27 +3039,16 @@
 eighbour
-.getTerrain
 ()));%0D%0A 
@@ -3137,16 +3137,114 @@
  getCell
+(int x, int y) %7B%0D%0A            return cells%5Bx%5D%5By%5D;%0D%0A        %7D%0D%0A%0D%0A        private Terrain getTerrain
 LeftNeig
@@ -3277,21 +3277,38 @@
 urn 
-cells%5Bx-1%5D%5By%5D
+getCell(x - 1, y).getTerrain()
 ;%0D%0A 
@@ -3335,28 +3335,34 @@
 private 
-Cell getCell
+Terrain getTerrain
 BottomNe
@@ -3397,21 +3397,38 @@
 urn 
-cells%5Bx%5D%5By+1%5D
+getCell(x, y + 1).getTerrain()
 ;%0D%0A 
@@ -3455,28 +3455,34 @@
 private 
-Cell getCell
+Terrain getTerrain
 RightNei
@@ -3516,21 +3516,38 @@
 urn 
-cells%5Bx+1%5D%5By%5D
+getCell(x + 1, y).getTerrain()
 ;%0D%0A 
@@ -3574,28 +3574,34 @@
 private 
-Cell getCell
+Terrain getTerrain
 TopNeigh
@@ -3633,21 +3633,38 @@
 urn 
-cells%5Bx%5D%5By-1%5D
+getCell(x, y - 1).getTerrain()
 ;%0D%0A 

use getCell method for fetching cell