/***************************************************************************
 *   Copyright (C) 2009 by Stefan Bühler                                   *
 *   stbuehler@web.de                                                      *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 *   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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "field2d.h"

#include <math.h>

#include <QResizeEvent>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QPainter>

namespace toruschess {
	
	Field2D::Field2D(Game *game, QWidget *parent)
	: QWidget(parent), m_lib(new PieceLibrary(this)), m_game(game), m_fieldWidth(360), m_fieldHeight(360), m_originX(23), m_originY(23), m_fieldBuffer(0) {
		for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) m_marked[x][y] = false;
		m_lib->setSize(m_fieldWidth / 8, m_fieldHeight / 8);
		checkSizes();
		connect(game, SIGNAL(updated()), this, SLOT(fieldUpdated()));
	}
	
	Field2D::~Field2D() {
		if (m_fieldBuffer) delete m_fieldBuffer;
	}
	
	void Field2D::markMoves(const QList<Move> &moves) {
		foreach(Move m, m_markedMoves) m_marked[m.to().x()][m.to().y()] = false;
		m_markedMoves = moves;
		foreach(Move m, m_markedMoves) m_marked[m.to().x()][m.to().y()] = true;
		updateFieldBuffer();
		update();
	}
	
	QSize Field2D::sizeHint() const {
		return QSize(17*m_fieldWidth/8, 17*m_fieldHeight/8);
	}
	
	void Field2D::paintEvent(QPaintEvent *) {
		QPainter pt(this);
		
		int winWidth = width(), winHeight = height();
		
		for (int px = m_originX - m_fieldWidth; px < winWidth; px += m_fieldWidth)
		for (int py = m_originY - m_fieldHeight; py < winHeight; py += m_fieldHeight) {
			pt.drawImage(QRect(px, py, m_fieldWidth, m_fieldHeight), *m_fieldBuffer);
		}
	}
	
	void Field2D::mousePressEvent(QMouseEvent *event) {
		if (event->button() == Qt::LeftButton) {
			QList<Move> moves;
			Pos p = findPos(event->x(), event->y());
			moves = m_game->field()->validMoves(p);
			markMoves(moves);
		}
		m_mouseLastX = event->x();
		m_mouseLastY = event->y();
	}
	
	void Field2D::mouseMoveEvent(QMouseEvent *event) {
		if (event->buttons() & Qt::RightButton) {
			m_originX += event->x() - m_mouseLastX;
			m_originY += event->y() - m_mouseLastY;
			checkSizes();
			update();
		}
		m_mouseLastX = event->x();
		m_mouseLastY = event->y();
	}
	
	void Field2D::mouseReleaseEvent(QMouseEvent *event) {
		m_mouseLastX = event->x();
		m_mouseLastY = event->y();
		if (event->button() == Qt::LeftButton) {
			if (m_markedMoves.size() == 0) return;
			Pos from = m_markedMoves[0].from();
			QList<Move> moves;
			markMoves(QList<Move>());
			Pos p = findPos(event->x(), event->y());
			Move m(m_game->field(), from, p);
			m_game->move(m);
		}
	}
	
	void Field2D::wheelEvent(QWheelEvent *event) {
		double zoom = pow(1.2, event->delta() / 120);
		int max = qMin(width(), height());
		m_fieldWidth = qMax(120, qMin(max, (int) (m_fieldWidth * zoom))) & ~(0x7);
		m_fieldHeight = qMax(120, qMin(max, (int) (m_fieldHeight * zoom))) & ~(0x7);
		m_lib->setSize(m_fieldWidth / 8, m_fieldHeight / 8);
		checkSizes();
		update();
	}
	
	void Field2D::checkSizes() {
		m_originX = m_originX % m_fieldWidth;
		m_originY = m_originY % m_fieldHeight;
		if (m_originX < 0) m_originX += m_fieldWidth;
		if (m_originY < 0) m_originY += m_fieldHeight;
		if (!m_fieldBuffer || m_fieldBuffer->width() != m_fieldWidth || m_fieldBuffer->height() != m_fieldHeight) {
			if (m_fieldBuffer) delete m_fieldBuffer;
			m_fieldBuffer = new QImage(QSize(m_fieldWidth, m_fieldHeight), QImage::Format_ARGB32_Premultiplied);
			m_fieldBuffer->fill(Qt::black);
			updateFieldBuffer();
		}
	}
	
	void Field2D::updateFieldBuffer() {
		QPainter pt(m_fieldBuffer);
		
		int pieceWidth = m_fieldWidth / 8;
		int pieceHeight = m_fieldHeight / 8;
		for (int x = 0; x < 8; x++)
		for (int y = 0; y < 8; y++) {
			bool marked = m_marked[x][y];
			int place = m_game->field()->place(Pos(x, y));

			QRect prect(x * pieceWidth, y * pieceHeight, pieceWidth - 1, pieceHeight - 1);
			m_lib->paint_board(pt, (x+y) % 2 == 1, prect);
			m_lib->paint(pt, place, prect);
			if (marked) {
				int r = qMax(1, (pieceWidth + pieceHeight) / 36);
				pt.setBrush(place != 0 ? Qt::green : Qt::white);
				pt.setPen(Qt::red);
				pt.drawEllipse(QPoint(prect.x() + pieceWidth / 2, prect.y() + pieceHeight / 2), r, r);
			}
		}
		update();
	}
	
	Pos Field2D::findPos(int x, int y) {
		return Pos(8 * ((x - m_originX) % m_fieldWidth) / m_fieldWidth, 8 * ((y - m_originY) % m_fieldHeight) / m_fieldHeight);
	}
	
	void Field2D::fieldUpdated() {
		markMoves(QList<Move>());
	}
	
}
