/***************************************************************************
 *   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 "piecelibrary.h"

#include <QPainter>
#include <QResource>

#include <lib3ds/io.h>
#include <lib3ds/mesh.h>

#include <GL/gl.h>

namespace toruschess {
typedef Lib3dsBool (*Lib3dsIoErrorFunc)(void *self);
typedef long (*Lib3dsIoSeekFunc)(void *self, long offset, Lib3dsIoSeek origin);
typedef long (*Lib3dsIoTellFunc)(void *self);
typedef size_t (*Lib3dsIoReadFunc)(void *self, void *buffer, size_t size);
typedef size_t (*Lib3dsIoWriteFunc)(void *self, const void *buffer, size_t size);

	Lib3dsBool io3ds_error(void *self);
	long io3ds_seek(void *self, long offset, Lib3dsIoSeek origin);
	long io3ds_tell(void *self);
	size_t io3ds_read(void *self, void *buffer, size_t size);
	size_t io3ds_write(void *self, const void *buffer, size_t size);

	class IO3ds {
	public:
		IO3ds(const QByteArray &data) : m_data(data), m_offset(0) {
			m_io = lib3ds_io_new(this, io3ds_error, io3ds_seek, io3ds_tell, io3ds_read, io3ds_write);
		}
		~IO3ds() {
			lib3ds_io_free(m_io);
		}
		
		long seek(long offset, Lib3dsIoSeek origin) {
			switch (origin) {
			case LIB3DS_SEEK_SET:
				m_offset = offset;
				break;
			case LIB3DS_SEEK_CUR:
				m_offset += offset;
				break;
			case LIB3DS_SEEK_END:
				m_offset = m_data.length() + offset;
				break;
			}
			return 0;
		}
		
		long tell() { return m_offset; }
		size_t read(void *buffer, size_t size) {
			qDebug("read (size %i): data-len = %i, offset = %i", (int) size, m_data.length(), (int) m_offset);
			if (m_offset >= m_data.length()) return 0;
			size_t r = qMin(size, (size_t) m_data.length() - m_offset);
			memcpy(buffer, m_data.constData() + m_offset, r);
			m_offset += r;
			qDebug("read: done with %i", (int) r);
			return r;
		}
		size_t write(const void *, size_t ) {
			return 0;
		}
		Lib3dsBool error() {
			qDebug("3ds error");
			return true;
		}
		
		Lib3dsIo* io() { return m_io; }
		
	private:
		Lib3dsIo *m_io;
		QByteArray m_data;
		long m_offset;
	};

	Lib3dsBool io3ds_error(void *self) {
		IO3ds *s = (IO3ds*) self;
		return s->error();
	}
	long io3ds_seek(void *self, long offset, Lib3dsIoSeek origin) {
		IO3ds *s = (IO3ds*) self;
		return s->seek(offset, origin);
	}
	long io3ds_tell(void *self) {
		IO3ds *s = (IO3ds*) self;
		return s->tell();
	}
	size_t io3ds_read(void *self, void *buffer, size_t size) {
		IO3ds *s = (IO3ds*) self;
		return s->read(buffer, size);
	}
	size_t io3ds_write(void *self, const void *buffer, size_t size) {
		IO3ds *s = (IO3ds*) self;
		return s->write(buffer, size);
	}


	Lib3dsFile *read_3ds(const QByteArray &data) {
		return lib3ds_file_load("media/pawn.3ds");
		IO3ds io(data);
		Lib3dsFile *f = lib3ds_file_new();
		if (!lib3ds_file_read(f, io.io())) {
			qDebug("data size: %i", (int) data.size());
			lib3ds_file_free(f);
			return NULL;
		}
		return f;
	}

	QByteArray loadResource(const QString &filename) {
		QResource res(filename);
		qDebug("res size: %i", (int) res.size());
		return QByteArray((const char*) res.data(), (int) res.size());
	}


	PieceLibrary::PieceLibrary(QObject *parent)
	: QObject(parent), m_pieces(0), m_buffers(0) {
		m_board = new QImage*[2];
		m_board[0] = new QImage(":/media/board2_white.png");
		m_board[1] = new QImage(":/media/board2_dark.png");
		m_pawn = read_3ds(loadResource(":/media/pawn.3ds"));
		setSize(45, 45);
	}
	
	PieceLibrary::~PieceLibrary() {
		if (m_pawn) lib3ds_file_free(m_pawn);
		delete [] m_pieces;
		delete [] m_buffers;
		delete [] m_board;
	}
	
	void PieceLibrary::setSize(int width, int height) {
		m_width = qMax(10, width);
		m_height = qMax(10, height);
		if (m_buffers) {
			for (int i = 0; i < 12; i++) {
				delete m_buffers[i];
			}
		} else {
			m_pieces = new QSvgRenderer* [12];
			m_buffers = new QImage* [12];
			for (int i = 0; i < 12; i++) {
				m_pieces[i] = new QSvgRenderer(QString(":/media/chess_%1.svg").arg(i+1), this);
			}
		}
		for (int i = 0; i < 12; i++) {
			m_buffers[i] = new QImage(QSize(m_width, m_height), QImage::Format_ARGB32_Premultiplied);
			m_buffers[i]->fill(0x0);
			QPainter p(m_buffers[i]);
			m_pieces[i]->render(&p);
		}
	}
	
	void PieceLibrary::paint(QPainter &pt, int place) const {
		if (place == 0) return;
		if (place < 0) place = 6 - place;
		if (place <= 0 || place > 12) return;
		pt.drawImage(0, 0, *m_buffers[place-1]);
	}
	
	void PieceLibrary::paint(QPainter &pt, int place, const QRect &rect) const {
		if (place == 0) return;
		if (place < 0) place = 6 - place;
		if (place <= 0 || place > 12) return;
		pt.drawImage(rect, *m_buffers[place-1]);
	}
	
	void PieceLibrary::paint_board(QPainter &pt, bool darkField, const QRect &rect) const {
		pt.drawImage(rect, *m_board[darkField ? 1 : 0]);
	}
	
	void PieceLibrary::paint_pawn() {
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(3,GL_FLOAT,0,m_pawn->meshes->pointL);
		for (unsigned int i = 0; i < m_pawn->meshes->faces; i++) {
			glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, m_pawn->meshes->faceL[i].points);
		}
		glDisableClientState(GL_VERTEX_ARRAY);
	}
	
}
