using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System;
using System.Text;
using System.Threading;


public partial class CityGen : MonoBehaviour {

    // The delay in milliseconds between board updates.
    //private const int DELAY = 50;

    // True if cell rules can loop around edges.
    private bool loopEdges = true;


    public void simulate()
    {
        // Use with predefined seed.
        this.city.genDemo();

        // Run simulation
        for(int i = 0; i < 5; i++) {
            this.updateBoard();
        }

        // Add city to the game
        this.drawBoard();
    }

    // Draws the board to game
    private void drawBoard()
    {
        string logBoard = "";
    	for(int i = 0; i < this.city.layoutDimension.y; i++) {
    		for(int j = 0; j < this.city.layoutDimension.x; j++) {
				logBoard += this.city.layout[j,i].development + this.city.layout[j,i].zoneName + " ";
                Prefab prefab = city.findPrefabById(this.city.layout[j,i].development);
				if(prefab != null && prefab.prefab != null){
					//print(prefab.identifier);
					Vector3 position = new Vector3( transform.position.x + j * prefab.footprint.x,
													transform.position.y, 
													transform.position.z + i * prefab.footprint.y);
					GameObject go = Instantiate(prefab.prefab, position, Quaternion.identity);
					go.transform.SetParent(this.transform); 
				}
			}
            print(logBoard);
            logBoard = "";
		}	
    }

    // Moves the board to the next state based on Conway's rules.
    private void updateBoard()
    {
/*
        string output = "";
        for(int i = 0; i < this.city.layoutDimension.y; i++ ){
            for(int j = 0; j < this.city.layoutDimension.x; j++ ){
                output += this.city.layout[j,i] + " "; 
            }
            print(output);
            output = "";
        }*/

        // A temp variable to hold the next state while it's being calculated.
        Tile[,] newBoard = new Tile[(int)this.city.layoutDimension.x, (int)this.city.layoutDimension.y];

        for (var y = 0; y < this.city.layoutDimension.y; y++) {
            for (var x = 0; x < this.city.layoutDimension.x; x++) {
                var n = updateTile(x, y);
 
                //store the updated value in the temp board.
                newBoard[x, y] = n;
            }
        }

        // Set the board to its new state.
        this.city.layout = newBoard;
/*
        for(int i = 0; i < this.city.layoutDimension.y; i++ ){
            for(int j = 0; j < this.city.layoutDimension.x; j++ ){
                output += this.city.layout[j,i] + " "; 
            }
            print(output);
            output = "";
        }*/
    }

    //updates the type and development of tile at (x,y)
    private Tile updateTile(int x, int y)
    {
        // The value indicating type and development of current tile.
        Vector3 value = getTileValueVector(x, y);

        List<Vector3> effects = new List<Vector3>();

        // This nested loop enumerates the 9 cells in the specified cells neighborhood.
        for (var j = -1; j <= 1; j++) {
            // If loopEdges is set to false and y+j is off the board, continue.
            if (!this.loopEdges && y + j < 0 || y + j >= (int)this.city.layoutDimension.y) {
                continue;
            }

            // Loop around the edges if y+j is off the board.
            int k = (y + j + (int)this.city.layoutDimension.y) % (int)this.city.layoutDimension.y;
            
            for (var i = -1; i <= 1; i++) {
                //  If loopEdges is set to false and x+i is off the board, continue.
                if (!this.loopEdges && x + i < 0 || x + i >= (int)this.city.layoutDimension.x) {
                    continue;
                }

                // Loop around the edges if x+i is off the board.
                int h = (x + i + (int)this.city.layoutDimension.x) % (int)this.city.layoutDimension.x;

                // Count the neighbor cell at (h,k) if it is alive.
                ZoneType zoneType = this.city.findZoneByName(
                    this.city.layout[h, k].zoneName
                );
                //if(zoneType != null){
                effects.Add(zoneType.zoneRule(value));
                //}
            }
        }
        
        
        
        return this.city.layout[x, y].updateTile(
            this.standardize(
                this.grindDaEffects(effects)
            )
        );
    }

    // Get a Vector4 representing tile at (x,y) preference towards the three zoneTypes and development.
    public Vector3 getTileValueVector(int x, int y){
        string zoneName = this.city.layout[x, y].zoneName;

        ZoneType zoneType = this.city.findZoneByName(zoneName);

        if(zoneType.zoneName == "Empty"){
            return Vector3.zero;
        }
/*
        int development = zoneType.getDevelopmentIndex(
            this.city.layout[x, y].development
        ); */
        int development = this.city.layout[x,y].development;
        switch(zoneName){
            case "Recidential":
                return new Vector3(1.0f * development, 0.0f, 0.0f);

            case "Commercial":
                return new Vector3(0.0f, 1.0f * development, 0.0f);

            case "Industrial":
                return new Vector3(0.0f, 0.0f, 1.0f * development);
        }

        return Vector3.zero;
    }

    public Vector3 grindDaEffects(List<Vector3> effects){
        Vector3 result = new Vector3();

        foreach (Vector3 effect in effects)
            result += (effect + new Vector3(
                UnityEngine.Random.Range(0, 10), 
                UnityEngine.Random.Range(0, 10), 
                UnityEngine.Random.Range(0, 10)
            )
        );

        return result;
    }

    public Vector3 standardize(Vector3 vector){
        vector.Normalize();
        vector = vector * 3;

        if (Mathf.Min(vector.x, vector.y, vector.z) == 0.0f)
            return vector;

        float ratio = Mathf.Max(vector.x, vector.y, vector.z) / Mathf.Min(vector.x, vector.y, vector.z);

        return vector * ratio ;
    }
}

