#define NEWIMAGE

enum ImageKind {
	IMAGE_UNKNOWN,
	IMAGE_EMPTY, // deprecated
	IMAGE_ALPHA,
	IMAGE_MASK, // deprecated
	IMAGE_OPAQUE,
};

inline void Fill(RGBA *t, RGBA c, size_t n)     { memset32(t, *(dword *)&c, n); }
void FillDown(RGBA *t, int linecy, RGBA c, int cy);
inline void Copy(RGBA *t, const RGBA *s, int n) { memcpy_t(t, s, n); }

force_inline RGBA Premultiply(const RGBA& s)
{
	RGBA t;
	int alpha = s.a + (s.a >> 7);
	t.r = alpha * (s.r) >> 8;
	t.g = alpha * (s.g) >> 8;
	t.b = alpha * (s.b) >> 8;
	t.a = s.a;
	return t;
}

force_inline RGBA Unmultiply(const RGBA& s)
{
	extern int um_table__[256];
	RGBA t;
	int alpha = um_table__[s.a];
	t.r = (alpha * s.r) >> 8;
	t.g = (alpha * s.g) >> 8;
	t.b = (alpha * s.b) >> 8;
	t.a = s.a;
	return t;
}

int  Premultiply(RGBA *t, const RGBA *s, size_t len);
int  Unmultiply(RGBA *t, const RGBA *s, size_t len);

void TransformComponents(RGBA *t, const RGBA *s, int len,
	const byte r[], const byte g[], const byte b[], const byte a[]);
void MultiplyComponents(RGBA *t, const RGBA *s, int len, int num, int den = 256);

void AlphaBlend(RGBA *t, const RGBA *s, int len);
void AlphaBlend(RGBA *t, const RGBA *s, int len, Color color);

void AlphaBlendOpaque(RGBA *t, const RGBA *s, int len, Color color);
void AlphaBlendOpaque(RGBA *t, const RGBA *s, int len);

void AlphaBlendStraight(RGBA *b, const RGBA *f, int len);
void AlphaBlendStraight(RGBA *b, const RGBA *f, int len, Color color);
void AlphaBlendStraightOpaque(RGBA *t, const RGBA *s, int len);
void AlphaBlendStraightOpaque(RGBA *t, const RGBA *s, int len, int alpha);

int  GetChMaskPos32(dword mask);
void AlphaBlendOverBgST(RGBA *b, RGBA bg, int len);

const byte *UnpackRLE(RGBA *t, const byte *src, int len);
String      PackRLE(const RGBA *s, int len);

inline int  Grayscale(int r, int g, int b) { return (77 * r + 151 * g + 28 * b) >> 8; }
inline int  Grayscale(const RGBA& c)       { return Grayscale(c.r, c.g, c.b); }

class  Image;

class ImageBuffer : NoCopy {
	std::atomic<int> kind; // atomic because it can be set by 2 threads, in theory
	Size         size;
	Buffer<RGBA> pixels;
	Point        hotspot;
	Point        spot2;
	Size         dots;
	bool         paintonce = false;

	void         Set(Image& img);
	void         DeepCopy(const ImageBuffer& img);

	RGBA*        Line(int i) const      { ASSERT(i >= 0 && i < size.cy); return (RGBA *)~pixels + i * (size_t)size.cx; }
	friend void  DropPixels___(ImageBuffer& b) { b.pixels.Clear(); }
	void         InitAttrs();

	friend class Image;

public:
	void  SetKind(int k)                { kind = k; }
	int   GetKind() const               { return kind; }
	int   ScanKind() const;
	int   GetScanKind() const           { return kind == IMAGE_UNKNOWN ? ScanKind() : (int)kind; }

	void  SetHotSpot(Point p)           { hotspot = p; }
	Point GetHotSpot() const            { return hotspot; }

	void  Set2ndSpot(Point p)           { spot2 = p; }
	Point Get2ndSpot() const            { return spot2; }
	
	void  SetHotSpots(const Image& src);

	void  SetDots(Size sz)              { dots = sz; }
	Size  GetDots() const               { return dots; }
	void  SetDPI(Size sz);
	Size  GetDPI();
	
	void  CopyAttrs(const ImageBuffer& img);
	void  CopyAttrs(const Image& img);
	
	Size  GetSize() const               { return size; }
	int   GetWidth() const              { return size.cx; }
	int   GetHeight() const             { return size.cy; }
	size_t GetLength() const            { return (size_t)size.cx * size.cy; }

	RGBA *operator[](int i)             { return Line(i); }
	const RGBA *operator[](int i) const { return Line(i); }
	RGBA *operator~()                   { return pixels; }
	operator RGBA*()                    { return pixels; }
	const RGBA *operator~() const       { return pixels; }
	operator const RGBA*() const        { return pixels; }
	RGBA *Begin()                       { return pixels; }
	const RGBA *Begin() const           { return pixels; }
	RGBA *End()                         { return pixels + GetLength(); }
	const RGBA *End() const             { return pixels + GetLength(); }
	RGBA *begin()                       { return pixels; }
	const RGBA *begin() const           { return pixels; }
	RGBA *end()                         { return pixels + GetLength(); }
	const RGBA *end() const             { return pixels + GetLength(); }

	void  Create(int cx, int cy);
	void  Create(Size sz)               { Create(sz.cx, sz.cy); }
	bool  IsEmpty() const               { return (size.cx | size.cy) == 0; }
	void  Clear()                       { Create(0, 0); }

	void  PaintOnceHint(bool b = true)  { paintonce = b; }
	bool  IsPaintOnceHint() const       { return paintonce; }

	void  operator=(Image& img);
	void  operator=(ImageBuffer& img);

	ImageBuffer()                       { Create(0, 0); }
	ImageBuffer(int cx, int cy)         { Create(cx, cy); }
	ImageBuffer(Size sz)                { Create(sz.cx, sz.cy); }
	ImageBuffer(Image& img);
	ImageBuffer(ImageBuffer& b);
// BW, defined in CtrlCore:
	ImageBuffer(ImageDraw& iw);
};

void Premultiply(ImageBuffer& b);
void Unmultiply(ImageBuffer& b);

class Image : public ValueType< Image, 150, Moveable_<Image> > {
private:
	struct Data {
		Atomic refcount;
		int64  serial;
		uint64 aux_data;
		int    paintcount;

		void   NewSerial();
		void   Retain()  { AtomicInc(refcount); }
		void   Release() { if(AtomicDec(refcount) == 0) delete this; }
		
		ImageBuffer buffer;
		bool        paintonly;
		
		int         GetKind();

		Data(ImageBuffer& b);
	};

	Data *data;

	void Set(ImageBuffer& b);

	friend class  ImageBuffer;
	friend struct Data;
	friend class  SystemDraw;
	friend void   SetPaintOnly__(Image& img)   { img.data->paintonly = img.data->refcount == 1; }
	friend void   SysImageRealized(const Image& img);
	friend struct scImageMaker;

	void         SetAuxData(uint64 data);
	friend void  iml_ReplaceAll(Image& tgt, Image& src);

public:
	Size   GetSize() const                     { return data ? data->buffer.GetSize() : Size(0, 0); }
	int    GetWidth() const                    { return GetSize().cx; }
	int    GetHeight() const                   { return GetSize().cy; }
	size_t GetLength() const                   { return data ? data->buffer.GetLength() : 0; }
	Point  GetHotSpot() const;
	Point  Get2ndSpot() const;
	Size   GetDots() const;
	Size   GetDPI() const;
	int    GetKindNoScan() const;
	int    GetKind() const;
	bool   IsOpaque() const                    { return GetKind() == IMAGE_OPAQUE; }

	const RGBA *Begin() const                  { return data ? ~data->buffer : NULL; }
	const RGBA *begin() const                  { return Begin(); }
	const RGBA *End() const                    { return Begin() + GetLength(); }
	const RGBA *end() const                    { return End(); }
	const RGBA* operator~() const              { return Begin(); }
	operator const RGBA*() const               { return Begin(); }
	const RGBA* operator[](int i) const        { ASSERT(data); return data->buffer[i]; }

	int64 GetSerialId() const                 { return data ? data->serial : 0; }
	bool  IsSame(const Image& img) const      { return GetSerialId() == img.GetSerialId(); }

	bool   operator==(const Image& img) const;
	bool   operator!=(const Image& img) const;
	hash_t GetHashValue() const;
	String ToString() const;

	void  Serialize(Stream& s);
	void  Xmlize(XmlIO& xio)                  { XmlizeBySerialize(xio, *this); }
	void  Jsonize(JsonIO& jio)                { JsonizeBySerialize(jio, *this); }
	void  Clear();

	Image& operator=(const Image& img);
	Image& operator=(ImageBuffer& img);

	bool IsNullInstance() const         { Size sz = GetSize(); return (sz.cx|sz.cy) == 0; }

	bool IsEmpty() const                { return IsNullInstance(); }
	operator Value() const              { return RichToValue(*this); }
	
	bool IsPaintOnly() const            { return data && data->paintonly; }
	bool IsPaintOnceHint() const        { return data && data->buffer.IsPaintOnceHint(); }

	Image()                             { data = NULL; }
	Image(const Nuller&)                { data = NULL; }
	Image(const Value& src);
	Image(const Image& img);
	Image(Image (*fn)());
	Image(ImageBuffer& b);
	~Image();

	// Defined in CtrlCore or by Rainbow:
	static Image Arrow();
	static Image Wait();
	static Image IBeam();
	static Image No();
	static Image SizeAll();
	static Image SizeHorz();
	static Image SizeVert();
	static Image SizeTopLeft();
	static Image SizeTop();
	static Image SizeTopRight();
	static Image SizeLeft();
	static Image SizeRight();
	static Image SizeBottomLeft();
	static Image SizeBottom();
	static Image SizeBottomRight();
	static Image Cross();
	static Image Hand();
	
	// standard mouse cursor support
	
	uint64       GetAuxData() const;

	// required by system image cache managemenent
	
	int GetRefCount() const         { return data ? (int)data->refcount : 0; }
};

Image Premultiply(const Image& img);
Image Unmultiply(const Image& img);

struct ImageIml : Moveable<ImageIml> {
	Image  image;
	dword  flags = 0;
};

Vector<ImageIml> UnpackImlDataUncompressed(const String& data);
Vector<ImageIml> UnpackImlData(const void *ptr, int len);
Vector<ImageIml> UnpackImlData(const String& d);

enum {
	GUI_MODE_NORMAL   = 0,
	GUI_MODE_DARK     = 1,
	GUI_MODE_UHD      = 2,
	GUI_MODE_DARK_UHD = 3,
};

enum {
	IML_IMAGE_FLAG_FIXED        = 0x1,
	IML_IMAGE_FLAG_FIXED_COLORS = 0x2,
	IML_IMAGE_FLAG_FIXED_SIZE   = 0x4,
	IML_IMAGE_FLAG_UHD          = 0x8,
	IML_IMAGE_FLAG_DARK         = 0x10,
	IML_IMAGE_FLAG_S3           = 0x20,
};

Image MakeImlImage(const String& id, Function<ImageIml (int, const String&)> GetRaw, dword global_flags);

class Iml {
	struct IImage : Moveable<IImage> {
		std::atomic<bool>  loaded;
		Image              image;

		IImage() { loaded = false; }
	};
	struct Data : Moveable<Data> {
		const char *data;
		int   len, count;
	};
	Vector<Data> data[4]; // 0 normal, 1 HiDPI - HD, 2 DK - Dark, 3 HDK - HiDPI + dark
	VectorMap<String, IImage> map;
	const char **name;
	dword global_flags = 0;
	bool  premultiply;

	Index<String> ex_name[3]; // 0 HiDPI - HD, 1 DK - Dark, 2 HDK - HiDPI + dark

	void  Init(int n);

public:
	void   Reset();
	void   Skin();
	int    GetCount() const                  { return map.GetCount(); }
	String GetId(int i) const                { return map.GetKey(i); }
	Image  Get(int i);
	int    Find(const String& id) const      { return map.Find(id); }
	void   Set(int i, const Image& img);

	ImageIml GetRaw(int mode, int i); // tries to get image for mode, can return Null
	ImageIml GetRaw(int mode, const String& id); // tries to get image for mode by id, can return Null

// these methods serve for .iml import
	Iml(const char **name, int n);//Deprecated - legacy .iml
	void AddData(const byte *data, int len, int count, int mode = 0);
	void AddId(int mode1, const char *name);
	void Premultiplied()                   { premultiply = false; }
	void GlobalFlag(dword f)               { global_flags |= f; }
	
	static void ResetAll(); // clears all .iml caches
	static void SkinAll(); // reskins all .iml caches
};

void   Register(const char *imageclass, Iml& iml);

int    GetImlCount();
String GetImlName(int i);
Iml&   GetIml(int i);
int    FindIml(const char *name);
Image  GetImlImage(const char *name);
void   SetImlImage(const char *name, const Image& m);

String StoreImageAsString(const Image& img);
Image  LoadImageFromString(const String& s);
Size   GetImageStringSize(const String& src);
Size   GetImageStringDots(const String& src);

#include "Raster.h"
#include "ImageOp.h"
#include "SIMD.h"
