/*
 *  psychlops_g_canvas.h
 *  Psychlops Standard Library (Universal)
 *
 *  Last Modified 2006/01/04 by Kenchi HOSOKAWA
 *  (C) 2006 Kenchi HOSOKAWA, Kazushi MARUYA and Takao SATO
 */

#ifndef HEADER_PSYCHLOPS_GRAPHIC_DISPLAY
#define HEADER_PSYCHLOPS_GRAPHIC_DISPLAY

#include <sstream>
#include <vector>
#include <list>

#include "psychlops_g_color.h"
#include "psychlops_g_fundamental.h"
#include "psychlops_g_shape.h"
#include "psychlops_g_font.h"

#include "../devices/psychlops_io_clock.h"
#include "../ApplicationInterfaces/psychlops_code_snippets.h"

#include "../devices/psychlops_io_display.h"

namespace Psychlops {

	class APICanvasProperties;
	class APIImageProperties;
	class ImageCache_;
	class DebugTweakTool;
	class Canvas : virtual public DrawableWithCache
	{
		friend class APICanvasProperties;
		friend class APIImageProperties;
		friend class Image;
		friend class DebugTweakTool;

	public:
		static void setDefaultCanvas(Canvas *cnvs);
	protected:
		static void resetDefaultCanvas(Canvas *cnvs);

	protected:
		Color bgcolor_;

		//	Physical Settings
		double DisplayWidthCentimeter_;
		double DisplayHeightCentimeter_;
		double DisplayDistanceCentimeter_;
		//	Display Resolution
		int DisplayHorizontalPixelNumber_;
		int DisplayVerticalPixelNumber_;

	public:
		void setPhysicalSettings(double width, double height, double distance, int horizontalpixs, int varticalpixs);
		int horizontalArcdegToPixel(double angle);
		int varticalArcdegToPixel(double angle);
		int getDisplayHorizontalPixelNumber();
		int getDisplayVerticalPixelNumber();

		void popMatrices();
		void pushPixToPixProjection();
		void RenderModeSmooth2D();
		void RenderModeSmooth3D(double left, double right, double top, double bottom, double near, double far);

	public:
		long DISPLAYED_FRAMES, FAILED_FRAMES, TOTAL_REFRESH, LAST_FAILED_FRAMES, FAILED_FLIPS;
		enum Error { MEMORY_SHORTAGE, RESET, SINGLETON };
		enum CanvasMode { fullscreen, window };
		enum TargetSurface { ALL=1, BACK=2, FRONT=4 };
		bool not_strict;

	protected:
		bool has_instance_;
		int Width, Height, colordepth;
		double refreshrate;

		Clock time_lastFrame, time_lastFrame_before_flip_, time_init_;
		int rest_wating_vsyncs_;

		double frameAccuracy;
		bool checkingFPS, checkingFPS_last, displayingFPS;
		double frameInterval, frameIntervalAccurate;
		long displayedFrames, failedFrames, lastFailedFrames_;
//		GLenum default_drawing_mode_;
		Rectangle clear_area_;

		Rectangle progressbarrect;
		Color progressbarbordercolor, progressbarfillcolor, progressbaremptycolor, progressbarerrorcolor;

	// Pseudo Properties
	public:
		template <typename X> class READ_ONLY_ {
			friend class Canvas;
			private:
			X val_;
			void set(X val) { val_=val; }
			public:
			READ_ONLY_() {}
			operator X() const { return val_; }
		};
		READ_ONLY_<double> RefreshRate;
		class READ_ONLY_POINT_ {
			friend class Canvas;
			public:
			READ_ONLY_<double> x, y;
			READ_ONLY_POINT_();
			operator Point() const;
		};
		READ_ONLY_POINT_ center;

	protected:
		APICanvasProperties *api;
		void (*APIsetColor)(const Color &col);
		void (*APIsetColorClear)(const Color &col);
/*
		static GLuint font_LCD[128];
		static GLuint fillOvalPrimitives[4], drawOvalPrimitives[4];
*/		static int font_LCD_height;
		static void initPrimitives();
		static void setfontLCD();
		static void setfontMinimum();
		static void setFillOvalPrimitives();
		void loadFontMinimum();


		////////	Construct and Destruct  ////////
		void initProperties();
		void initCounters();
		virtual void initAPIprop();
		virtual void initAPIattributes();
		virtual void popAPIattributes();

	private:
		Canvas(const Canvas&);
	public:
		Canvas();
		Canvas(CanvasMode mode, const Display disp = Display::primary);
		Canvas(int d_width, int d_height, CanvasMode mode, const Display disp = Display::primary);
		Canvas(Rectangle rect, CanvasMode mode = Canvas::window);
		Canvas(int d_width, int d_height, int d_colordepth, double d_refreshrate, const Display disp = Display::primary);
		~Canvas(void);
		void set(CanvasMode mode, const Display disp = Display::primary);
		void set(int d_width, int d_height, CanvasMode mode, const Display disp = Display::primary);
		void set(Rectangle rect, CanvasMode mode = Canvas::window);
		void set(int d_width, int d_height, int d_colordepth, double d_refreshrate, const Display disp = Display::primary);
		void release();



		////////	Critical Operations ////////

		void flip(const int frame_duration_by_vsyncs = 1);
		void flipAndWait(const int frame_duration_by_vsyncs);
		void flipAfter(const int vsyncs);
		private:
		void flip(float N);
		void flip(double N);
		public:
			//	obsolete
			void displayFrame(void);
			void displayFrameWithWait(int N_vsyncs_after_latest_flip);
			private:
			void displayFrameWithWait(double N);
		public:
		Canvas& clear(const Color &col);
		Canvas& clear(TargetSurface target = BACK);
		Canvas& clear(const Color &col, TargetSurface target);
		void setClearColor(Color col);
		void setGammaValue(const double gamma_r, const double gamma_g, const double gamma_b);
		void setGammaTable(const std::vector<double> &table_r, const std::vector<double> &table_g, const std::vector<double> &table_b);
//		void setGammaTable(const Matrix &table_all);



		////////	Drawing Graphical Elements   ////////
		protected:
		inline void setStrokeState(const Stroke& strk);
		public:
		virtual Drawable& pix(const double x, const double y, const Color &col);
		virtual Canvas& pix(const Point &po, const Color &col);
		void pix(int dotsCnt, double x[], double y[], const Color col[]);
		void pix(int dotsCnt, double x[], double y[], const Color &col);
		void pix(int dotsCnt, const Point po[], const Color col[]);
		void line(double x1, double y1, double x2, double y2, const Color &col);
		void line(const Point &po1, const Point &po2, const Color &col);
		void line(double x1, double y1, double x2, double y2, const Stroke &strk);
		void line(const Point &po1, const Point &po2, const Stroke &strk);
		virtual Canvas& line(const Line &drawee, const Color &col);
		virtual Canvas& line(const Line &drawee, const Stroke &strk);
		virtual Canvas& rect(const Rectangle &drawee, const Color &col);
		Canvas& rect(const int nRects, const Rectangle *rec, const Color &col);
		Canvas& rect(const int nRects, const Rectangle *rec, const Color *col);
		Canvas& rect(const unsigned int nRectsBegin, const unsigned int nRectsEnd, const Rectangle *rec, const Color *col);
		virtual Canvas& rect(const Rectangle &drawee, const Stroke &strk);
		Canvas& rect(const int nRects, const Rectangle *rec, const Stroke &strk);
		Canvas& rect(const int nRects, const Rectangle *rec, const Stroke *strk);
		Canvas& rect(const unsigned int nRectsBegin, const unsigned int nRectsEnd, const Rectangle *rec, const Stroke *strk);
		Canvas& oval(const Ellipse &drawee, const Color &col);
		Canvas& oval(const Ellipse &drawee, const Stroke &strk);
		Canvas& oval(const Rectangle &rect, const Color &col);
		Canvas& oval(const Rectangle &rect, const Stroke &strk);
		virtual Canvas& ellipse(const Ellipse &drawee, const Color &col);
		virtual Canvas& ellipse(const Ellipse &drawee, const Stroke &strk);
		Canvas& ellipse(const Rectangle &rect, const Color &col);
		Canvas& ellipse(const Rectangle &rect, const Stroke &strk);
		void polygon(Point *vertices, unsigned int nVertices, const Color &col);
		void polygon(Point *vertices, unsigned int nVertices, const Stroke &strk);
		virtual Canvas& polygon(const Polygon &drawee);
		virtual Canvas& polygon(const Polygon &drawee, const Color &col);
		virtual Canvas& polygon(const Polygon &drawee, const Stroke &strk);
		virtual Canvas& polyline(const PolyLine &drawee);
		virtual Canvas& polyline(const PolyLine &drawee, const Color &col);
		virtual Canvas& polyline(const PolyLine &drawee, const Stroke &strk);
		virtual Canvas& figures(const Group &drawee);

		Canvas& drawPixel(const double x, const double y, const Color &col);
		Canvas& drawPixel(const Point &po, const Color &col);
		void drawPixel(int dotsCnt, double x[], double y[], const Color col[]);
		void drawPixel(int dotsCnt, double x[], double y[], const Color &col);
		void drawPixel(int dotsCnt, const Point po[], const Color col[]);
		void drawLine(double x1, double y1, double x2, double y2, const Color &col);
		void drawLine(const Point &po1, const Point &po2, const Color &col);
		Canvas& fillRect(const Rectangle &rect, const Color &col);
		Canvas& fillRect(const int nRects, const Rectangle *rec, const Color &col);
		Canvas& fillRect(const int nRects, const Rectangle *rec, const Color *col);
		Canvas& fillRect(const unsigned int nRectsBegin, const unsigned int nRectsEnd, const Rectangle *rec, const Color *col);
		Canvas& fillOval(const Rectangle &rect, const Color &col);
		void drawPolygon(Point *vertices, unsigned int nVertices, const Color &col);



		void copy(const Rectangle &source, const Rectangle &target, bool doesdeletesource=false, const Color &delcol=Color::black);
		void to(Image &img, const Rectangle &srcrect) const;
//		void to(Image &img, const Rectangle &srcrect, const Rectangle &tgtrect) const;
		Canvas& image(const Image &img);
		Canvas& image(Image &img, const Rectangle &target_area, const Rectangle &source_rect);
		Canvas& image(const Image &img, const double left, const double top);
			Canvas& image(const Image &img, double alpha);
			void drawImage(const Image &img);
			void drawImage(const Image &img, const double left, const double top);
			void drawImage(Image &img, const Rectangle &target_area);
			//void drawImage(const Image &img, Point lt, Point rt, Point rb, Point lb);
		virtual void cacheImage(Image &img);
		virtual void uncacheImage(Image &img);
	protected:
		virtual void cacheImageBody(Image &img);
		virtual void uncacheImageBody(Image &img);
	public:


		////////	Utilities   ////////

		//	FPS Checker
		void displayFPS();
		void initFPS();
		void watchFPS();
		void ignoreFPS();
		void showFPS();
		void hideFPS();
		int lastFailedFrames();
			//	obsolete
			void enableFPSChecker();	//	obsolete
			void disableFPSChecker();	//	obsolete
			void enableDisplayingFPS();	//	obsolete
			void disableDisplayingFPS();	//	obsolete

		void progressbar(Rectangle &inrect, Color fillcol=Color::blue, Color empcol=Color(0.25,0.25,0.25));
		void progressbar(double l, double t, double r, double b, Color fillcol=Color::blue, Color empcol=Color(0.25,0.25,0.25));
		void progressbar(double percentage);
		template <typename X> void progressbar(X now, X max) { progressbar((double)now/(double)max); }

//		void var(int number, double x, double y, Color col=Color::white, bool fillForward=false);
		void var_(std::string str, double x, double y, const Color &col=Color::white, bool fillForward = false);
		template <typename X> void var(X val, double x, double y, const Color &col=Color::white, bool fillForward = false) {
			std::stringstream var_buffer_("");
 			var_buffer_ << val;
			std::string str = var_buffer_.str();
			var_(str, x, y, col, fillForward);
		}
//		void msg(const char* string, const double x, const double y, const Color col=Color::white);
//		void msg(const std::string &string, const double x, const double y, const Color col=Color::white);
		int msg(const char* string, const double x, const double y, const Color &col=Color::white, const int horiz_align=Letters::TEXT_ALIGN_LEFT, const double max_width=-1.0);
		int msg(const std::string &string, const double x, const double y, const Color &col=Color::white, const int horiz_align=Letters::TEXT_ALIGN_LEFT, const double max_width=-1.0);
		template <typename X> void checkVar(const X val, const double x, const double y, const Color &col=Color::white, const bool fillForward=false) {
			var(val, x, y, col, fillForward);
		}
//		void showMessage(const char* string, const double x, const double y, Color col=Color::white);
//		void showMessage(const std::string &string, const double x, const double y, Color col=Color::white);
		int showMessage(const char* string, const double x, const double y, const Color &col=Color::white, const int align=Letters::TEXT_ALIGN_LEFT, const double max_width=-1.0);
		int showMessage(const std::string &string, const double x, const double y, const Color &col=Color::white, const int align=Letters::TEXT_ALIGN_LEFT, const double max_width=-1.0);
			//	obsolete
			template <typename X> void varcheck(const X val, const double x, const double y, const Color &col=Color::white, const bool fillForward=false) {	//	obsolete
				var(val, x, y, col, fillForward);
			}
			void message(const char* string, const double x, const double y, const Color &col=Color::white);	//	obsolete

		Canvas& letters(Letters &letters, const Color &col=Color::white);
		int msg(Letters &letters, const double x, const double y, const Color &col=Color::white, const int horiz_align=Letters::TEXT_ALIGN_LEFT, const double max_width=-1.0);
		int msg(const std::wstring &string, const double x, const double y, const Color &col=Color::white, const int horiz_align=Letters::TEXT_ALIGN_LEFT, const double max_width=-1.0);
		void cacheLetters(Letters &letters);
		void uncacheLetters(Letters &letters);

		protected:
		std::list<Letters*> letter_cache;
		typedef std::map<Image*, ImageCache_*> ImageCache;
		mutable ImageCache img_caches;
		typedef std::map<Letters*, Letters::Cache*> LettersCache;
		mutable LettersCache let_caches;

		public:
		std::vector<Figure *> internal_billboard;
		public:
		std::vector<Figure *> billboard;


		void shift(int x, int y);


		////////	Properties	////////

		virtual int getWidth() const;
		virtual int getHeight() const;
		int getColorDepth() const;
		double getRefreshRate() const;
		double getHcenter() const;
		double getVcenter() const;
		virtual const Point getCenter() const;
		Point mouse() const;
		Point mouse(const Point &pp) const;




		////////////////	Special Utility	////////////////
		struct Geometry {
			double dist_cm, horiz_cm, vert_cm, horiz_pix, vert_pix, horiz_pix_per_cm, vert_pix_per_cm;
			Geometry();
			Geometry(double dist, double horiz, double vert, Canvas &cnvs);
			const double pixels(double visual_angle);
		};

	};
#ifdef __GNUC__
extern template void Canvas::var<int>(int val, double x, double y, const Color &col, bool fillForward);
extern template void Canvas::var<double>(double val, double x, double y, const Color &col, bool fillForward);
#endif



}	/*	<- namespace Psycholops 	*/


#endif
