/***************************************************************************
*   KBlocks, a falling blocks game for KDE                                *
*   Copyright (C) 2010 Zhongjie Cai <squall.leonhart.cai@gmail.com>       *
*                                                                         *
*   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 2 of the License, or     *
*   (at your option) any later version.                                   *
***************************************************************************/
#include "KBlocksLayout.h"

KBlocksLayout::KBlocksLayout(FieldInterface *pF, PieceInterface *pA, PieceInterface *pN)
{
    mpGameField = pF;
    mpActivePiece = pA;
    mpNextPiece = pN;

    mPieceCellCount = mpActivePiece->getCellCount();
    mpLastPiecePos = new QPoint*[mPieceCellCount];
    for (int i = 0; i < mPieceCellCount; i++) {
        mpLastPiecePos[i] = new QPoint(0, 0);
    }

    mWidth = mpGameField->getWidth();
    mHeight = mpGameField->getHeight();
    boardInfo = new int *[mHeight];
    for (int i = 0; i < mHeight; i++) {
        boardInfo[i] = new int[mWidth];
        for (int j = 0; j < mWidth; j++) {
            boardInfo[i][j] = -1;
        }
    }

    prepareInfo = new int *[PREPARE_AREA_WIDTH];
    for (int i = 0; i < PREPARE_AREA_WIDTH; i++) {
        prepareInfo[i] = new int[PREPARE_AREA_WIDTH];
        for (int j = 0; j < PREPARE_AREA_WIDTH; j++) {
            prepareInfo[i][j] = -1;
        }
    }
}

KBlocksLayout::~KBlocksLayout()
{
    for (int i = 0; i < mHeight; i++) {
        delete [] boardInfo[i];
    }
    delete [] boardInfo;

    for (int i = 0; i < PREPARE_AREA_WIDTH; i++) {
        delete [] prepareInfo[i];
    }
    delete [] prepareInfo;

    for (int i = 0; i < mPieceCellCount; i++) {
        delete mpLastPiecePos[i];
    }
    delete [] mpLastPiecePos;
}

void KBlocksLayout::beginUpdate(QList<int> *list)
{
    int px = 0;
    int py = 0;
    list->clear();
    for (int i = 0; i < mPieceCellCount; i++) {
        px = mpLastPiecePos[i]->x();
        py = mpLastPiecePos[i]->y();
        list->append(px);
        list->append(py);
        if ((px >= 0) && (px < mWidth)
                && (py >= 0) && (py < mHeight)) {
            boardInfo[py][px] = -1;
        }
    }
}

void KBlocksLayout::updateLayout(int type, const QList<int> &dataList)
{
    switch (type) {
    case KBlocksLayout_Update_FreezePiece:
        updateFreezePiece(dataList);
        break;
    case KBlocksLayout_Update_RemoveLine:
        updateRemoveLine(dataList);
        break;
    case KBlocksLayout_Update_PunishLine:
        updatePunishLine(dataList);
        break;
    }
}

void KBlocksLayout::endUpdate()
{
    int px = 0;
    int py = 0;
    int activePieceColor = mpActivePiece->getType();
    for (int i = 0; i < mPieceCellCount; i++) {
        px = mpActivePiece->getCellPosX(i);
        py = mpActivePiece->getCellPosY(i);
        mpLastPiecePos[i]->setX(px);
        mpLastPiecePos[i]->setY(py);
        if ((px >= 0) && (px < mWidth)
                && (py >= 0) && (py < mHeight)) {
            boardInfo[py][px] = activePieceColor;
        }
    }

    updatePrepareArea();
}

void KBlocksLayout::updateSnapshot()
{
    for (int y = 0; y < mHeight; y++) {
        for (int x = 0; x < mWidth; x++) {
            if (mpGameField->getCell(x, y)) {
                boardInfo[y][x] = 0; // TODO : what color is better here?
            } else {
                boardInfo[y][x] = -1;
            }
        }
    }

    endUpdate();
}

int KBlocksLayout::getFieldColor(int posX, int posY)
{
    if ((posX < 0) || (posX >= mWidth) || (posY < 0) || (posY >= mHeight)) {
        return -1;
    }

    return boardInfo[posY][posX];
}

int KBlocksLayout::getPrepareColor(int posX, int posY)
{
    if ((posX < 0) || (posX >= PREPARE_AREA_WIDTH) || (posY < 0) || (posY >= PREPARE_AREA_WIDTH)) {
        return -1;
    }

    return prepareInfo[posY][posX];
}

void KBlocksLayout::updatePrepareArea()
{
    for (int i = 0; i < PREPARE_AREA_WIDTH; i++) {
        for (int j = 0; j < PREPARE_AREA_WIDTH; j++) {
            prepareInfo[i][j] = -1;
        }
    }

    int nextPieceColor = mpNextPiece->getType();
    for (int i = 0; i < 4; i++) {
        int posX = mpNextPiece->getCellPosX(i);
        int posY = mpNextPiece->getCellPosY(i);
        prepareInfo[posY][posX] = nextPieceColor;
    }
}

void KBlocksLayout::updateFreezePiece(const QList<int> &dataList)
{
    QList<int> tmpList = dataList;
    int freezeColor = tmpList.takeFirst();
    int loopCount = tmpList.size() / 2;
    for (int i = 0; i < loopCount; i++) {
        int posX = tmpList[i * 2];
        int posY = tmpList[i * 2 + 1];
        if ((posX >= 0) && (posX < mWidth)
                && (posY >= 0) && (posY < mHeight)) {
            boardInfo[posY][posX] = freezeColor;
        }
    }
}

void KBlocksLayout::updateRemoveLine(const QList<int> &dataList)
{
    int lineCount = dataList.size();
    for (int k = 0; k < lineCount; k++) {
        for (int i = dataList[k]; i > 0; i--) {
            for (int j = 0; j < mWidth; j++) {
                boardInfo[i][j] = boardInfo[i - 1][j];
            }
        }
        for (int j = 0; j < mWidth; j++) {
            boardInfo[0][j] = -1;
        }
    }
}

void KBlocksLayout::updatePunishLine(const QList<int> &dataList)
{
    int punishColor = mpNextPiece->getType(); // TODO : this color or new color?
    int lineCount = dataList.size();
    for (int k = 0; k < lineCount; k++) {
        for (int i = 0; i < mHeight - dataList[k]; i++) {
            for (int j = 0; j < mWidth; j++) {
                boardInfo[i][j] = boardInfo[i + dataList[k]][j];
            }
        }
        for (int i = mHeight - dataList[k]; i < mHeight; i++) {
            for (int j = 0; j < mWidth; j++) {
                if (mpGameField->getCell(j, i)) {
                    boardInfo[i][j] = punishColor;
                } else {
                    boardInfo[i][j] = -1;
                }
            }
        }
    }
}
