#ifndef _xbCOLOR_
#define _xbCOLOR_
/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbColor
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#ifndef _xbSTRING_
#	include "xbString.h"
#endif

#ifndef _xbENGINE_
#	include "xbEngine.h"
#endif

#ifndef _xbCONFIG_
#	include "xbConfig.h"
#endif

//TODO: merge avec gpColor

extern xbEXPORT xbCfgPath	g_xbCfgXdcUserColors;
extern xbEXPORT xbCfgPath	g_xbCfgXdcCommonColors;

#define xblowCOLOR_NULL		(uint64)(0x00000000FFFFFFFF)
#define xblowCOLOR_SYSMASK	(uint64)(0xFFFFFFFF00000000)
#define xblowCOLOR_TRGBMASK		(uint64)(0x00000000FFFFFFFF)
struct xbEXPORT xblowColor
{
	uint64 val;

	xblowColor()
	{
		val = xblowCOLOR_NULL;
	}
	xblowColor(const xblowColor &clr)
	{
		val = clr.val;
	}
	xblowColor(uint64 clr)
	{
		val = clr;
	}
	xblowColor(ulong clr)
	{
		val = clr;
	}
	xblowColor &operator=(const xblowColor &clr)
	{
		val = clr.val;
		return *this;
	}
	xblowColor &operator=(uint64 clr)
	{
		val = clr;
		return *this;
	}
	xblowColor &operator=(ulong clr)
	{
		val = clr;
		return *this;
	}

	bool IsNull() const
	{
		return val == xblowCOLOR_NULL;
	}
	bool IsValid() const
	{
		return val != xblowCOLOR_NULL;
	}
	bool IsTransparent() const
	{
		return (TRGB & 0xFF000000) == 0xFF000000;
	}
	bool HasTransparency() const
	{
		return (TRGB & 0xFF000000) != 0;
	}
	bool IsSysColor() const
	{
		return (val & xblowCOLOR_SYSMASK)?true:false;
	}
	ulong GetTRGB() const
	{
		if (!(val & xblowCOLOR_SYSMASK))
			return ulong(val);
		return sysCalc();
	}
	xbPropertyR(ulong,TRGB);

	ulong GetRGB() const
	{
		return RGBonly();
	}
	xbPropertyR(ulong,RGB);

	//Pour nous TTRRGGBB oł TT = transparency, 255=transparent, pour ms: AARRGGBB oł AA = alpha, 255=opaque
	ulong GetWinARGB() const;
	xbPropertyR(ulong,WinARGB);

	ulong RGBonly(bool bTransparencyRejected = true) const
	{
		if (!(val & xblowCOLOR_SYSMASK))
		{
			if (bTransparencyRejected && (val & 0xFF000000))
				xbThrow xbEarg("Transparency not supported here","(color:"+GetLabel()+")");
			return ulong(val & 0xFFFFFF);
		}
		ulong ret = sysCalc();
		if (bTransparencyRejected && (ret & 0xFF000000))
			xbThrow xbEarg("Transparency not supported here","(color:"+GetLabel()+")");
		return ulong(ret & 0xFFFFFF);
	}

	inline bool operator==(const xblowColor& clr2) const 
	{
		return Compare(clr2)==0;
	}
	inline bool operator!=(const xblowColor& clr2) const 
	{
		return Compare(clr2)!=0;
	}
	inline bool operator>(const xblowColor& clr2) const 
	{
		return Compare(clr2)>0;
	}
	inline bool operator<(const xblowColor& clr2) const 
	{
		return Compare(clr2)<0;
	}
	inline bool operator>=(const xblowColor& clr2) const 
	{
		return Compare(clr2)>=0;
	}
	inline bool operator<=(const xblowColor& clr2) const 
	{
		return Compare(clr2)<=0;
	}
	ulong sysCalc() const;
	void Empty()
	{
		val = xblowCOLOR_NULL;
	}
	int Compare(const xblowColor &clr2) const;
	xbString GetLabel() const
	{
		xbString ret;
		toString(&ret);
		return ret;
	}

	bool fromString(LPCWSTR str,xbOpMode opm);
	bool fromString(const xbStringA &str,xbOpMode opm);
	void toString(xbBSTR *pStr) const;
	void toString(xbStringW *pStr) const;
	void toString(xbStringA *pStr) const;
	uint64 toUint64() const {return val;}

	static bool _set(uint64 &clr,const xbString &str,xbOpMode opm =xbOp_Optional);
	bool Set(const xbString &str,xbOpMode opm =xbOp_Optional);
	static void _toRgb(uint64 &clr);
	static void _opY(uint64 &clr,char chOp,long ratio);
	static void _opU(uint64 &clr,char chOp,long ratio);
	static void _opV(uint64 &clr,char chOp,long ratio);
	static void _opR(uint64 &clr,char chOp,long ratio);
	static void _opG(uint64 &clr,char chOp,long ratio);
	static void _opB(uint64 &clr,char chOp,long ratio);
	static void _rgbToYuv(const uint64 &clr,uint64 &clrDest);
	static void _yuvToRgb(const uint64 &clr,uint64 &clrDest);

private:
	xbDclNoNewForObject(xblowColor);
};

#define xbNULLCOLOR					xblowCOLOR_NULL
#define xbSYSCOLOR(val)				(((uint64)val) << 32)
#define xbSYSCOLORget(val)			((ulong)(val >> 32))

#define xbSYSCOLORGET(clr)			(xbSYSCOLORget(clr) & 0x00000FFF)
#define xbSYSCOLORGETRPL(clr)		((xbSYSCOLORget(clr) & 0x00FFF000) >> 12)

#define xbWHATSYSCOLOR(clr)			((xbSYSCOLORget(clr) & 0x7F000000) >> 24)
#define xbNT4COLOR					0x00000000
#define xbNT5COLOR					0x05000000

#define xbCOLOR_SCROLLBAR				xbSYSCOLOR(0)
#define xbCOLOR_BACKGROUND				xbSYSCOLOR(1)
#define xbCOLOR_ACTIVECAPTION			xbSYSCOLOR(2)
#define xbCOLOR_INACTIVECAPTION			xbSYSCOLOR(3)
#define xbCOLOR_MENU					xbSYSCOLOR(4)
#define xbCOLOR_WINDOW					xbSYSCOLOR(5)
#define xbCOLOR_WINDOWFRAME				xbSYSCOLOR(6)
#define xbCOLOR_MENUTEXT				xbSYSCOLOR(7)
#define xbCOLOR_WINDOWTEXT				xbSYSCOLOR(8)
#define xbCOLOR_CAPTIONTEXT				xbSYSCOLOR(9)
#define xbCOLOR_ACTIVEBORDER			xbSYSCOLOR(10)
#define xbCOLOR_INACTIVEBORDER			xbSYSCOLOR(11)
#define xbCOLOR_APPWORKSPACE			xbSYSCOLOR(12)
#define xbCOLOR_HIGHLIGHT				xbSYSCOLOR(13)
#define xbCOLOR_HIGHLIGHTTEXT			xbSYSCOLOR(14)
#define xbCOLOR_BTNFACE					xbSYSCOLOR(15)
#define xbCOLOR_BTNSHADOW				xbSYSCOLOR(16)
#define xbCOLOR_GRAYTEXT				xbSYSCOLOR(17)
#define xbCOLOR_BTNTEXT					xbSYSCOLOR(18)
#define xbCOLOR_INACTIVECAPTIONTEXT		xbSYSCOLOR(19)
#define xbCOLOR_BTNHIGHLIGHT			xbSYSCOLOR(20)

#define xbCOLOR_3DDKSHADOW				xbSYSCOLOR(xbNT4COLOR|21)
#define xbCOLOR_3DLIGHT					xbSYSCOLOR(xbNT4COLOR|22)
#define xbCOLOR_INFOTEXT				xbSYSCOLOR(xbNT4COLOR|23)
#define xbCOLOR_INFOBK					xbSYSCOLOR(xbNT4COLOR|24)

#define xbCOLOR_HOTLIGHT				xbSYSCOLOR(xbNT5COLOR|0x8000|26)
#define xbCOLOR_GRADIENTACTIVECAPTION   xbSYSCOLOR(xbNT5COLOR|0x2000|27)
#define xbCOLOR_GRADIENTINACTIVECAPTION xbSYSCOLOR(xbNT5COLOR|0x3000|28)

#define xbCOLOR_MENUHIGHLIGHT			xbSYSCOLOR(xbNT5COLOR|0xD000|29) //->xbCOLOR_HIGHLIGHT
#define xbCOLOR_MENUBAR					xbSYSCOLOR(xbNT5COLOR|0x4000|30) //->xbCOLOR_MENU

#define xbCOLOR_SYS_LENGTH				0x30
#define xbCOLOR_USER_START				0x100
#define xbCOLOR_USER_END				0xFFF

struct xbColorNameSt
{
	uint64		ref;
	const char	*pName;
};

xbEXPORT extern xbColorNameSt g_xbArrClrName[];

//CAUTION : Lock order : first g_xbEngineCenter.m_sect, next g_xbColorEngine.m_sect
class xbColorType;
class xbEXPORT __declspec(uuid("{953877A7-3A35-4ade-8460-D07E3116B589}"))
	xbColorEngine:public xbEngine
{
public:
	xbColorEngine(const char *pLabel,const xbEngines &engUsed = xbEngines())
		:xbEngine(xbEngineStart_OnUse,pLabel,engUsed)
	{
	}
	virtual ~xbColorEngine()
	{
		Destroy();
	}
	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(xbColorEngine);
		return xbEngine::GetInterface(riid,ppInterf);
	}

	ulong GetTRGB(uint64 sysColor,int recLevel = 0)
	{
		xbLock lckStart(this);
		if (sysColor == xbNULLCOLOR)
			xbThrowEsys("xbColorEngine()::GetTRGB(), color is NULL (xbNULLCOLOR)");
		
		if (!(sysColor & xblowCOLOR_SYSMASK)) 
			return (ulong)(sysColor & xblowCOLOR_TRGBMASK);

		int idxClr; 
		if (xbWHATSYSCOLOR(sysColor) > xbGetOSver()) 
			idxClr = xbSYSCOLORGETRPL(sysColor);
		else 
			idxClr = xbSYSCOLORGET(sysColor);

		xbSectLock	lck(&m_sect);
		if (idxClr < xbCOLOR_SYS_LENGTH)
		{
			SysColorInfo *pInfo = &m_sysColor[idxClr];
			if (pInfo->m_clr != xbNULLCOLOR) 
				return (ulong)pInfo->m_clr;

			return (ulong)(pInfo->m_clr = ::GetSysColor(idxClr));
		}

		if (idxClr < xbCOLOR_USER_START || idxClr > xbCOLOR_USER_END)
			xbThrowEsys("xbColorEngine()::GetTRGB(), bad color id ("+xbString(sysColor,xbStringNum_hexa)+")");

		if (recLevel > 10)
			xbThrowEsys("xbColorEngine()::GetTRGB(), too many recursive calls ("+xbString(sysColor,xbStringNum_hexa)+",nb rec calls="+xbString(recLevel)+")");

		idxClr -= xbCOLOR_USER_START;
		if (idxClr >= m_userColor.GetLength())
			xbThrowEsys("xbColorEngine()::GetTRGB(), bad user color index ("+xbString(sysColor,xbStringNum_hexa)+",nb user colors="+xbString(m_userColor.GetLength())+")");

		UserColorInfo	*pInfo = &m_userColor.GetAt(idxClr);

		return GetTRGB(pInfo->m_clr,recLevel+1);
	}

	bool IsColorNull(uint64 sysColor,int recLevel = 0) const
	{
		xbLock lckStart(this);
		if (sysColor == xbNULLCOLOR) 
			return true;
		
		if (!(sysColor & xblowCOLOR_SYSMASK)) 
			return false;

		ulong idxClr; 
		if (xbWHATSYSCOLOR(sysColor) > xbGetOSver()) 
			idxClr = xbSYSCOLORGETRPL(sysColor);
		else 
			idxClr = xbSYSCOLORGET(sysColor);

		xbSectLock	lck(&m_sect);
		if (idxClr < xbCOLOR_SYS_LENGTH) return false;

		if (idxClr < xbCOLOR_USER_START || idxClr > xbCOLOR_USER_END)
			xbThrowEsys("xbColorEngine()::GetRgb(), bad color id ("+xbString(sysColor,xbStringNum_hexa)+")");

		if (recLevel > 10)
			xbThrowEsys("xbColorEngine()::GetRgb(), too many recursive calls ("+xbString(sysColor,xbStringNum_hexa)+",nb rec calls="+xbString(recLevel)+")");

		idxClr -= xbCOLOR_USER_START;
		if (sysColor >= m_userColor.GetLength())
			xbThrowEsys("xbColorEngine()::GetRgb(), bad user color index ("+xbString(sysColor,xbStringNum_hexa)+",nb user colors="+xbString(m_userColor.GetLength())+")");

		const UserColorInfo	*pInfo = &m_userColor.GetAt(idxClr);

		return IsColorNull(pInfo->m_clr,recLevel+1);
	}

	void Add(xbColorType *pType);
	void Remove(xbColorType *pType);
	void SetUserColor(int id,uint64 ref);
	xbString GetUserColorName(int id);
	uint64 GetUserColor(const xbString &name);

	void	SysChanges();
	void	UserChanges();
	virtual bool isLockedByMe() const {return m_sect.IsLockedByMe();}

	mutable xbSectShare	m_sect;
protected:
	virtual void onStart() {}
	virtual bool onStop(bool) 
	{
		xbSectLock	lck(&m_sect);
		{
			SysColorInfo *ptr = m_sysColor;
			SysColorInfo *pend = m_sysColor + xbCOLOR_SYS_LENGTH;
			while(ptr < pend)
			{
				ptr->m_clr = xbNULLCOLOR;
				ptr++;
			}
		}
		m_userColor.Clear();
		return true;
	}

	struct xbEXPORT SysColorInfo
	{
		uint64	m_clr;
		SysColorInfo()
		{
			m_clr = xbNULLCOLOR;
		}
	};
	SysColorInfo			m_sysColor[xbCOLOR_SYS_LENGTH];

	struct UserColorInfo:public SysColorInfo
	{
		xbColorType		*m_pColorType;
		const char		*m_pName;
		UserColorInfo()
		{
			m_pName = NULL;
			m_pColorType = NULL;
		}
	};
	
	xbArray<UserColorInfo>	m_userColor;
};

extern xbColorEngine	xbEXPORT g_xbColorEngine;

class xbEXPORT xbColor:public xblowColor
{
public:
	xbColor()
	{
	}
	xbColor(uint64 clr)
		:xblowColor(clr)
	{
	}
	xbColor(const xbColor &clr)
		:xblowColor(clr)
	{
	}
	xbColor(const xbString &clr)
	{
		Set(clr);
	}
	xbColor(const xbColorType &clrType);

	bool IsEmpty() const
	{
		return g_xbColorEngine.IsColorNull(val);
	}
	bool IsNull() const
	{
		return g_xbColorEngine.IsColorNull(val);
	}
	bool IsValid() const
	{
		return !g_xbColorEngine.IsColorNull(val);
	}
	xbColor &operator=(uint64 clr)
	{
		if (val == clr) 
			return *this;
		_destroyobjs();
		val = clr;
		return *this;
	}
	xbColor &operator=(const xbColor &clr)
	{
		if (val == clr.val) 
			return *this;
		_destroyobjs();
		val = clr.val;
		return *this;
	}
	xbColor &operator=(const xbColorType &clrType);

	uint64	GetRef() const
	{
		return val;
	}
	xbPropertyR(uint64,Ref);

	bool operator==(const xbColorType &i) const;
	bool operator!=(const xbColorType &i) const;

	operator xbString() const {return GetLabel();}
	operator COLORREF() const
	{
		return RGB;
	}
protected:
	inline void _destroyobjs()
	{
	}
private:
	xbDclNoNewForObject(xbColor);
};

extern xbColor xbEXPORT xbColorNull;

class xbEXPORT __declspec(uuid("{1D691F3A-B14B-410b-9536-3946B09300F1}"))
	xbColorType:public xbEngine
{
public:
	xbColorType(const char *pName,const char *pLabel,const char *pFamily,
		COLORREF dfltVal)
		:xbEngine(xbEngineStart_Auto,pLabel,xbEngines(&g_xbColorEngine,&g_xbCfgXdcUserColors,&g_xbCfgXdcCommonColors))
	{
		m_pUserCfgPath = &g_xbCfgXdcUserColors;
		m_pCommonCfgPath = &g_xbCfgXdcCommonColors;
		m_pName = pName;
		m_pFamily = pFamily;
		m_dfltVal = dfltVal;
		m_val = xbNULLCOLOR;
		m_id = 0;
	}
	xbColorType(xbCfgPath *pUserCfgPath,xbCfgPath *pCommonCfgPath,
		const char *pName,const char *pLabel,const char *pFamily,
		COLORREF dfltVal)
		:xbEngine(xbEngineStart_Auto,pLabel,xbEngines(&g_xbColorEngine,pUserCfgPath,pCommonCfgPath))
	{
		m_pUserCfgPath = pUserCfgPath;
		m_pCommonCfgPath = pCommonCfgPath;
		m_pName = pName;
		m_pFamily = pFamily;
		m_dfltVal = dfltVal;
		m_val = xbNULLCOLOR;
		m_id = 0;
	}
	virtual ~xbColorType()
	{
		Destroy();
	}
	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(xbColorType);
		return xbEngine::GetInterface(riid,ppInterf);
	}

	uint64 GetColor() const;
	void SetColor(uint64 color);
	xbPropertyRW(uint64,Color);

	uint64 GetDfltColor() const;
	void SetDfltColor(uint64 color);
	xbPropertyRW(uint64,DfltColor);

	xbString GetName() {CheckReady();return m_pName;}
	xbPropertyR(xbString,Name);

	xbString GetFamily() {CheckReady();return m_pFamily;}
	xbPropertyR(xbString,Family);

	uint64 GetRef() const
	{
		xbLock lckStart(this);
		return xbSYSCOLOR(m_id + xbCOLOR_USER_START);
	};
	xbPropertyR(uint64,Ref);

	void Default();

	bool ConfigRead();
	void ConfigWrite();

	LPCSTR GetNamePtr() {return m_pName;}

	operator COLORREF() const
	{
		xbLock lckStart(this);
		return m_clr.operator COLORREF();
	}
	operator const xbColor &() const
	{
		xbLock lckStart(this);
		return m_clr;
	}
	operator const xbColor *() const
	{
		xbLock lckStart(this);
		return &m_clr;
	}
	operator xbColor *()
	{
		xbLock lckStart(this);
		return &m_clr;
	}
	virtual bool isLockedByMe() const {return g_xbColorEngine.m_sect.IsLockedByMe();}
protected:
	virtual void onStart() 
	{
		xbSectLock lck(&g_xbColorEngine.m_sect);
		ConfigRead();
		g_xbColorEngine.Add(this);
	}
	virtual bool onStop(bool) 
	{
		xbSectLock lck(&g_xbColorEngine.m_sect);
		g_xbColorEngine.Remove(this);
		ConfigWrite();
		return true;
	}

	uint64		m_val,m_dfltVal;
	int			m_id;
	xbCfgPath	*m_pUserCfgPath,*m_pCommonCfgPath;

	const char	*m_pName,*m_pFamily;

	xbColor		m_clr;

	friend xbColorEngine;
};

xbEXPORT uint64 xbBlendColor( uint64 clr1, uint64 clr2 );
xbEXPORT uint64 xbBlendColor( uint64 zero_color, uint64 one_color, double zero_to_one_ratio );
xbEXPORT uint64 xbBlendColorInt( uint64 zero_color, uint64 one_color, DWORD ratiotwo );


#endif
