/*
* This file is part of robotreality
*
* Copyright(c) sschulz <AT> techfak.uni-bielefeld.de
* http://opensource.cit-ec.de/projects/robotreality
*
* This file may be licensed under the terms of the
* GNU General Public License Version 3 (the ``GPL''),
* or (at your option) any later version.
*
* Software distributed under the License is distributed
* on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the GPL for the specific language
* governing rights and limitations.
*
* You should have received a copy of the GPL along with this
* program. If not, go to http://www.gnu.org/licenses/gpl.html
* or write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The development of this software was supported by the
* Excellence Cluster EXC 277 Cognitive Interaction Technology.
* The Excellence Cluster EXC 277 is a grant of the Deutsche
* Forschungsgemeinschaft (DFG) in the context of the German
* Excellence Initiative.
*
*/
#ifndef _HEADPROJECTOR_H_
#define _HEADPROJECTOR_H_
#include <cstdio>
#include <cv.h>
#include <highgui.h>
#include <opencv2/contrib/contrib.hpp>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <boost/signals2.hpp>
#include <libgen.h>
#include <tinyconfig.h>

#define USE_TESTIMAGE 0
#define USE_GPU 1
#define USE_GL  1

#if USE_GPU
	#include <cuda.h>
	#include <cuda_runtime.h>
	#include <opencv2/gpu/gpu.hpp>
	#include <opencv2/core/internal.hpp>
	
	#include <opencv2/core/gpumat.hpp>
	#include <opencv2/core/opengl_interop.hpp>
	using namespace cv::gpu;
#endif


#define TEST_IMAGE_FILENAME "test_image.png"

using namespace cv;

class HeadProjector{
public:
	HeadProjector(char *filename);
	~HeadProjector();
	
	void init();
	void run();
	void handle_mouse_event(int event, int x, int y, int flags);
	void handle_mouse_event2(int event, int x, int y, int flags);
	void change_selection(int id);
private:
	TinyConfig tconfig;
	void show_projection_config();
	int camera_id;

	void quit();
	void show_projection();
	void calc_projection();
	void init_calibration();
	void init_mousehandler();
	void add_projection_point(int x, int y);
	void add_lens_center(int x, int y);
	void calculate_calibration();
	void calculate_calibration(int index);
	void calculate_projection_matrix();
	void project_image(Mat in, Mat out);
	void add_lens_black_spot();
	
	Mat init_distortion_map(const Mat& src, const Mat& cameraMatrix, const Mat& distCoeffs, const Mat& zoom);
	Point2f remap_to_undistorted_image_coords(Point2f p,  Mat warp_zoom_matrix, Mat camera_matrix, Mat dist_coeffs, Mat zoom_to_full_image);
	void latency_test_set_trigger(int fd, int level);
	void latency_test_thread();
	int select_projection_point(int x, int y);
	
	
	Mat vscale;
	Mat cam_input;
	Mat image_projected;
	Mat image_unprojected;
	
	Point2f lens_center;
	Point2f remapped_lens_center;
	int selected_projection_point;
	Point2f projection_target[4];
	int projection_target_index;
	bool use_single_deformation;
	Mat new_distortion_map[3];
	
	vector<Mat> cam_input_rgbsplit;
	vector<Mat> cam_input_projected_rgbsplit;
	

 	Mat camera_matrix;
	Mat dist_coeffs;
	vector<Mat> rvecs, tvecs;
	Mat warp_zoom_matrix;
	Mat warp_zoom_matrix_inverse;

	
	Mat new_warp_zoom_matrix[3];
	Mat new_warp_zoom_matrix_inverse[3];
	Mat new_zoom_to_full_image_inverse[3];
	Mat new_zoom_to_full_image[3];
	Mat new_projection_matrix[3];
	Mat new_camera_matrix[3];
	Mat new_dist_coeffs[3];
	
	Mat debug_output_image;
	Mat cam_input_projected;
	
	
	Point2f warp_perspective_point2f(Point2f in, Mat h);
	
	Size pattern_size;
	double square_size;
	int clicked_points_count;
	
	
	vector<Point2f> clicked_points;
	Point2f mouse_pos;
	int calibration_index;

	vector<Point2f> calibration_points[3];
	Mat image_projection_config;
	
	vector<Point2f> *NO_object_points;
	
	#if USE_GPU
	GpuMat distort_x[3], distort_y[3];
	GpuMat gpu_cam_input_projected;
	GpuMat gpu_cam_input;
	vector<GpuMat> gpu_cam_input_rgbsplit;
	vector<GpuMat> gpu_cam_input_projected_rgbsplit;
	#endif
	
	TickMeter trigger_tickmeter;
	bool latency_test_trigger_detected;
	Point2f latency_test_clicked_pos;
	
	int latency_test_fd;

};


#endif