package com.codegym.games.game2048;

import com.codegym.engine.cell.*;

import java.util.ArrayList;
import java.util.Arrays;

public class Game2048 extends Game {

    //variables for class
    private static final int SIDE = 4; //size of screen
    private int[][] gameField = new int[SIDE][SIDE]; //gives each part of grid a location
    private boolean isGameStopped = false;

    //initializes game screen and begins game
    @Override
    public void initialize()
    {
        setScreenSize(SIDE, SIDE); //size of game
        createGame();
        drawScene(); //creates grid
    }

    private void createGame()
    {
        createNewNumber();//initial two numbers put in grid at start of game
        createNewNumber();


    }

    //sets game board and puts numbers in grid.
    private void drawScene()
    {

        for (int x = 0; x<SIDE; x++)
            for (int y = 0; y<SIDE; y++)
                setCellColoredNumber(x, y, gameField[y][x]); //prints number in grid

    }

    //creates a number, twice at beginning and each time player slides across grid and also finds the value of the max int on grid
    private void createNewNumber()
    {

            int x = getRandomNumber(SIDE); //initializing blank coordinates
            int y = getRandomNumber(SIDE);
            int z = getRandomNumber(10);
            //Sets a value to an empty coordinate within the grid at a 90% chance of 2 and 10% chance of a 4
            if (gameField[x][y] == 0) {
                if (z == 9)
                    gameField[x][y] = 4;
                else
                    gameField[x][y] = 2;
            } else createNewNumber();

        if (getMaxTileValue() == 2048)
            win();




    }

    //denotes color of each cell dependant on number value
    private Color getColorByValue(int value)
    {
        if (value == 0)
            return Color.ANTIQUEWHITE;
        else if (value == 2)
            return Color.LIGHTPINK;
        else if (value == 4)
            return Color.PURPLE;
        else if (value == 8)
            return Color.BLUE;
        else if (value == 16)
            return Color.LIGHTSEAGREEN;
        else if (value == 32)
            return Color.GREEN;
        else if (value == 64)
            return Color.LIGHTGREEN;
        else if (value == 128)
            return Color.ORANGE;
        else if (value == 256)
            return Color.CORAL;
        else if (value == 512)
            return Color.RED;
        else if (value == 1024)
            return Color.PINK;
        else
            return Color.DEEPPINK;

    }

    //sets color of cells based apon getColorByValue method
    private void setCellColoredNumber(int x, int y, int value)
    {
        if (value != 0)
            setCellValueEx(x, y, getColorByValue(value), Integer.toString(value) );
        else
            setCellValueEx(x, y, getColorByValue(value), "" );

    }
    //this method takes a row from the grid and then sorts empty fields to right and moves other
    //numbers to left without changing there order. This is only the beginning
    private boolean compressRow(int[] row) {
        boolean isChanged = false;

        for (int i = 0; i < row.length; i++)
        {
                for (int j = 1; j < row.length; j++)
                {
                    if (row[j-1] == 0)
                    {
                        if(row[j] != 0)
                         isChanged = true;
                         row[j-1] = row[j];
                         row[j] = 0; // had to use this way because creating temp array did not fit task conditions
                    }
                }

        }

        return isChanged; //variable used to discover whether the row was changed or not.
    }

    //puts adjoining cells of the same value together
    private boolean mergeRow(int[] row)
    {
       boolean isChanged = false;
       //int[] rowChange = new int[row.length];

           for(int j = 1; j<row.length; j++)
               if(row[j] == row[j-1] && row[j] != 0)
               {
                   isChanged = true; //changing variable
                   row[j - 1] = row[j] + row[j - 1];
                   row[j] = 0; //where there is a gap from combined numbers a zero is inuptted
               }
      return  isChanged;
    }

    @Override //pre-built method for keyboard input, we use it to "shift" our grid around
    public void onKeyPress(Key key)
    {
        if (key == Key.LEFT)
        {
            moveLeft();
            drawScene();
        }

        else if (key == Key.RIGHT)
        {
            moveRight();
            drawScene();
        }
        else if (key == Key.UP)
        {
            moveUp();
            drawScene();
        }
        else if (key == Key.DOWN)
        {
            moveDown();
            drawScene();
        }

    }

    //this method compresses and merges numbers if possible, checking each with a secondary boolean result and if either true creates a new number.
    private void moveLeft()
    {
        boolean compressed = false;
    boolean merged = false;
    boolean compressed2 = false;
    int move = 0; //counting integer to check movement on screen

        for (int i = 0; i < SIDE; i++)
        {

                compressed = compressRow(gameField[i]); //makes boolean results true
                merged = mergeRow(gameField[i]);
                compressed2 = compressRow(gameField[i]);

                if (compressed || merged || compressed2)//if either action returned true move + 1
                    move ++;
            }

            if (move != 0)
                createNewNumber();

        }


    private void moveRight()
    {
        boolean compressed = false;
        boolean merged = false;
        boolean compressed2 = false;
        int move = 0; //counting integer to check movement on screen
        rotateClockwise(); //As we have already set up the left move, by rotating the array we can
        rotateClockwise(); // implement the "slide-to-the-left" method

        for (int i = 0; i < SIDE; i++)
        {

            compressed = compressRow(gameField[i]); //makes boolean results true
            merged = mergeRow(gameField[i]);
            compressed2 = compressRow(gameField[i]);

            if (compressed || merged || compressed2)//if either action returned true move + 1
                move ++;
        }

        if (move != 0)
            createNewNumber();

        rotateClockwise();
        rotateClockwise();

    }


    private void moveUp()
    {
        boolean compressed = false;
        boolean merged = false;
        boolean compressed2 = false;
        int move = 0; //counting integer to check movement on screen

        rotateClockwise();
        rotateClockwise();
        rotateClockwise();
        for (int i = 0; i < SIDE; i++)
        {

            compressed = compressRow(gameField[i]); //makes boolean results true
            merged = mergeRow(gameField[i]);
            compressed2 = compressRow(gameField[i]);

            if (compressed || merged || compressed2)//if either action returned true move + 1
                move ++;
        }

        if (move != 0)
            createNewNumber();

        rotateClockwise();

    }


    private void moveDown()
    {
        boolean compressed = false;
        boolean merged = false;
        boolean compressed2 = false;
        int move = 0; //counting integer to check movement on screen
        rotateClockwise();
        for (int i = 0; i < SIDE; i++)
        {

        compressed = compressRow(gameField[i]); //makes boolean results true
        merged = mergeRow(gameField[i]);
        compressed2 = compressRow(gameField[i]);

        if (compressed || merged || compressed2)//if either action returned true move + 1
        move ++;
        }

        if (move != 0)
        createNewNumber();


        rotateClockwise();
        rotateClockwise();
        rotateClockwise();
    }

    private void rotateClockwise()
    {
        int[][] temp = new int[SIDE][SIDE];
        for (int i = 0; i < SIDE; ++i)
            for (int j = 0; j < SIDE; ++j)
                temp[i][j] = gameField[SIDE - j - 1][i];

        for (int i = 0; i < SIDE; ++i)
            for (int j = 0; j < SIDE; ++j)
                gameField[i][j] = temp[i][j];
    }

    //finds the max of the array
    private int getMaxTileValue()
    {
        int max = 0;
        for (int i = 0 ; i < SIDE; i++)
            for (int j = 0 ; j<SIDE; j++)
                if (gameField[i][j] > max)
                max = gameField[i][j];


         return max;
    }

    private void win()
    {
        isGameStopped = true;
        showMessageDialog(Color.GOLD, "You are a Winner!", Color.BLACK, 45);
    }

      private boolean canUserMove()
    {
        boolean movesLeft = false;
        //comparison loop, checking for empty cells and if all are full checking for  any duplicates when all spaces are taken.
        for (int i = 0 ; i < SIDE; i++)
            for (int j = 0 ; j<SIDE; j++)
            {
                if (gameField[i][j] == 0)
                {
                    movesLeft = true;
                }
                else if((i-1) > 0 && (gameField[i][j] == gameField[i-1][j]))
                {
                    movesLeft = true;
                }
                else if ((i+1) > 4 && (gameField[i][j] == gameField[i+1][j]))
                {
                    movesLeft = true;
                }
                else if ((j+1) < 4 && (gameField[i][j] == gameField[i][j+1]))
                {
                    movesLeft = true;
                }
                else if ((j-1)>0 && (gameField[i][j] == gameField[i][j-1]))
                {
                    movesLeft = true;
                }

            }

        return movesLeft;

    }




}