/***************************************************************************
                           object.cpp
                           -------------------
    copyright            : (C) 2004 by Jos van den Oever
    email                : jos@vandenoever.info
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "object.h"
#include <QtGui/QPainter>
#include <QtGui/QPixmap>
#include <QtCore/QTimer>
#include <q3picture.h>
#include <qcursor.h>
#include <q3valuelist.h>
//Added by qt3to4:
#include <QPaintEvent>
#include <QResizeEvent>
#include <QMouseEvent>
#include <cmath>
#include <iostream>

Object::Object(QWidget *parent, const char *name)
        : QWidget(parent, name, Qt::WNoAutoErase) {

	scaling = 1;
	m.scale(scaling);
	setMouseTracking(true);
	firstMouseEvent = true;
	changed = true;
	maxdist=0;
	elements=0;

	currentCursor = 0;
	setCursor(Qt::SizeHorCursor);
	rotangle = 0;
}
void
Object::addSide(Side s) {
	double newdist;
	newdist = s.getMaxDistance();
	if (newdist > maxdist) {
		maxdist = newdist;
		m.scale(1/scaling);
		scaling = 2*maxdist;
		m.scale(scaling);
	}
	s.number = elements++;
	sides.push_back(s);
}
const Side &
Object::getSide(short i) const {
	QList<Side>::const_iterator it = sides.begin();
	while (it != sides.end()) {
		if ((*it).number == i) {
			return *it;
		}
		it++;
	}
	throw 0;
}
Side &
Object::getSide(short i) {
	QList<Side>::iterator it = sides.begin();
	while (it != sides.end()) {
		if ((*it).number == i) {
			return *it;
		}
		it++;
	}
	throw 0;
}
bool
Object::hasPicture(const Q3Picture *p1, short i) const {
	QList<Side>::const_iterator it = sides.begin();
	while (it != sides.end()) {
		const Q3Picture *p2 = (*it).picture();
		if ((*it).number != i && p1->size() == p2->size()
			&& !strncmp(p1->data(), p2->data(), p1->size())) {
			return true;
		}
		it++;
	}
	return false;
}
void
Object::rotateX(double deg) {
	m.rotX(deg);
	changed = true;
	emit rotatedX(deg);
	emit rotated();
	update();
}
void
Object::rotateY(double deg) {
	m.rotY(deg);
	changed = true;
	emit rotatedY(deg);
	emit rotated();
	update();
}
void
Object::rotateZ(double deg) {
	m.rotZ(deg);
	changed = true;
	emit rotated();
	update();
}
void
Object::setOrient(double t, double p, double r) {
	m.setOrient(t, p, r);
	changed = true;
	update();
}
void
Object::reset() {
	m.unit();
	m.savePosition();
	m.scale(scaling);
	changed = true;
	update();
}
void
Object::paintEvent(QPaintEvent *) {
	if (buffer.size() != size()) {
		buffer.resize(size());
	}
	if (changed) {
		updateMatrices();
		QPainter painter;
		painter.begin(&buffer);
		painter.setRenderHint(QPainter::Antialiasing);
		if (paletteBackgroundPixmap() != NULL) {
			QSize s = paletteBackgroundPixmap()->size();
			QPoint p(0,0);
			if (backgroundOrigin() == QWidget::WindowOrigin) {
				p = mapTo(topLevelWidget(), p);
			} else if (backgroundOrigin() == QWidget::AncestorOrigin) {
				QWidget *w = parentWidget();
				while (w && w->backgroundOrigin()
					== QWidget::AncestorOrigin) {
						w = w->parentWidget();
				}
				if (w) {
					p = mapTo(w, p);
				} else {
					p = mapTo(topLevelWidget(), p);
				}
			} else {
				p = mapTo(parentWidget(), p);
			}
			p.rx() %= s.width();
			p.ry() %= s.height();
			painter.drawTiledPixmap(buffer.rect(),
				*paletteBackgroundPixmap(), p);
		} else {
			painter.fillRect(buffer.rect(), backgroundColor());
		}
		painter.translate(width()/2,height()/2);
		QList<Side>::const_iterator it;
		it = sides.constBegin();
		while (it != sides.constEnd()) {
			(*it).paintSide(&painter);
			it++;
		}
		painter.end();
		changed=false;
	}
	bitBlt(this, 0, 0, &buffer);
}
void
Object::mouseMoveEvent ( QMouseEvent * e ) {
	int x = e->x();
	int y = e->y();
	if (!firstMouseEvent && e->state() == Qt::LeftButton) {
		switch (rotangle) {
		case 0:
			m.rotY(x-lastX);
			break;
		case 1:
			m.rotX(y-lastY);
			break;
		case 2:
			m.rotZ(x-lastX);
			updateCursor(x, y, true);
			break;
		case 3:
			m.rotZ(lastY-y);
			updateCursor(x, y, true);
			break;
		case 4:
			m.rotZ(lastX-x);
			updateCursor(x, y, true);
			break;
		case 5:
			m.rotZ(y-lastY);
			updateCursor(x, y, true);
			break;
		}
		emit rotated();
		changed = true;
		update();
		movedSinceMousePress = true;
	} else {
		updateCursor(x,y);
	}
	lastX = x;
	lastY = y;
	firstMouseEvent = false;
}
void
Object::mousePressEvent(QMouseEvent *e) {
	if (e->button() == Qt::LeftButton) {
		movedSinceMousePress = false;
	}
}
void
Object::mouseReleaseEvent(QMouseEvent *e) {
	if (e->button() == Qt::LeftButton) {
		int x = e->x();
		int y = e->y();
		QPoint p;
		if (!movedSinceMousePress) {
			p.setX(x-width()/2);
			p.setY(y-height()/2);
			for (int i=sides.size()-1; i>= 0; --i) {
				if (sides[i].processClick(p)) break;
			}
			changed = true;
			update();
		}
		updateCursor(x,y);
	}
}
/* The maximal size of the the cube is 2*sqrt(3). So, the matrix should be scaled min(width, height)/2/sqrt(3). */
void
Object::resizeEvent( QResizeEvent * ) {
	double dim = (width()<height())?width():height();
	m.scale(1/scaling);
	scaling = dim/2.0/maxdist;
	m.scale(scaling);
	changed = true;
	update();
}
void
Object::updateMatrices() {
	QList<Side>::iterator it;
	it = sides.begin();
	while (it != sides.end()) {
		(*it).updateMatrix(m);
		it++;
	}
/*
	class Compare_sides {
	public:
		int operator()(pair<Side, short int> s1, pair<Side, short int> s2) {
			return s1.first < s2.first;}
	};
	sides.sort(Compare_sides());
*/
	qSort(sides);
}
void
Object::animateToSavedOrientation() {
	animate = true;
	QTimer *timer = new QTimer( this );
        connect( timer, SIGNAL(timeout()), this, SLOT(animationStep()) );
        timer->start( 40, true );
}
void
Object::animationStep() {
	if (animate && m.stepBack()) {
		changed = true;
		update();
		QTimer *timer = new QTimer( this );
		connect(timer, SIGNAL(timeout()), this, SLOT(animationStep()));
		timer->start( 40, true ); // 40 milliseconds single-shot timer
	} else {
		emit animationFinished();
	}
}
void
Object::stopAnimation() {
	animate = false;
}
void
Object::saveOrientation() {
	m.savePosition();
}
void
Object::setPicture(short i, const Q3Picture *p) {
	getSide(i).setPicture(p);
}
/* The cursor changes depending on the position above the object.
   Also, the rotation depends on the starting position above the object.

	\2222222/
	3\22122/5
	33\111/55
	330\1/055
	3000X0005
	330/1\055
	33/111\55
	3/44144\5
	/4444444\

   The value of 0 means rotation around the y axis, 1 means rotation around the
   x axis and the other value mean rotation around the z axis. */
void
Object::updateCursor(int x, int y, bool onlyZ) {
	float fx, fy;
	float ax, ay;
	fx = float(x)/width();
	fy = float(y)/height();
	ax = fabs(fx-0.5);
	ay = fabs(fy-0.5);
	if (ax > ay) {
		if (fx < 0.5) {
			rotangle = 1;
		} else {
			rotangle = 3;
		}
	} else {
		if (fy < 0.5) {
			rotangle = 0;
		} else {
			rotangle = 2;
		}
	}
	if (!onlyZ && ax*ax+ay*ay < 0.1) {
		if (rotangle == 1 || rotangle == 3) {
			rotangle = 0;
		} else {
			rotangle = 1;
		}
	} else {
		rotangle += 2;
	}

	int newcursor = rotangle%2;
	if (currentCursor != newcursor) {
		if (newcursor == 0) {
			setCursor(Qt::SizeHorCursor);
		} else {
			setCursor(Qt::SizeVerCursor);
		}
		currentCursor = newcursor;
	}
}
