#ifndef __OBJECT_IMAGE_H__
#define __OBJECT_IMAGE_H__

#include "Object.h"
#include "Object_Color.h"
#include "Object_Palette.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Class_Image
//-----------------------------------------------------------------------------
class DLLDECLARE Class_Image : public Class {
public:
	Class_Image(Environment *pEnvOuter);
	virtual bool CastFrom(Environment &env, Signal sig, Value &value);
};

//-----------------------------------------------------------------------------
// Object_Image
//-----------------------------------------------------------------------------
class DLLDECLARE Object_Image : public Object {
#if USE_WINDOWS_DIB
protected:
	HBITMAP _hBmp;
public:
	inline HBITMAP GetHBITMAP() { return _hBmp; }
#else
protected:
	OAL::Memory _memory;
#endif
public:
	enum {
		OffsetRed	= Object_Palette::OffsetRed,
		OffsetGreen	= Object_Palette::OffsetGreen,
		OffsetBlue	= Object_Palette::OffsetBlue,
		OffsetAlpha	= Object_Palette::OffsetAlpha,
	};
public:
	enum Format {
		FORMAT_None,
		FORMAT_RGB, FORMAT_RGBA,
	};
	struct Accum {
		size_t red;
		size_t green;
		size_t blue;
		size_t alpha;
		size_t cnt;
		inline void AddRGB(const unsigned char *pPixel) {
			red   += Object_Image::GetPixelR(pPixel);
			green += Object_Image::GetPixelG(pPixel);
			blue  += Object_Image::GetPixelB(pPixel);
			cnt++;
		}
		inline void AddRGBA(const unsigned char *pPixel) {
			red   += Object_Image::GetPixelR(pPixel);
			green += Object_Image::GetPixelG(pPixel);
			blue  += Object_Image::GetPixelB(pPixel);
			alpha += Object_Image::GetPixelA(pPixel);
			cnt++;
		}
		inline void StoreRGB(unsigned char *pPixel) {
			StorePixel(pPixel,
				static_cast<unsigned char>(red),
				static_cast<unsigned char>(green),
				static_cast<unsigned char>(blue));
		}
		inline void StoreRGBA(unsigned char *pPixel) {
			StorePixel(pPixel,
				static_cast<unsigned char>(red),
				static_cast<unsigned char>(green),
				static_cast<unsigned char>(blue),
				static_cast<unsigned char>(alpha));
		}
	};
	enum ScanDir {
		SCAN_LeftTopHorz, SCAN_LeftTopVert,
		SCAN_RightTopHorz, SCAN_RightTopVert,
		SCAN_LeftBottomHorz, SCAN_LeftBottomVert,
		SCAN_RightBottomHorz, SCAN_RightBottomVert,
	};
	class DLLDECLARE Scanner {
	private:
		Object_Image *_pObjImage;
		unsigned char *_pPixel;
		size_t _iPixel, _iLine;
		size_t _nPixels, _nLines;
		int _pitchPixel;
		int _pitchLine;
	public:
		Scanner(Object_Image *pObjImage, size_t x, size_t y,
								size_t width, size_t height, ScanDir scanDir);
		~Scanner();
		inline Object_Image *GetImageObj() { return _pObjImage; }
		inline unsigned char *GetPointer() { return _pPixel; }
		inline unsigned char GetRed() const { return _pPixel[OffsetRed]; }
		inline unsigned char GetGreen() const { return _pPixel[OffsetGreen]; }
		inline unsigned char GetBlue() const { return _pPixel[OffsetBlue]; }
		inline unsigned char GetAlpha() const { return _pPixel[OffsetAlpha]; }
		inline void FwdPixel() {
			_iPixel++;
			_pPixel += _pitchPixel;
		}
		inline void BwdPixel() {
			_iPixel--;
			_pPixel -= _pitchPixel;
		}
		inline void FwdLine() {
			_iLine++;
			_pPixel += _pitchLine;
		}
		inline void BwdLine() {
			_iLine--;
			_pPixel -= _pitchLine;
		}
		inline bool NextPixel() {
			_iPixel++;
			_pPixel += _pitchPixel;
			return _iPixel < _nPixels;
		}
		inline bool NextLine() {
			_iPixel = 0;
			_iLine++;
			if (_iLine >= _nLines) return false;
			_pPixel += _pitchLine;
			return true;
		}
		inline bool Next() {
			_iPixel++;
			_pPixel += _pitchPixel;
			if (_iPixel < _nPixels) return true;
			_iPixel = 0;
			_iLine++;
			if (_iLine >= _nLines) return false;
			_pPixel += _pitchLine;
			return true;
		}
		inline bool Next(Scanner &scannerSlave) {
			_iPixel++;
			_pPixel += _pitchPixel;
			scannerSlave._pPixel += scannerSlave._pitchPixel;
			if (_iPixel < _nPixels) return true;
			_iPixel = 0;
			_iLine++;
			if (_iLine >= _nLines) return false;
			_pPixel += _pitchLine;
			scannerSlave._pPixel += scannerSlave._pitchLine;
			return true;
		}
		inline size_t CountPixels() const { return _nPixels; }
		inline size_t CountLines() const { return _nLines; }
		inline size_t GetPixelIdx() const { return _iPixel; }
		inline size_t GetLineIdx() const { return _iLine; }
		inline void StorePixel(unsigned char red, unsigned char green, unsigned char blue) {
			_pPixel[OffsetRed] = red;
			_pPixel[OffsetGreen] = green;
			_pPixel[OffsetBlue] = blue;
		}
		inline void StorePixel(unsigned char red, unsigned char green, unsigned char blue,
						unsigned char alpha) {
			_pPixel[OffsetRed] = red;
			_pPixel[OffsetGreen] = green;
			_pPixel[OffsetBlue] = blue;
			_pPixel[OffsetAlpha] = alpha;
		}
	};
	class IteratorEach : public Iterator {
	private:
		Scanner _scanner;
		bool _doneFlag;
	public:
		inline IteratorEach(Object_Image *pObjImage,
				size_t x, size_t y, size_t width, size_t height, ScanDir scanDir) :
			Iterator(false),
			_scanner(pObjImage, x, y, width, height, scanDir), _doneFlag(false) {}
		virtual ~IteratorEach();
		virtual bool DoNext(Environment &env, Signal sig, Value &value);
		virtual String ToString(Signal sig) const;
	};
public:
	AScript_DeclareObjectAccessor(Image)
protected:
	Format _format;
	size_t _width, _height;
	unsigned char *_buff;
	struct {
		size_t bitsPerPixel;
		size_t bytesPerPixel;
		size_t bytesPerLine;
	} _metrics;
	Object_Palette *_pObjPalette;
public:
	Object_Image(Environment &env, Format format);
	Object_Image(Class *pClass, Format format);
	Object_Image(const Object_Image &obj);
	virtual ~Object_Image();
	virtual Object *Clone() const;
	inline bool IsValid() const { return _buff != NULL; }
	inline Format GetFormat() const { return _format; }
	inline size_t GetWidth() const { return _width; }
	inline size_t GetHeight() const { return _height; }
	inline unsigned char *GetBuffer() { return _buff; }
	inline size_t GetBitsPerPixel() const { return _metrics.bitsPerPixel; }
	inline size_t GetBytesPerPixel() const { return _metrics.bytesPerPixel; }
	inline size_t GetBytesPerLine() const { return _metrics.bytesPerLine; }
	inline size_t GetBufferSize() const {
		return GetBytesPerLine() * _height;
	}
	inline unsigned char *GetPointer(size_t y) {
		return _buff + GetBytesPerLine() * y;
	}
	inline unsigned char *GetPointer(size_t x, size_t y) {
		return _buff + GetBytesPerLine() * y + GetBytesPerPixel() * x;
	}
	inline const unsigned char *GetPointer(size_t y) const {
		return _buff + GetBytesPerLine() * y;
	}
	inline const unsigned char *GetPointer(size_t x, size_t y) const {
		return _buff + GetBytesPerLine() * y + GetBytesPerPixel() * x;
	}
	inline Scanner *CreateScanner(size_t x, size_t y,
				size_t width, size_t height, ScanDir scanDir = SCAN_LeftTopHorz) {
		return new Scanner(dynamic_cast<Object_Image *>(Object::Reference(this)),
											x, y, width, height, scanDir);
	}
	inline Scanner *CreateScanner(ScanDir scanDir = SCAN_LeftTopHorz) {
		return new Scanner(dynamic_cast<Object_Image *>(Object::Reference(this)),
											0, 0, _width, _height, scanDir);
	}
	bool CheckEmpty(Signal sig) const;
	bool CheckValid(Signal sig) const;
	virtual Value DoPropGet(Signal sig, const Symbol *pSymbol, bool &evaluatedFlag);
	virtual Value DoPropSet(Signal sig, const Symbol *pSymbol,
									const Value &value, bool &evaluatedFlag);
	virtual String ToString(Signal sig, bool exprFlag);
	bool AllocBuffer(Signal sig, size_t width, size_t height, unsigned char fillValue);
	void FreeBuffer();
	inline bool CheckCoord(int x, int y) const {
		return 0 <= x && x < static_cast<int>(_width) &&
				0 <= y && y < static_cast<int>(_height);
	}
	bool CheckCoord(Signal sig, size_t x, size_t y) const;
	bool AdjustCoord(int &x, int &y, int &width, int &height) const;
	void PutPixel(unsigned char *buff, const Object_Color *pObjColor);
	void GetPixel(const unsigned char *buff, Object_Color *pObjColor);
	bool Store(Signal sig, size_t x, size_t y, size_t width, size_t height,
						const Symbol *pSymbol, const Object_Matrix *pObjMat);
	bool Store(Signal sig, size_t x, size_t y, size_t width, size_t height,
						const Symbol *pSymbol, Iterator *pIterator);
	bool Extract(Signal sig, size_t x, size_t y, size_t width, size_t height,
				const Symbol *pSymbol, Object_Matrix *pObjMat);
	bool Extract(Signal sig, size_t x, size_t y, size_t width, size_t height,
				const Symbol *pSymbol, Object_List *pObjList);
	void FillRect(size_t x, size_t y, size_t width, size_t height, const Object_Color *pObjColor);
	void FillRectAlpha(size_t x, size_t y, size_t width, size_t height,
				const Object_Color *pObjColor, unsigned char alphaKey, unsigned char alphaRest);
	inline void Fill(const Object_Color *pObjColor) {
		FillRect(0, 0, _width, _height, pObjColor);
	}
	inline void FillAlpha(const Object_Color *pObjColor,
				unsigned char alphaKey, unsigned char alphaRest) {
		FillRectAlpha(0, 0, _width, _height, pObjColor, alphaKey, alphaRest);
	}
	const Object_Palette *GetPaletteObj() const { return _pObjPalette; }
	Object_Image *ReduceColor(Signal sig, const Object_Palette *pObjPalette);
	Object_Image *Blur(Signal sig, int radius);
	Object_Image *Flip(Signal sig, bool horzFlag, bool vertFlag);
	Object_Image *Rotate90(Signal sig, bool clockwiseFlag);
	Object_Image *Rotate(Signal sig, double angle, const Object_Color *pObjColor);
	Object_Image *Crop(Signal sig, size_t x, size_t y, size_t width, size_t height);
	Object_Image *Resize(Signal sig, size_t width, size_t height);
	void Paste(size_t x, size_t y, Object_Image *pObj,
		size_t width, size_t height, size_t xOffset, size_t yOffset, unsigned char alpha);
	Object_Palette *CreateEmptyPalette(size_t nEntries);
	void SetPaletteObj(Object_Palette *pObjPalette);
	bool Read(Signal sig, Stream &stream, const char *imgType);
	bool Write(Signal sig, Stream &stream, const char *imgType);
	int CalcDIBBitCount() const;
	static inline size_t CalcDIBPaletteSize(int biBitCount) {
		return (biBitCount <= 8)? (1 << biBitCount) * 4 : 0;
	}
	size_t CalcDIBImageSize(int biBitCount, bool maskFlag) const;
	bool ReadDIBPalette(Signal sig, Stream &stream, int biBitCount);
	bool WriteDIBPalette(Signal sig, Stream &stream, int biBitCount);
	bool ReadDIB(Signal sig, Stream &stream,
				int biWidth, int biHeight, int biBitCount, bool maskFlag);
	bool WriteDIB(Signal sig, Stream &stream, int biBitCount, bool maskFlag);
	size_t SymbolToPixelOffset(Signal sig, const Symbol *pSymbol) const;
	static Format SymbolToFormat(Signal sig, const Symbol *pSymbol);
	static const Symbol *FormatToSymbol(Format format);
	static inline unsigned char GetPixelGray(unsigned char *buff) {
		return Color::CalcGray(GetPixelR(buff), GetPixelG(buff), GetPixelB(buff));
	}
	static inline void StorePixel(unsigned char *buff,
					unsigned char red, unsigned char green, unsigned char blue) {
		*(buff + OffsetRed) = red;
		*(buff + OffsetGreen) = green;
		*(buff + OffsetBlue) = blue;
	}
	static inline void StorePixel(unsigned char *buff,
					unsigned char red, unsigned char green, unsigned char blue,
					unsigned char alpha) {
		*(buff + OffsetRed) = red;
		*(buff + OffsetGreen) = green;
		*(buff + OffsetBlue) = blue;
		*(buff + OffsetAlpha) = alpha;
	}
	static inline void StorePixel(unsigned char *pPixelDst,
								const unsigned char *pPixelSrc, bool alphaFlag) {
		*pPixelDst++ = *pPixelSrc++;
		*pPixelDst++ = *pPixelSrc++;
		*pPixelDst++ = *pPixelSrc++;
		if (alphaFlag) *pPixelDst++ = *pPixelSrc++;
	}
	static inline void StorePixelRGB(unsigned char *pPixelDst,
													const unsigned char *pPixelSrc) {
		*pPixelDst++ = *pPixelSrc++;
		*pPixelDst++ = *pPixelSrc++;
		*pPixelDst++ = *pPixelSrc++;
	}
	static inline void StorePixelRGBA(unsigned char *pPixelDst,
													const unsigned char *pPixelSrc) {
		*pPixelDst++ = *pPixelSrc++;
		*pPixelDst++ = *pPixelSrc++;
		*pPixelDst++ = *pPixelSrc++;
		*pPixelDst++ = *pPixelSrc++;
	}
	static inline unsigned char GetPixelR(const unsigned char *buff) {
		return *(buff + OffsetRed);
	}
	static inline unsigned char GetPixelG(const unsigned char *buff) {
		return *(buff + OffsetGreen);
	}
	static inline unsigned char GetPixelB(const unsigned char *buff) {
		return *(buff + OffsetBlue);
	}
	static inline unsigned char GetPixelA(const unsigned char *buff) {
		return *(buff + OffsetAlpha);
	}
	static inline void RotateCoord(int &xm, int &ym,
							int x, int y, int cos1024, int sin1024) {
		xm = (x * cos1024 - y * sin1024) >> 10;
		ym = (x * sin1024 + y * cos1024) >> 10;
	}
private:
	void InitMetrics();
	Object_Image *CreateDerivation(Signal sig,
				size_t width, size_t height, Object_Palette *pObjPalette = NULL);
};

//-----------------------------------------------------------------------------
// ImageStreamer
//-----------------------------------------------------------------------------
class DLLDECLARE ImageStreamer {
public:
	typedef std::vector<ImageStreamer *> List;
private:
	const char *_imgType;
	static List *_pList;
public:
	inline ImageStreamer(const char *imgType) : _imgType(imgType) {}
	inline const char *GetImgType() const { return _imgType; }
	virtual bool IsResponsible(Signal sig, Stream &stream) = 0;
	virtual bool Read(Environment &env, Signal sig, Object_Image *pObjImage, Stream &stream) = 0;
	virtual bool Write(Environment &env, Signal sig, Object_Image *pObjImage, Stream &stream) = 0;
public:
	static void Register(ImageStreamer *pImageStreamer);
	static ImageStreamer *FindResponsible(Signal sig, Stream &stream, const char *imgType);
	static ImageStreamer *FindByImgType(const char *imgType);
};

}

#endif
