/***************************************************************************
** Initial code taken from: http://www.labsquare.org (Sacha Schutz)       **
** Where can be drawn a grid and that object could paint the desired cell **
****************************************************************************
**                                                                        **
**  GridView, a simple GridView made with Qt4                             **
**  Copyright (C) 2013 Sacha Schutz                                       **
**                                                                        **
**  This program is free software: you can redistribute it and/or modify  **
**  it under the terms of the GNU General Public License as published by  **
**  the Free Software Foundation, either version 3 of the License, or     **
**  (at your option) any later version.                                   **
**                                                                        **
**  This program is distributed in the hope that it will be useful,       **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         **
**  GNU General Public License for more details.                          **
**                                                                        **
**  You should have received a copy of the GNU General Public License     **
**  along with this program.  If not, see http://www.gnu.org/licenses/.   **
**                                                                        **
****************************************************************************/

//The rest of the code was implemented by Ramon Collazo for the project 
//TUES at UPRRP.

#include "grid.h"
#include <QVBoxLayout>
#include <QToolBar>
#include <QtGlobal>
#include <QDebug>
#include <QtCore>

/// \fn GridWidget::GridWidget(QWidget *parent)
/// \~English
/// \brief Default constructor. The properties of the grid are set as follows:
///* mCellSize: Sets the size of the cell to 10px
///* mRowCount: Sets the number of rows in the grid to 39
///* mColumnCount: Sets the number of columns in the grid to 27
///* Tool and ToolSize: Sets the Tool which is going to be used to draw the grid
///* frontColor: The color which will be used to paint to black
///* backColor: Current clicked square
///* background: Sets the background color to white
/// \~English
/// \brief Constructor por defecto. Las propiedades de el grid se ajustan como sigue:
/// * mCellSize: Ajusta el tamano de la celda a 10px
/// * mRowCount: Ajusta el numero de filas en la cuadricula a 39
/// * ColumnCount: Ajusta el numero de columnas en el grid a 27
/// * Tool and ToolSize: Ajusta la herramiento que se va a utilizar para dibujar
/// el grid.
/// * frontColor: El color que se utilizara para pintar a negro
/// * backColor: Cuadrado marcado actualmente
/// * background: El color del fondo en blanco
GridWidget::GridWidget(QWidget *parent):
    QWidget(parent)
{
    mCellSize = 10;
    mRowCount = 39;
    mColumnCount = 27;

    Tool = "dot";
    ToolSize = 1;
    frontColor = Qt::black;

    /* QColor::Invalid is used to identify when the color is not selected*/
    /* QColor::Invalid es usado para identificar cuando el color no esta seleccionado*/
    backColor = QColor::Invalid;

    background = Qt::white;
    emit canUndo(false);
    emit canRedo(false);
}

/// \fn GridWidget::GridWidget(int rowCount, int columnCount, QWidget *parent):
/// \~English
/// \brief Constructor which receives the number of rows and columns in the grid
/// \param rowCount number of rows in the grid
/// \param columnCount number of columns in the grid
/// \param parent parent window
/// \~English
/// \brief Constructor que recibe el numero de filas y columnas en la cuadricula
/// \param rowCount numero de filas en la cuadricula
/// \param columnCount numero de columnas en la cuadricula
/// \param parent ventana padre
GridWidget::GridWidget(int rowCount, int columnCount, QWidget *parent):
    QWidget(parent)
{

  mCellSize = 10;
  mRowCount = rowCount;
  mColumnCount = columnCount;

  Tool = "dot";
  ToolSize = 1;
  frontColor = Qt::black;
  backColor = QColor::Invalid;
  background = Qt::white;
  emit canUndo(false);
  emit canRedo(false);
}

/// \fn void GridWidget::setTool(QString tool)
/// \~English
/// \param tool choosen tool
/// \brief Sets the tool
/// \param tool herramienta escogida
/// \~Spanish
/// \brief Ajusta la herramienta
void GridWidget::setTool(QString tool){
    Tool = tool;
}

/// \fn void GridWidget::setFront(QString front)
/// \~English
/// \brief Sets the color of the brush
/// \param front brush color
/// \~Spanish
/// \brief Ajusta el color de la brocha
/// \param front color de la brocha
void GridWidget::setFront(QString front){
    frontColor = front;
}

/// \fn void GridWidget::setBack(QString back)
/// \~English
/// \brief Sets the color of the background
/// \param back background color
/// \~Spanish
/// \brief Ajusta el color del fondo
/// \param back color para el fondo
void GridWidget::setBack(QString back){
    background=back;
}

/// \fn void GridWidget::setToolSize(int size)
/// \~English
/// \brief Sets the size of the tool
/// \param size tool size
/// \~Spanish
/// \brief Ajusta el tamano de la herramienta
/// \param size tamano de la herramienta
void GridWidget::setToolSize(int size){
    ToolSize = size;
}

/// \fn void GridWidget::setGridSize(int rowCount, int columnCount)
/// \~English
/// \brief Sets the number of columns and rows of the grid
/// \param rowCount number of rows
/// \param columnCount number of columns
/// \~Spanish
/// \brief Ajusta el numero de columnas y fileas de la cuadricula
/// \param rowCount numero de filas
/// \param columnCount numero de columnas
void GridWidget::setGridSize(int rowCount, int columnCount)
{
    mRowCount = rowCount;
    mColumnCount = columnCount;
}

/// \fn void GridWidget::paintEvent(QPaintEvent *event)
/// \~English
/// \brief This function is automatically invoked each time the widget or
/// its parent receives a repaint signal.
/// \param event received event reference
/// \~Spanish
/// \brief Esta funcion es invocada automaticmente cada ves que el widget o
/// el padre recibe una senal de repintar.
/// \param event referencia a un evento recibido
void GridWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    drawGrid(this);
}

/// \fn void GridWidget::mousePressEvent(QMouseEvent * event)
/// \~English
/// \brief When the mouse is clicked on a cell of the grid it gets the x,y
/// coordinates of the mouse and uses them to paint the tool at that
/// location.
/// \param event received event reference
/// \~Spanish
/// \brief Cuando el raton (mouse) is marcada en una celda de la cuadricula
/// obtiene las coordenadas x, y del raton y los usa para pintar la herramienta en
/// ese lugar.
void GridWidget::mousePressEvent(QMouseEvent * event)
{
    if (!newStates.empty()) newStates.clear();
    if (oldStates.size() == 5) oldStates.pop_front();
    oldStates.push_back(mColors);

    emit canUndo(true);
    emit canRedo(false);

    int X = event->x() / mCellSize;
    int Y = event->y() / mCellSize;

    backColor = mColors[mColumnCount * Y + X];
    identifyTool(Tool, X, Y);

    emit cellClicked(QPoint(X,Y));
    QWidget::mousePressEvent(event);
}

/// \fn void GridWidget::undo()
/// \~English
/// \brief When the undo button is pressed the current state is pushed into
/// the redo vector(newStates) and the last state in the undo vector(oldStates)
/// is painted on the grid.
/// \~Spanish
/// \brief Cuando el boton es apretado el estado acutual es empujado en
/// el vector(newStates) de redo y el ultimo estado en el vector(oldStates) undo es
/// pintado en la cuadricula
void GridWidget::undo(){

    /* When the undo button is pressed the current state is pushed into
     * the redo vector and the last state in the undo vector is painted
     * on the grid.
     * Cuando el boton undo es marcado el estado corriente es empujaro en
     * el vector redo y el ultimo estado en el vector undo es pintado en la
     * cuadricula
     */

    if (!oldStates.empty()){
        newStates.push_back(mColors);
        canRedo(true);
        mColors.clear();
        mColors = oldStates.back();
        oldStates.pop_back();
        if (oldStates.empty()) canUndo(false);

        repaint();
    }
}

/// \fn void GridWidget::redo()
/// \~English
/// \brief When the redo button is pressed the current state is pushed into
/// the undo vector(oldStates) and the last state in the redo vector(newStates)
/// is painted on the grid.
/// \~Spanish Cuando el boton redo es marcado el estado actual es empujado en
/// el vector(oldStates) undo y el ultimo estado en el vector redo(newStates) es
/// pintaod en el grid.
///
void GridWidget::redo(){

    if (!newStates.empty()){
        oldStates.push_back(mColors);
        canUndo(true);
        mColors.clear();
        mColors = newStates.back();
        newStates.pop_back();
        if (newStates.empty()) canRedo(false);
        repaint();
    }
}

/// \fn void GridWidget::switchOn(int x, int y, const QColor &color)
/// \~English
/// \brief Saves the given color and position in the vector
/// that represents the painted cells of the grid
/// \param x coordinate x of the cell in the grid
/// \param y coordinate y of the cell in the grid
/// \param color color to paint cell
/// \~Spanish
/// \brief Guarda el color y la posicion dados en el vector que
/// representa las celdas pintadas en la cuadricula
/// \param x coordenada x de la celda en el cuadricula
/// \param y coordenada y de la celda en la cuadricula
/// \param color color to paint cell
void GridWidget::switchOn(int x, int y, const QColor &color)
{
    int index =  mColumnCount * y  + x;
    mColors[index] = color;
}


/// \fn QColor GridWidget::getCellColor(int x, int y) ;
/// \~English
/// \brief Returns the color of the cell in position (x,y)
/// \param x coordinate x of the cell in the grid
/// \param y coordinate y of the cell in the grid
/// \return the color of the cell
/// \~Spanish
/// \brief Devuelve el color de la celda en la posicion (x,y)
/// \param x coordenada x de la celda en el cuadricula
/// \param y coordenada y de la celda en la cuadricula
/// \return el color de la celda
QColor GridWidget::getCellColor(int x, int y){

    return mColors[mColumnCount * y  + x] ;
}


/// \fn int GridWidget::getGridColumns() ;
/// \~English
/// \brief Returns the number of columns in the grid
/// \return number of columns in the grid
/// \~Spanish
/// \brief Devuelve el numero de columnas en la cuadricula
/// \return el numero de columnas en la cuadricula
int GridWidget::getGridColumns() {
    return mColumnCount ;
}

/// \fn int GridWidget::getGridRows() ;
/// \~English
/// \brief Returns the number of rows in the grid
/// \return number of rows in the grid
/// \~Spanish
/// \brief Devuelve el numero de filas en la cuadricula
/// \return el numero de filas en la cuadricula
int GridWidget::getGridRows(){
    return mRowCount ;
}

/// \fn void GridWidget::switchOff(int x, int y)
/// \~English
/// \brief Removes the given position from the vector that
/// represents the painted cells of the grid
/// \param x coordinate x of the cell in the grid
/// \param y coordinate y of the cell in the grid
/// \~Spanish
/// \brief Remueve la posicion dada del vector que representa
/// las celdas pintadas en la cuadricula
/// \param x coordenada x de la celda en el cuadricula
/// \param y coordenada y de la celda en la cuadricula
void GridWidget::switchOff(int x, int y)
{
    int index =  mColumnCount * y + x;
    mColors.remove(index);
}

/// \fn void GridWidget::clear()
/// \~English
/// \brief Clears the grid and sets it to its initial state
/// \~Spanish
/// \brief Limpa la cuadricula y la pone en su estado inicial.
void GridWidget::clear()
{
    oldStates.push_back(mColors);
    mColors.clear();
    canRedo(false);
    if (!newStates.empty()) newStates.clear();
}

/// \fn void GridWidget::setCellSize(int size)
/// \~English
/// \brief Sets the size of the cells in the grid
/// \param size cell size
/// \~Spanish
/// \brief Ajusta el tamano de las celdas de la cuadricula
/// \param size tamano de la celda
void GridWidget::setCellSize(int size)
{
    mCellSize = size;
}

/// \fn void GridWidget::drawGrid(QPaintDevice *device)
/// \~English
/// \brief Function that first sets the size of the GridWidget, then paints the cells
/// with the color selected for the background and finally paints
/// the lines to form the grid.
/// \param device the panel to paint the grid
/// \~Spanish
/// \brief Funcion que primero ajusta el tamano del widget de la cuadricula, luego pinta
/// las celdas con el color seleccionado en el background y finalmente pinta las lineas
/// para formar el grid.
/// \param device el panel para pintar la cuadricula
void GridWidget::drawGrid(QPaintDevice *device)
{
    mGridPix = QPixmap(size());

    QPainter paint;
    paint.begin(device);

    mGridPix.fill(background);
    paint.drawPixmap(0,0,mGridPix);
    paint.setPen(QPen(Qt::lightGray));

    //Draws vertical lines
    //Dibuja las lineas verticales
    for ( int x=0;x<width(); x+=mCellSize )
        paint.drawLine(x,rect().top(), x, rect().bottom());

    //Draws horizontal lines
    // Dibuja las lineas horizontales
    for ( int y=0;y<height(); y+=mCellSize )
        paint.drawLine(rect().left(),y,rect().right(),y);

    // Draw Cells
    // Dibuja las celdas
    foreach (int index , mColors.keys())
    {
        int y = qFloor(index/mColumnCount);
        int x = index % mColumnCount;

        if (mColors[index] != QColor::Invalid){
            paint.setBrush(mColors[index]);
            paint.drawRect(x*mCellSize, y*mCellSize, mCellSize, mCellSize);
        }
    }

    paint.end();
}

/// \fn void GridWidget::identifyTool(QString tool, int x, int y)
/// \~English
/// \brief This function is called on each mousePressEvent inside the grid.
/// It identifies the way the grid will be painted calling the function of the
/// tool received in the parameters (dot, rowfill, column fill, diagonal, square,
/// triangles and circles).
/// \param tool the tool to be called by the funtion
/// \param x coordinate x of the cell in the grid.
/// \param y coordinate y of the cell in the grid.
/// \~Spanish
/// \brief Esta funcion es llamada en cada evento de el mouse presionado dentro
/// de la cuadricula.  Identifica la forma en que el grid va a ser pintado llamando
/// la funcion del tool recibido en los parametros (dot, rowfill, column fill, diagonal, square,
/// triangles and circles).
/// \param tool la herramienta a llamar por la funcion
/// \param x coordenada x de la celda en la cuadricula
/// \param y coordenada y de la celde en la cuadricula
void GridWidget::identifyTool(QString tool, int x, int y){

    if (tool == "dot")                 Dot(x, y, frontColor);
    else if (tool == "row")            RowMajorFill(x, y, backColor, frontColor);
    else if (tool == "column")         ColMajorFill(x, y, backColor, frontColor);
    else if (tool == "diagonal left")  DiagonalLeft(x, y, backColor, frontColor);
    else if (tool == "diagonal right") DiagonalRight(x, y, backColor, frontColor);
    else if (tool == "square")         square(x, y, frontColor, ToolSize);
    else if (tool == "triangle")       triangle(x, y, frontColor, ToolSize);
    else if (tool == "circle")         circle(x, y, frontColor, ToolSize);
    else if (tool == "flood")          flood_fill(x,y,frontColor, backColor);
    else qDebug() << "Tool Selection Error!";
    repaint();
}
