/*
    QGLView
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef GLCAMERA_H
#define GLCAMERA_H

#include "globject.h"

class Camera;

// extern QMatrix4x4 globCameraMatrix;
// extern Camera * currentCamera;

class Camera: public GLObjectBase {
	friend class QGLView;
	friend class GLParticlesSystem;
	friend QDataStream & operator<<(QDataStream & s, const GLObjectBase * p);
	friend QDataStream & operator>>(QDataStream & s, GLObjectBase *& p);

public:
	Camera();

	void setPos(const QVector3D & p) {
		pos_ = p;
		anglesFromPoints();
		buildTransform();
	}
	void setAim(const QVector3D & p) {
		aim_ = p;
		anglesFromPoints();
		buildTransform();
	}
	void move(const QVector3D & p) {
		pos_ += p;
		aim_ += p;
		buildTransform();
	}
	void move(const float & x, const float & y = 0., const float & z = 0.) {
		pos_ += QVector3D(x, y, z);
		aim_ += QVector3D(x, y, z);
		buildTransform();
	}
	void moveForward(const float & x, bool withZ = true);
	void moveBackward(const float & x, bool withZ = true) { moveForward(-x, withZ); }
	void moveLeft(const float & x, bool withZ = true);
	void moveRight(const float & x, bool withZ = true) { moveLeft(-x, withZ); }
	void moveUp(const float & x, bool onlyZ = false);
	void moveDown(const float & x, bool onlyZ = false) { moveUp(-x, onlyZ); }
	void rotateZ(const float & a);
	void rotateXY(const float & a);
	void rotateRoll(const float & a) {
		angles_.setX(angles_.x() + a);
		buildTransform();
	}
	void orbitZ(const float & a);
	void orbitXY(const float & a);
	void panZ(const float & a);
	void panXY(const float & a);
	void setFOV(const float & f) { fov_ = f; }
	void setAngles(const QVector3D & a) { setRotation(a); }
	void setAngleZ(const float & a);
	void setAngleXY(const float & a);
	void setAngleRoll(const float & a) {
		angles_.setX(a);
		buildTransform();
	}
	void setAngleLowerLimitXY(const float & a) {
		angle_limit_lower_xy = a;
		buildTransform();
	}
	void setAngleUpperLimitXY(const float & a) {
		angle_limit_upper_xy = a;
		buildTransform();
	}
	void setAngleLimitsXY(const float & lower, const float & upper) {
		angle_limit_lower_xy = lower;
		angle_limit_upper_xy = upper;
		buildTransform();
	}
	void setDepthStart(const float & d) { depth_start = d; }
	void setDepthEnd(const float & d) { depth_end = d; }
	void setMirrorX(bool yes) { mirror_x = yes; }
	void setMirrorY(bool yes) { mirror_y = yes; }
	void flyCloser(const float & s);
	void flyFarer(const float & s);
	void flyToDistance(const float & d);

	QVector3D aim() const { return aim_; }
	QVector3D angles() const { return rotation(); }
	QVector3D direction() const { return (aim_ - pos_).normalized(); }
	QVector3D directionXY() const {
		QVector3D tv = aim_ - pos_;
		return QVector3D(tv.x(), tv.y(), 0.).normalized();
	}
	float FOV() const { return fov_; }
	float distance() const { return (pos_ - aim_).length(); }
	float angleZ() const { return angles_.z(); }
	float angleXY() const { return angles_.y(); }
	float angleRoll() const { return angles_.x(); }
	float angleLowerLimitXY() const { return angle_limit_lower_xy; }
	float angleUpperLimitXY() const { return angle_limit_upper_xy; }
	float depthStart() const { return depth_start; }
	float depthEnd() const { return depth_end; }
	bool isMirrorX() const { return mirror_x; }
	bool isMirrorY() const { return mirror_y; }
	void anglesFromPoints();
	void apply(const GLfloat & aspect = 1.);
	void assign(const Camera & c);


	virtual GLObjectBase * clone(bool withChildren = true);
	QMatrix4x4 offsetMatrix() const;

private:
	QVector3D aim_, offset_;
	GLfloat fov_;
	GLfloat depth_start;
	GLfloat depth_end;
	GLfloat angle_limit_lower_xy;
	GLfloat angle_limit_upper_xy;
	bool mirror_x;
	bool mirror_y;
};

#endif // GLCAMERA_H
