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

public class BattleManager : MonoBehaviour
{

    public static BattleManager ms_instance;


    private const int BOARD_TEX_WIDTH = 1024;
    private const int BOARD_TEX_HEIGHT = 1024;

    private const int GRID_PIXEL_SIZE = 16;

    public static BattleManager Instance
    {
        get { return ms_instance; }
    }


    Texture2D m_boardTexture;
    GameObject m_boardObject;
    GameObject m_playerObject;

    private Color[] GetSolidColorArray( Color color, int size )
    {
        Color[] square = new Color[GRID_PIXEL_SIZE * GRID_PIXEL_SIZE];
        for (int i=0;i<size;i++ )
        {
            square[i] = color;
        }
        return square;
    }

    // Use this for initialization
    void Start()
    {
        if (ms_instance != null)
        {
            Debug.LogError("Only one battleman allowed");
        }
        ms_instance = this;

        m_boardTexture = new Texture2D(BOARD_TEX_WIDTH, BOARD_TEX_HEIGHT, TextureFormat.RGB24, false);
        
        new GridManager();
        GridManager.Instance.AddObject(PlayerObject.Instance.GetComponent<GridObject>());
        
        m_turnNumber = 1;
        m_playerTurn = true;

        m_boardObject = GameObject.Find("Board");
        m_playerObject = GameObject.Find("Player");
        
        m_playerObject.transform.localScale = new Vector3(GridManager.UNITY_GRID_SIZE, GridManager.UNITY_CHARACTER_DEPTH, GridManager.UNITY_GRID_SIZE);
        m_boardObject.transform.localScale = new Vector3(GridManager.MAP_SIZE_X * GridManager.UNITY_GRID_SIZE, GridManager.UNITY_BOARD_DEPTH, GridManager.MAP_SIZE_Y * GridManager.UNITY_GRID_SIZE);
        
        PlayerObject.Instance.SetupPlayerDefaults();
        PlayerObject.Instance.LoadFromPlayerPrefs();
    }
    
    public int m_turnNumber = 1;
    public bool m_playerTurn = true;

    public Texture2D m_grassTexture;
    public Texture2D m_dirtTexture;
    public Texture2D m_stoneTexture;

    public float Remap( float from, float to, float val )
    {
        float size = to / from;
        float v = val - from;
        return v / size;
    }

    public void GenerateBoard()
    {
        Random.seed = (PlayerObject.Instance.Info.m_battleNumber * 567);

        float textureScaleX = (GridManager.MAP_SIZE_X * GRID_PIXEL_SIZE) / (float)BOARD_TEX_WIDTH;
        float textureScaleY = (GridManager.MAP_SIZE_Y * GRID_PIXEL_SIZE) / (float)BOARD_TEX_HEIGHT;

        
        float[][] perlinNoise = PerlinNoise.GeneratePerlinNoise(GridManager.MAP_SIZE_X, GridManager.MAP_SIZE_Y, 5);
        
        for (int gridX = 0; gridX < GridManager.MAP_SIZE_X; gridX++)
        {
            for (int gridY = 0; gridY < GridManager.MAP_SIZE_Y; gridY++)
            {
                float perlinVal = perlinNoise[gridX][gridY];


                const float FIRST_DIV = 0.25f;
                const float SECOND_DIV = 0.5f;
                Texture2D chosenTex = m_dirtTexture;

                if ( perlinVal < 0.3f )
                {
                    perlinVal = Remap(0.0f, FIRST_DIV, perlinVal);
                }
                else if (perlinVal < SECOND_DIV)
                {
                    perlinVal = Remap(FIRST_DIV, SECOND_DIV, perlinVal);
                    chosenTex = m_grassTexture;
                }
                else
                {
                    perlinVal = Remap(SECOND_DIV, 1.0f, perlinVal);
                    chosenTex = m_stoneTexture;
                }

                //brighten
                   perlinVal *= 2.0f;

                {
                    // Checkerboard
                    if ((gridY % 2) == 0)
                    {
                        if ((gridX % 2) == 0)
                        {
                            perlinVal *= 1.05f;
                        }
                    }
                    else
                    {
                        if ((gridX % 2) != 0)
                        {
                            perlinVal *= 0.95f;
                        }
                    }

                }

                perlinVal = Mathf.Max(0.0f, perlinVal);
                perlinVal = Mathf.Min(1.0f, perlinVal);

                for (int pixelX = 0; pixelX < GRID_PIXEL_SIZE; pixelX++)
                {
                    for (int pixelY = 0; pixelY < GRID_PIXEL_SIZE; pixelY++)
                    {
                        int x = (gridX * GRID_PIXEL_SIZE) + pixelX;
                        int y = (gridY * GRID_PIXEL_SIZE) + pixelY;

                        Color clr = chosenTex.GetPixel(pixelX, pixelY);

                        clr.r *= perlinVal;
                        clr.g *= perlinVal;
                        clr.b *= perlinVal;

                        m_boardTexture.SetPixel(x, y, clr);

                    }
                }
            }
        }

        m_boardTexture.Apply();

        m_boardObject.GetComponent<Renderer>().material.mainTexture = m_boardTexture;
        m_boardObject.GetComponent<Renderer>().material.mainTextureScale = new Vector2(textureScaleX, textureScaleY);
    }

    public void StartBattle()
    {
        SetupNewBattle();
    }

    public void SetupNewBattle()
    {
        GenerateBoard();

        EnemyManager.Instance.m_iterations = 0;//HACK, like, so many others
        GridCoord pos = EnemyManager.Instance.RandGridPos(25, 0);


        PlayerObject.Instance.GetComponent<GridObject>().UpdatePosition(pos, true);

        EnemyManager.Instance.AddRandomEnemies();

        m_turnNumber = 1;
        m_playerTurn = true;

        // Start pos
        CameraPan.Instance.SnapToPlayer();
        TurnManager.Instance.SetupNewPlayerTurn();

        VoiceManager.Instance.Play(1) ;
    }

    public void BattleOver( bool won )
    {
        MenuSystem.Instance.SetState(MenuSystem.State.PostBattle);
        MenuSystem.Instance.m_postBattleWon = (won);
        PlayerObject.Instance.Info.ResetHPMP();
        EnemyManager.Instance.KillAllEnemies();
        RangeHighlighter.ClearHighlights();

        if ( won )
        {
            VoiceManager.Instance.Play(2);
            PlayerObject.Instance.Info.m_battleNumber++;
        }
        else
        {
            VoiceManager.Instance.Play(0);
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (MenuSystem.InBattle == false )
        {
            return;
        }
        if ( TurnManager.Instance.TurnFinished() )
        {
            if ( m_playerTurn )
            {
                if ( TurnManager.Instance.SetupNewEnemyTurn() == false )
                {
                    // Won battle!
                    SFXManager.Play(SFXManager.Instance.m_winBattle);
                    BattleOver(true);
                    return;
                }
            }
            else
            {
                if ( PlayerObject.Instance.Info.IsDead )
                {
                    // Lost battle. :(
                    SFXManager.Play(SFXManager.Instance.m_loseBattle);
                    BattleOver(false);
                    return;
                }

                m_turnNumber++;
                TurnManager.Instance.SetupNewPlayerTurn();
            }

            m_playerTurn = !m_playerTurn;
        }
    }

}




    class PerlinNoise
    {


        public static float[][] GenerateWhiteNoise(int width, int height)
        {            
            float[][] noise = GetEmptyArray<float>(width, height);

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    noise[i][j] = (float)Random.Range(0.0f,1.0f) % 1;
                }
            }

            return noise;
        }

        public static float Interpolate(float x0, float x1, float alpha)
        {
            return x0 * (1 - alpha) + alpha * x1;
        }



        public static T[][] GetEmptyArray<T>(int width, int height)
        {
            T[][] image = new T[width][];

            for (int i = 0; i < width; i++)
            {
                image[i] = new T[height];
            }

            return image;
        }

        public static float[][] GenerateSmoothNoise(float[][] baseNoise, int octave)
        {
            int width = baseNoise.Length;
            int height = baseNoise[0].Length;

            float[][] smoothNoise = GetEmptyArray<float>(width, height);

            int samplePeriod = 1 << octave; // calculates 2 ^ k
            float sampleFrequency = 1.0f / samplePeriod;

            for (int i = 0; i < width; i++)
            {
                //calculate the horizontal sampling indices
                int sample_i0 = (i / samplePeriod) * samplePeriod;
                int sample_i1 = (sample_i0 + samplePeriod) % width; //wrap around
                float horizontal_blend = (i - sample_i0) * sampleFrequency;

                for (int j = 0; j < height; j++)
                {
                    //calculate the vertical sampling indices
                    int sample_j0 = (j / samplePeriod) * samplePeriod;
                    int sample_j1 = (sample_j0 + samplePeriod) % height; //wrap around
                    float vertical_blend = (j - sample_j0) * sampleFrequency;

                    //blend the top two corners
                    float top = Interpolate(baseNoise[sample_i0][sample_j0],
                        baseNoise[sample_i1][sample_j0], horizontal_blend);

                    //blend the bottom two corners
                    float bottom = Interpolate(baseNoise[sample_i0][sample_j1],
                        baseNoise[sample_i1][sample_j1], horizontal_blend);

                    //final blend
                    smoothNoise[i][j] = Interpolate(top, bottom, vertical_blend);                    
                }
            }
            
            return smoothNoise;
        }

        public static float[][] GeneratePerlinNoise(float[][] baseNoise, int octaveCount)
        {
            int width = baseNoise.Length;
            int height = baseNoise[0].Length;

            float[][][] smoothNoise = new float[octaveCount][][]; //an array of 2D arrays containing

            float persistance = 0.7f;

            //generate smooth noise
            for (int i = 0; i < octaveCount; i++)
            {
                smoothNoise[i] = GenerateSmoothNoise(baseNoise, i);
            }

            float[][] perlinNoise = GetEmptyArray<float>(width, height); //an array of floats initialised to 0

            float amplitude = 1.0f;
            float totalAmplitude = 0.0f;
            
            //blend noise together
            for (int octave = octaveCount - 1; octave >= 0; octave--)
            {
                amplitude *= persistance;
                totalAmplitude += amplitude;

                for (int i = 0; i < width; i++)
                {
                    for (int j = 0; j < height; j++)
                    {
                        perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
                    }
                }
            }

            //normalisation
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    perlinNoise[i][j] /= totalAmplitude;
                }
            }        

            return perlinNoise;
        }

        public static float[][] GeneratePerlinNoise(int width, int height, int octaveCount)
        {
            float[][] baseNoise = GenerateWhiteNoise(width, height);

            return GeneratePerlinNoise(baseNoise, octaveCount);
        }

    }



