/*
 *  psychlops_g_image.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_IMAGE
#define HEADER_PSYCHLOPS_GRAPHIC_IMAGE

#include <string>



#include "../math/psychlops_math.h"
#include "psychlops_g_fundamental.h"
#include "psychlops_g_color.h"
#include "psychlops_g_shape.h"
#include "psychlops_g_canvas.h"
#include "psychlops_g_font.h"


namespace Psychlops {

namespace IMAGE_FORMATS {
	class IMAGE_FORMAT {
		protected:
		float * target_bitmap_f_;
		unsigned char * target_bitmap_ub_;
		unsigned int target_bytes_per_line_;
		unsigned int pix_components_;
		unsigned int pix_precision_;

		public:
		virtual ~IMAGE_FORMAT();
		virtual void load(const char *file_name, Image * target) = 0;
		virtual void save(const char *file_name, Image * target) = 0;
		protected:
		void readTargetMemoryAlignment(Image * target);
	};
}


	class APIImageCache;
	class APIImageProperties;
	class APIFontProperties;
	class ImageManipulator;
	class ShaderAPI;
	namespace Devices
	{
		class CanvasBits;
	}

	class ImageCache_ {
		friend class Canvas;
		friend class ShaderAPI;
		friend class Devices::CanvasBits;
		APIImageCache* id;
		bool dirty;
	public:
		ImageCache_();
		ImageCache_(APIImageCache* id__, bool dirty__);
	};

//	class Image : public Drawable {
	class Image : virtual public Figure {
		friend class APICanvasProperties;
		friend class APIImageProperties;
		friend class APIFontProperties;
		friend class ImageManipulator;
		friend class IMAGE_FORMATS::IMAGE_FORMAT;
		friend class ShaderAPI;
		friend class Canvas;
		friend class Recangle;
		friend class Devices::CanvasBits;

		public:
		enum PixelComponentsCnt {
			GRAY=0, RGB=1, RGBA=2
		};
		enum PixelComponentsPrecision {
			BYTE=0, FLOAT=1
		};
		static const int PixCompSize_[3];
		static const int PixPrecSize_[2];


		protected:
		APIImageProperties *api_;
		typedef std::map<DrawableWithCache*, ImageCache_> CacheID;
		mutable CacheID caches;


		inline static unsigned char round8bit(double value) {
			double val = value*255.0+0.5, integer_part, frac;
			frac = modf(val, &integer_part);
			if(frac!=0.0) return (unsigned char)integer_part;
			if((int)(integer_part)%2==0) return (unsigned char)integer_part; else return (unsigned char)integer_part-1;
		}
		public:
		inline void pix_direct(int x, int iy, double l)
		{
			int offset = (height_-iy-1)*lineValue_ + x*PixCompSize_[pixcomp_];
			if(pixprec_ == Image::BYTE) {
				unsigned char *bitmap = bitmapub_ + offset;
				*(bitmap) = round8bit(l);
				if(pixcomp_==Image::GRAY) return; else *(++bitmap) = round8bit(l);
				*(++bitmap) = round8bit(l);
				if(pixcomp_==Image::RGB) return; else *(++bitmap) = 255;
			} else {
				float *bitmapf = bitmapf_ + offset;
				*(bitmapf) = l;
				if(pixcomp_==Image::GRAY) return; else *(++bitmapf) = l;
				*(++bitmapf) = l;
				if(pixcomp_==Image::RGB) return; else *(++bitmapf) = 1;
			}
		}
		inline void pix_direct(int x, int iy, int l)
		{
			int offset = (height_-iy-1)*lineValue_ + x*PixCompSize_[pixcomp_];
			if(pixprec_ == Image::BYTE) {
				unsigned char *bitmap = bitmapub_ + offset;
				*(bitmap) = l;
				if(pixcomp_==Image::GRAY) return; else *(++bitmap) = l;
				*(++bitmap) = l;
				if(pixcomp_==Image::RGB) return; else *(++bitmap) = 255;
			} else {
				float *bitmapf = bitmapf_ + offset;
				*(bitmapf) = l/255.0;
				if(pixcomp_==Image::GRAY) return; else *(++bitmapf) = l/255.0;
				*(++bitmapf) = l/255.0;
				if(pixcomp_==Image::RGB) return; else *(++bitmapf) = 1.0;
			}
		}
		inline void pix_direct(int x, int iy, double r, double g, double b, double a)
		{
			int offset = (height_-iy-1)*lineValue_ + x*PixCompSize_[pixcomp_];
			if(pixprec_ == Image::BYTE) {
				unsigned char *bitmap = bitmapub_ + offset;
				*(bitmap) = round8bit(r);
				if(pixcomp_==Image::GRAY) return; else *(++bitmap) = round8bit(g);
				*(++bitmap) = round8bit(b);
				if(pixcomp_==Image::RGB) return; else *(++bitmap) = a;
			} else {
				float *bitmapf = bitmapf_ + offset;
				*(bitmapf) = r;
				if(pixcomp_==Image::GRAY) return; else *(++bitmapf) = g;
				*(++bitmapf) = b;
				if(pixcomp_==Image::RGB) return; else *(++bitmapf) = a;
			}
		}
		inline void pix_direct(int x, int iy, const Color &c)
		{
			pix_direct(x,iy, c.Red, c.Green, c.Blue, c.Alpha);
		}
		inline void pix_direct(int x, int iy, int r, int g, int b, int a)
		{
			int offset = (height_-iy-1)*lineValue_ + x*PixCompSize_[pixcomp_];
			if(pixprec_ == Image::BYTE) {
				unsigned char *bitmap = bitmapub_ + offset;
				*(bitmap) = r;
				if(pixcomp_==Image::GRAY) return; else *(++bitmap) = g;
				*(++bitmap) = b;
				if(pixcomp_==Image::RGB) return; else *(++bitmap) = a;
			} else {
				float *bitmapf = bitmapf_ + offset;
				*(bitmapf) = r/255.0;
				if(pixcomp_==Image::GRAY) return; else *(++bitmapf) = g/255.0;
				*(++bitmapf) = b/255.0;
				if(pixcomp_==Image::RGB) return; else *(++bitmapf) = a;
			}
		}

		protected:
		int width_, height_;
		PixelComponentsCnt pixcomp_;
		PixelComponentsPrecision pixprec_;
		//int VRAMtop_, VRAMleft_;
		Rectangle area_, localarea_, targetarea_;

		//bool quickened_;
		bool have_instance_;
		mutable float *bitmapf_;
		mutable unsigned char *bitmapub_;
		unsigned int pixBytes_, lineBytes_, bitmapBytes_, lineValue_, bitmapValue_;
		static int lineBytesAlingnment_;
		static bool setWithoutClear_; // make it local?

		void (* pix_)(const Image &, const int, const int, const Color&);
		void (* pix_direct_)(const Image &, const int, const int, const Color&);
		void (* pix_alpha_)(const Image &, const int, const int, const double);
		Color (* getpix_)(const Image &, const int, const int);
		static void line_direct_copy_(const Image &src, Image &tgt, const int iy, const int jy=-1);


		private:
		Image& operator =(const Image &source);
		Image(const Image &readimage);
		Image(const Image &readimage, bool dummy);
		inline void initPointersNull();
		void releasebitmap();
		const void * getBitmapPtr() const;

		public:
		Image();
		Image(std::string filename);
		Image(const char * filename);
		Image(Rectangle rectangle, PixelComponentsCnt pixcompcntval=RGB, PixelComponentsPrecision pixcompprec=BYTE);
		Image(Rectangle rectangle, PixelComponentsPrecision pixcompprec);
		Image(long x, long y, PixelComponentsCnt pixcompcntval=RGB, PixelComponentsPrecision pixcompprec=BYTE);
		Image(long x, long y, PixelComponentsPrecision pixcompprec);
		void set(long x, long y, PixelComponentsCnt pixcompcntval=RGB, PixelComponentsPrecision pixcompprec=BYTE);
		void set(long x, long y, PixelComponentsPrecision pixcompprec);
		void set(Rectangle rectangle, PixelComponentsCnt pixcompcntval=RGB, PixelComponentsPrecision pixcompprec=BYTE);
		void set(Rectangle rectangle, PixelComponentsPrecision pixcompprec);
		static void setWithoutClear(bool y);
		~Image(void);
		void release();
		bool hasInstance() const;
		Image dup() const;
		Image duplicate() const;
		Image& convert(PixelComponentsCnt pixcompcntval);
		Image& convertColorCalibration(bool on_off);

//void pix_ub_bits_mono_(int ix, int iy, double lum);
		Image& clear(const Color &col=Color::black);
		inline Image& pix(const int x, const int y, const Color &col) { pix_(*this, x, y, col); return *this; }
		inline Image& pix(const double x, const double y, const Color &col) { return pix(Math::round(x), Math::round(y), col); }
		inline Image& pix(const Point &po, const Color &col) { return pix(Math::round(po.x), Math::round(po.y), col); }
		inline Image& pix_raw(const int x, const int y, const Color &col) { pix_direct_(*this, x, y, col); return *this; }
		inline Image& alpha(const int x, const int y, const double a) { pix_alpha_(*this, x, y, a); return *this; }
		Image& alpha(const double a);
		Image& alpha(const Matrix &a);
		void line(const double x1, const double y1, const double x2, const double y2, const Color &col);
		void line(const Point &po1, const Point &po2, const Color &col);
		void rect(const Rectangle &rectnagle, const Color &col);
		void ellipse(const Rectangle &rect, const Color &col);
			// aliases
			void oval(const Rectangle &rect, const Color &col);
			void fillRect(const Rectangle &rectangle, const Color &col);
			void drawText(int x, int y, const char* str, int strlength);
//				void msg(Letters &letters, int x, int y, const Color &col);

		virtual Image& draw(double left, double top, Drawable &target = *Drawable::prime);
		virtual Image& draw(Drawable &target = *Drawable::prime);
		virtual Image& draw(double alpha, Drawable &target = *Drawable::prime);
			//obsolete
			void display(double left, double top);
			void display();
		virtual Image& cache(DrawableWithCache &target = *DrawableWithCache::prime);
		virtual Image& uncache(DrawableWithCache &target = *DrawableWithCache::prime);
		inline Image& quicken(bool on_off=true) { if(on_off) return cache(); else return uncache(); }
		inline Color getPix(int x, int y) const { return getpix_(*this, x, y); }

		class PartialView : public Figure
		{
			Image *img;
			Rectangle source, target;

		public:
			PartialView(Image *src, const Rectangle &d_source);
			PartialView& set(double width, double height);
			virtual ~PartialView();
			virtual const Point getDatum() const;
			virtual PartialView& setDatum(const Point &p);
			virtual PartialView& centering(const Point &p);
			virtual PartialView& draw(Drawable &drawable = *Drawable::prime);
		};
		PartialView operator ()(const Rectangle &scope);


		virtual const Point getDatum() const;
		virtual Image& setDatum(const Point& p);
		virtual Image& centering(const Drawable& target = *Drawable::prime);
		virtual Image& centering(const Figure& fig);
		virtual Image& centering(const Point& p);
		virtual Image& centering(const double x, const double y, const double z = 0);
		virtual Image& move_to(const double x, const double y, const double z = 0);
		virtual Image& shift(const double x, const double y, const double z = 0);
		virtual int getWidth() const;
		virtual int getHeight() const;
		virtual double getHcenter() const;
		virtual double getVcenter() const;
		virtual const Point getCenter() const;
		double getLeft() const;
		double getRight() const;
		double getTop() const;
		double getBottom() const;


		float* getFloatPrt();
		unsigned char* getElementPtr();
		int getPixBytes();	// Number of bytes of a pixel
		int getLineBytes();	// Number of bytes of a line
		int getBitmapBytes();	// Number of bytes of the whole bitmap
		int getLineNumber();	// Array length of line
		int getElementNumber();	// Array length of the whole bitmap

		void to(const Rectangle &source__, Image &other, const Rectangle target__) const;
		void to(const Rectangle &source, Image &other) const;

		void load(std::string filename);
		void load(const char * filename);
		void save(std::string filename);
		void save(const char * filename);
		void from(const Image &img);
		void from(const Matrix &gray);
		void from(const Matrix &r, const Matrix &g, const Matrix &b);
		void from(const Matrix &r, const Matrix &g, const Matrix &b, const Matrix &a);
		void to(Matrix &gray) const;
		void to(Point p, int width, int height, Matrix &gray) const;
		void to(Point p, const Rectangle &rect, Matrix &gray) const;
		void to(const Interval &horiz, const Interval &vert, Matrix &gray) const;
		void to(Matrix &r, Matrix &g, Matrix &b) const;
		void to(Point p, int width, int height, Matrix &r, Matrix &g, Matrix &b) const;
		void to(Point p, const Rectangle &rect, Matrix &r, Matrix &g, Matrix &b) const;
		void to(const Interval &horiz, const Interval &vert, Matrix &r, Matrix &g, Matrix &b) const;
		void to(Matrix &r, Matrix &g, Matrix &b, Matrix &a) const;
		void to(Point p, int width, int height, Matrix &r, Matrix &g, Matrix &b, Matrix &a) const;
		void to(Point p, const Rectangle &rect, Matrix &r, Matrix &g, Matrix &b, Matrix &a) const;
		void to(const Interval &horiz, const Interval &vert, Matrix &r, Matrix &g, Matrix &b, Matrix &a) const;
		// test
			Image & operator -=(Image &rhs);
	};



}	/*	<- namespace Psycholops 	*/


#endif
