/*
    QGLView
	Copyright (C) 2017  Ivan Pelipenko peri4ko@yandex.ru

    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 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 General Public License for more details.

    You should have received a copy of the GNU 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 setPosX(const double & o) {QVector3D dv = aim_ - pos_; pos_.setX(o); aim_ = pos_ + dv;}
	void setPosY(const double & o) {QVector3D dv = aim_ - pos_; pos_.setY(o); aim_ = pos_ + dv;}
	void setPosZ(const double & o) {QVector3D dv = aim_ - pos_; pos_.setZ(o); aim_ = pos_ + dv;}*/
	void setAim(const QVector3D & p) {aim_ = p; anglesFromPoints(); buildTransform();}
	void move(const QVector3D & p) {pos_ += p; aim_ += p; buildTransform();}
	void move(const double & x, const double & y = 0., const double & z = 0.) {pos_ += QVector3D(x, y, z); aim_ += QVector3D(x, y, z); buildTransform();}
	void moveForward(const double & x, bool withZ = true);
	void moveBackward(const double & x, bool withZ = true) {moveForward(-x, withZ);}
	void moveLeft(const double & x, bool withZ = true);
	void moveRight(const double & x, bool withZ = true) {moveLeft(-x, withZ);}
	void moveUp(const double & x, bool onlyZ = false);
	void moveDown(const double & x, bool onlyZ = false) {moveUp(-x, onlyZ);}
	void rotateZ(const double & a);
	void rotateXY(const double & a);
	void rotateRoll(const double & a) {angles_.setX(angles_.x() + a); buildTransform();}
	void orbitZ(const double & a);
	void orbitXY(const double & a);
	void panZ(const double & a);
	void panXY(const double & a);
	void setFOV(const double & f) {fov_ = f;}
	void setAngles(const QVector3D & a) {setRotation(a);}
	void setAngleZ(const double & a);
	void setAngleXY(const double & a);
	void setAngleRoll(const double & a) {angles_.setX(a); buildTransform();}
	void setAngleLowerLimitXY(const double & a) {angle_limit_lower_xy = a; buildTransform();}
	void setAngleUpperLimitXY(const double & a) {angle_limit_upper_xy = a; buildTransform();}
	void setAngleLimitsXY(const double & lower, const double & upper) {angle_limit_lower_xy = lower; angle_limit_upper_xy = upper; buildTransform();}
	void setDepthStart(const double & d) {depth_start = d;}
	void setDepthEnd(const double & d) {depth_end = d;}
	void setMirrorX(bool yes) {mirror_x = yes;}
	void setMirrorY(bool yes) {mirror_y = yes;}
	void flyCloser(const double & s);
	void flyFarer(const double & s);
	void flyToDistance(const double & 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();}
	double FOV() const {return fov_;}
	double distance() const {return (pos_ - aim_).length();}
	double angleZ() const {return angles_.z();}
	double angleXY() const {return angles_.y();}
	double angleRoll() const {return angles_.x();}
	double angleLowerLimitXY() const {return angle_limit_lower_xy;}
	double angleUpperLimitXY() const {return angle_limit_upper_xy;}
	double depthStart() const {return depth_start;}
	double depthEnd() const {return depth_end;}
	bool isMirrorX() const {return mirror_x;}
	bool isMirrorY() const {return mirror_y;}
	void anglesFromPoints();
	void apply(const GLdouble & aspect = 1.);
	void assign(const Camera & c);
	//QVector3D pointFromViewport(int x_, int y_, double z_); TODO

	QMatrix4x4 offsetMatrix() const;

private:
	//void localTransform(QMatrix4x4 & m);
	
	QVector3D aim_, offset_;
	GLdouble fov_;
	GLdouble depth_start;
	GLdouble depth_end;
	GLdouble angle_limit_lower_xy;
	GLdouble angle_limit_upper_xy;
	//GLdouble modelview[16], projection[16];
	//GLint viewport[4];
	bool mirror_x;
	bool mirror_y;

};

#endif // GLCAMERA_H
