/*<<<***************************************************************************
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.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbColor.h"

xbColor			xbColorNull;

xbColorEngine	g_xbColorEngine("Base Color Engine");

xbCfgPath	g_xbCfgXdcUserColors("XdcUserColors","Base User Colors Configuration Path",
							 xbCfgAppliUser,"UI/color");
xbCfgPath	g_xbCfgXdcCommonColors("XdcCommonColors","Base Common Colors Configuration Path",
							 xbCfgCommon,"UI/color");

/*****************************************************************************************
* Predefined colors names / cf HTML colors names
******************************************************************************************/
xbColorNameSt g_xbArrClrName[] = 
{
	xbNULLCOLOR,"null",
		xbCOLOR_SCROLLBAR,"scrollbar",
		xbCOLOR_BACKGROUND,"background",
		xbCOLOR_ACTIVECAPTION,"activecaption",
		xbCOLOR_INACTIVECAPTION,"inactivecaption",
		xbCOLOR_MENU,"menu",
		xbCOLOR_WINDOW,"window",
		xbCOLOR_WINDOWFRAME,"windowframe",
		xbCOLOR_MENUTEXT,"menutext",
		xbCOLOR_WINDOWTEXT,"windowtext",
		xbCOLOR_CAPTIONTEXT,"captiontext",
		xbCOLOR_ACTIVEBORDER,"activeborder",
		xbCOLOR_INACTIVEBORDER,"inactiveborder",
		xbCOLOR_APPWORKSPACE,"appworkspace",
		xbCOLOR_HIGHLIGHT,"highlight",
		xbCOLOR_HIGHLIGHTTEXT,"highlighttext",
		xbCOLOR_BTNFACE,"btnface",
		xbCOLOR_BTNSHADOW,"btnshadow",
		xbCOLOR_GRAYTEXT,"graytext",
		xbCOLOR_BTNTEXT,"btntext",
		xbCOLOR_INACTIVECAPTIONTEXT,"inactivecaptiontext",
		xbCOLOR_BTNHIGHLIGHT,"btnhighlight",

		xbCOLOR_3DDKSHADOW,"3DdkShadow",
		xbCOLOR_3DLIGHT,"3Dlight",
		xbCOLOR_INFOTEXT,"infotext",
		xbCOLOR_INFOBK,"infobk",

		xbCOLOR_HOTLIGHT,"hotlight",
		xbCOLOR_GRADIENTACTIVECAPTION,"gradientActiveCaption",
		xbCOLOR_GRADIENTINACTIVECAPTION,"gradientInactiveCaption",

		xbCOLOR_MENUHIGHLIGHT,"menuHighLight",
		xbCOLOR_MENUBAR,"menubar",

		0xFFF8F0, "aliceblue",   
		0xFFF8F8, "ghostwhite",
		0xADDEFF, "navajowhite",   
		0xD7EBFA, "antiquewhite",
		0x00D7FF, "gold",   
		0x800000, "navy",   
		0xFFFF00, "aqua",   
		0x20A5DA, "goldenrod",
		0xE6F5FD, "oldlace",   
		0xD4FF7F, "aquamarine",   
		0x808080, "gray",   
		0x008080, "olive",   
		0xFFFFF0, "azure",   
		0x008000, "green",   
		0x238E6B, "olivedrab",   
		0xDCF5F5, "beige",   
		0x2FFFAD, "greenyellow",
		0x00A5FF, "orange",   
		0xC4E4FF, "bisque",   
		0xF0FFF0, "honeydew",   
		0x0045FF, "orangered",   
		0x000000, "black",   
		0xB469FF, "hotpink",   
		0xD670DA, "orchid",   
		0xCDEBFF, "blanchedalmond",   
		0x5C5CCD, "indianred",   
		0xAAE8EE, "palegoldenrod",   
		0xFF0000, "blue",   
		0x82004B, "indigo",   
		0x98FB98, "palegreen",   
		0xE22B8A, "blueviolet",   
		0xF0FFFF, "ivory",   
		0xEEEEAF, "paleturquoise",   
		0x2A2AA5, "brown",   
		0x8CE6F0, "khaki",   
		0x9370DB, "palevioletred",   
		0x87B8DE, "burlywood",   
		0xFAE6E6, "lavender",   
		0xD5EFFF, "papayawhip",   
		0xA09E5F, "cadetblue",   
		0xF5F0FF, "lavenderblush",   
		0xB9DAFF, "peachpuff",   
		0x00FF7F, "chartreuse",   
		0x00FC7C, "lawngreen",   
		0x3F85CD, "peru",   
		0x1E69D2, "chocolate",   
		0xCDFAFF, "lemonchiffon",   
		0xCBC0FF, "pink",   
		0x507FFF, "coral",   
		0xE6D8AD, "lightblue",   
		0xDDA0DD, "plum",   
		0xED9564, "cornflowerblue",   
		0x8080F0, "lightcoral",   
		0xE6E0B0, "powderblue",   
		0xDCF8FF, "cornsilk",   
		0xFFFFE0, "lightcyan",   
		0x800080, "purple",   
		0x3C14DC, "crimson",   
		0xD2FAFA, "lightgoldenrodyellow",   
		0x0000FF, "red",   
		0xFFFF00, "cyan",   
		0x90EE90, "lightgreen",   
		0x8F8FBC, "rosybrown",   
		0x8B0000, "darkblue",   
		0xD3D3D3, "lightgrey",   
		0xE16941, "royalblue",   
		0x8B8B00, "darkcyan",   
		0xC1B6FF, "lightpink",   
		0x13458B, "saddlebrown",   
		0x0B86B8, "darkgoldenrod",   
		0x7AA0FF, "lightsalmon",   
		0x7280FA, "salmon",   
		0xA9A9A9, "darkgray",   
		0xAAB220, "lightseagreen",   
		0x60A4FA, "sandybrown",   
		0x006400, "darkgreen",   
		0xFACE87, "lightskyblue",   
		0x578B2E, "seagreen",   
		0x6BB7BD, "darkkhaki",   
		0x998877, "lightslategray",   
		0xEEF5FF, "seashell",   
		0x8B008B, "darkmagenta",   
		0xDEC4B0, "lightsteelblue",   
		0x2D52A0, "sienna",   
		0x2F6B55, "darkolivegreen",   
		0xE0FFFF, "lightyellow",   
		0xC0C0C0, "silver",   
		0x008CFF, "darkorange",   
		0x00FF00, "lime",   
		0xEBCE87, "skyblue",   
		0xCC3299, "darkorchid",   
		0x32CD32, "limegreen",   
		0xCD5A6A, "slateblue",   
		0x00008B, "darkred",   
		0xE6F0FA, "linen",   
		0x908070, "slategray",   
		0x7A96E9, "darksalmon",   
		0xFF00FF, "magenta",   
		0xFAFAFF, "snow",   
		0x8FBC8F, "darkseagreen",   
		0x000080, "maroon",   
		0x7FFF00, "springgreen",   
		0x8B3D48, "darkslateblue",   
		0xAACD66, "mediumaquamarine",   
		0xB48246, "steelblue",   
		0x4F4F2F, "darkslategray",   
		0xCD0000, "mediumblue",   
		0x8CB4D2, "tan",   
		0xD1CE00, "darkturquoise",   
		0xD355BA, "mediumorchid",   
		0x808000, "teal",   
		0xD30094, "darkviolet",   
		0xDB7093, "mediumpurple",   
		0xD8BFD8, "thistle",   
		0x9314FF, "deeppink",   
		0x71B33C, "mediumseagreen",   
		0x4763FF, "tomato",   
		0xFFBF00, "deepskyblue",   
		0xEE687B, "mediumslateblue",   
		0xD0E040, "turquoise",   
		0x696969, "dimgray",   
		0x9AFA00, "mediumspringgreen",   
		0xEE82EE, "violet",   
		0xFF901E, "dodgerblue",   
		0xCCD148, "mediumturquoise",
		0xB3DEF5, "wheat",   
		0x2222B2, "firebrick",   
		0x8515C7, "mediumvioletred",   
		0xFFFFFF, "white",   
		0xF0FAFF, "floralwhite",   
		0x701919, "midnightblue",   
		0xF5F5F5, "whitesmoke",   
		0x228B22, "forestgreen",   
		0xFAFFF5, "mintcream",   
		0x00FFFF, "yellow",   
		0xFF00FF, "fuchsia",   
		0xE1E4FF, "mistyrose",   
		0x32CD9A, "yellowgreen",   
		0xDCDCDC, "gainsboro",   
		0xB5E4FF, "moccasin",      
		xbNULLCOLOR,NULL,
};

/*****************************************************************************************
* xbColor
******************************************************************************************/
/*
red+y:20%
red+blue:20% => red/80% + blue/20%
Y: luminance
U: Chrominance Bleu
V: Chrominance Rouge
*/
bool xblowColor::Set(const xbString &str,xbOpMode opm)
{
	if (str.IsEmpty()) 
		return false;
	uint64 clr = xbNULLCOLOR;
	int pos=0;
	xbString sstr,sflag,sval;
	char curChOp,lastChOp=0;
	while((pos=str.GetToken(pos,"+-",&sstr,NULL,&curChOp)) > 0)
	{
		sstr.GetToken(0,":",&sflag,&sval);
		if (sval.IsEmpty())
		{
			uint64 tmp;
			if (!_set(tmp,sflag,opm))
				return false;
			if (!lastChOp)
			{
				clr = tmp;
				lastChOp = curChOp;
				continue;
			}
			_toRgb(clr);
			_toRgb(tmp);
			if (lastChOp == '+')
			{
				clr += tmp;
				lastChOp = curChOp;
				continue;
			}
			clr -= tmp;
			lastChOp = curChOp;
			continue;
		}

		long opl;
		const char *pchStop = NULL;
		if (xbStrToLong(sval,&opl,0,&pchStop)<=0 || opl <= 0)
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys("Invalid value ("+sval+") specified in color specification ("+str+") !");
			return false;
		}
		for(;;)
		{
			if (!pchStop || !*pchStop)
				break;
			if (xbStricmp(pchStop,"%")==0)
			{
				opl = -opl;
				break;
			}
			xbThrow xbEsys("Invalid value ("+sval+") specified in color specification ("+str+") !\nSyntax: [+|-][color|Y|U|V]:ratio[%], example: red+Y:20%");
		}
		_toRgb(clr);
		if (sflag.CompareNoCase("Y")==0 || sflag.CompareNoCase("L")==0)
		{
			_opY(clr,lastChOp,opl); //luminance
			lastChOp = curChOp;
			continue;
		}
		if (sflag.CompareNoCase("U")==0 || sflag.CompareNoCase("C")==0)
		{
			_opU(clr,lastChOp,opl); //chrominance
			lastChOp = curChOp;
			continue;
		}
		if (sflag.CompareNoCase("V")==0 || sflag.CompareNoCase("S")==0)
		{
			_opV(clr,lastChOp,opl); //Saturation
			lastChOp = curChOp;
			continue;
		}
		if (sflag.CompareNoCase("R")==0)
		{
			_opR(clr,lastChOp,opl);
			lastChOp = curChOp;
			continue;
		}
		if (sflag.CompareNoCase("G")==0)
		{
			_opG(clr,lastChOp,opl);
			lastChOp = curChOp;
			continue;
		}
		if (sflag.CompareNoCase("B")==0)
		{
			_opB(clr,lastChOp,opl);
			lastChOp = curChOp;
			continue;
		}

		uint64 tmp;
		if (!_set(tmp,sflag,opm))
			return false;
		_toRgb(tmp);


		if (opl<0)
			opl=-opl;
		clr = xbBlendColorInt(clr,tmp,(opl*256)/100);
		lastChOp = curChOp;
	}

	operator=(clr);

	return true;
}

/*
bool xbColor::_set(uint64 &clr,const xbString &str,xbOpMode opm)
{
	if (str.IsEmpty()) 
	{
		if (opm == xbOp_Required) 
			xbThrow xbEsys("In color specification : empty string given as color");
		return false;
	}

	xbString val;
	if (str[1] != '#')
	{
		{
			xbColorNameSt *ptr = g_xbArrClrName;
			while(ptr->pName)
			{
				if (str.CompareNoCase(ptr->pName) == 0)
				{
					clr = ptr->ref;
					return true;
				}
				ptr++;
			}
		}
		
		COLORREF ref = g_xbColorEngine.GetUserColor(str);
		if (ref != xbNULLCOLOR) 
		{
			clr = ref;
			return true;
		}

		val = str;
	}
	else 
		val = str.Mid(1);

	char *ptr = NULL;
	ULONG ref = strtoul(val,&ptr,16);
	if (ptr != NULL && *ptr != 0)
	{
		if (opm == xbOp_Required) 
			xbThrow xbEsys("In color specification("+val+") : bad color string");
//		else xbRelOutput("ERROR: xbColor::Set("+val+") : bad color string");
		return false;
	}

	COLORREF wref = ((ref & 0xFF) << 16) + (ref & 0xFF00) + ((ref & 0xFF0000) >> 16);
	clr = wref;

	return true;
}
xbColor::operator xbString() const
{
	COLORREF ref = GetRef();
	{
		xbColorNameSt *ptr = g_xbArrClrName;
		while(ptr->pName)
		{
			if (ptr->ref == ref)
				return ptr->pName;
			ptr++;
		}
	}
	
	if ((ref & xbSYSCOLOR) && xbSYSCOLORGET(ref) >= xbCOLOR_USER_START)
	{
		xbString ret(g_xbColorEngine.GetUserColorName(xbSYSCOLORGET(ref)-xbCOLOR_USER_START));
		if (!ret.IsEmpty()) 
			return ret;
	}

	if (ref & xbSYSCOLOR) return xbString();

	ULONG clr = ((ref & 0xFF) << 16) + (ref & 0xFF00) + ((ref & 0xFF0000) >> 16);

	char buf[32];
	xbSnprintf(buf,sizeof(buf),"#%06lX",clr);

	return buf;
}
*/

/*****************************************************************************************
* xbColorEngine
******************************************************************************************/
void	xbColorEngine::SysChanges()
{
	xbLock lckStart;
	if (!IsNowStarted(&lckStart)) return;
	xbDbgOutput("xbColorEngine::SysChanges() called");
	xbSectLock	lck(&m_sect);
	SysColorInfo *ptr = m_sysColor;
	SysColorInfo *pend = m_sysColor + xbCOLOR_SYS_LENGTH;
	while(ptr < pend)
	{
		ptr->m_clr = xbNULLCOLOR;
		ptr++;
	}
}
void	xbColorEngine::UserChanges()
{
	xbLock lckStart;
	if (!IsNowStarted(&lckStart)) return;
	xbDbgOutput("xbColorEngine::UserChanges() called");
	xbSectLock	lck(&m_sect);

	UserColorInfo *ptr = m_userColor.Begin();
	UserColorInfo *pend = m_userColor.End();
	while(ptr < pend)
	{
		if (ptr->m_pColorType) 
			ptr->m_pColorType->ConfigRead();
		ptr++;
	}
}

void xbColorEngine::Add(xbColorType *pType)
{
	xbLock lckStart(this);
	xbSectLock lck(&m_sect);

	UserColorInfo	*pInfo = NULL;
	int no = 0;
	while(no < m_userColor.Length)
	{
		if (!m_userColor.GetAt(no).m_pColorType)
		{
			pInfo = &m_userColor.GetAt(no);
			break;
		}
		no++;
	}
	if (!pInfo)
	{
		no = m_userColor.Length;
		m_userColor.Length = no + 1;
		pInfo = &m_userColor.GetAt(no);
	}

	pInfo->m_pColorType = pType;
	pInfo->m_pName = pType->GetNamePtr();
	pInfo->m_clr = pType->m_val == xbNULLCOLOR?pType->m_dfltVal:pType->m_val;

	pType->m_clr = xbSYSCOLOR(no + xbCOLOR_USER_START);
	pType->m_id = no;
}

void xbColorEngine::Remove(xbColorType *pType)
{
	xbLock lckStart(this);
	xbSectLock lck(&m_sect);
	xbAssert(m_userColor.GetAt(pType->m_id).m_pColorType == pType);
	m_userColor.GetAt(pType->m_id).m_pColorType = NULL;
	pType->m_id = 0;
	pType->m_clr = xbNULLCOLOR;
}

void xbColorEngine::SetUserColor(int id,uint64 ref)
{
	xbLock lckStart(this);
	xbSectLock lck(&m_sect);
	xbAssert(m_userColor.GetAt(id).m_pColorType != NULL);
	m_userColor.GetAt(id).m_clr = ref;
}

xbString xbColorEngine::GetUserColorName(int id)
{
	xbLock lckStart;
	if (!IsNowStarted(&lckStart)) return xbString();

	xbSectLock lck(&m_sect);
	if (id < 0 || id >= m_userColor.Length) return xbString();
	xbAssert(m_userColor.GetAt(id).m_pColorType != NULL);
	return m_userColor.GetAt(id).m_pName;	
}

uint64 xbColorEngine::GetUserColor(const xbString &name)
{
	xbLock lckStart;
	if (!IsNowStarted(&lckStart)) 
		return xbNULLCOLOR;

	xbSectLock	lck(&m_sect);

	int id = 0;
	UserColorInfo *ptr = m_userColor.Begin();
	UserColorInfo *pend = m_userColor.End();
	while(ptr < pend)
	{
		if (ptr->m_pColorType && ptr->m_pName && name.CompareNoCase(ptr->m_pName) == 0) 
			return xbSYSCOLOR(id + xbCOLOR_USER_START);

		ptr++;
		id++;
	}
	return xbNULLCOLOR;
}

/*****************************************************************************************
* xbColor
******************************************************************************************/
xbColor::xbColor(const xbColorType &clrType)
{
	val = clrType.Ref;
}

xbColor &xbColor::operator=(const xbColorType &clrType)
{
	uint64 clr = clrType.Ref;
	if (val == clr) return *this;
	_destroyobjs();
	val = clr;
	return *this;
}
bool xbColor::operator==(const xbColorType &i) const
{
	return i.Ref == val;
}

bool xbColor::operator!=(const xbColorType &i) const
{
	return i.Ref != val;
}

/*****************************************************************************************
* xbColorType
******************************************************************************************/

uint64 xbColorType::GetColor() const
{
	g_xbColorEngine.CheckReady();
	xbSectLock lck(&g_xbColorEngine.m_sect);
	CheckReady();

	if (m_val == xbNULLCOLOR) return m_dfltVal;
	return m_val;
}

void xbColorType::SetColor(uint64 color)
{
	g_xbColorEngine.CheckReady();
	{
		xbSectLock lck(&g_xbColorEngine.m_sect);
		CheckReady();
		if (m_val == color) 
			return;
		m_val = color;
	}
	xbLock lckStart;
	if (IsNowStarted(&lckStart)) 
	{
		g_xbColorEngine.SetUserColor(m_id,m_val == xbNULLCOLOR?m_dfltVal:m_val);
	}
}

uint64 xbColorType::GetDfltColor() const
{
	g_xbColorEngine.CheckReady();
	xbSectLock lck(&g_xbColorEngine.m_sect);
	CheckReady();
	return m_dfltVal;
}

void xbColorType::SetDfltColor(uint64 color)
{
	g_xbColorEngine.CheckReady();
	{
		xbSectLock lck(&g_xbColorEngine.m_sect);
		CheckReady();
		if (m_dfltVal == color) 
			return;
		m_dfltVal = color;
	}
	xbLock lckStart;
	if (m_val == xbNULLCOLOR && IsNowStarted(&lckStart)) 
	{
		g_xbColorEngine.SetUserColor(m_id,m_dfltVal);
	}
}

void xbColorType::Default()
{
	g_xbColorEngine.CheckReady();
	{
		xbSectLock lck(&g_xbColorEngine.m_sect);
		CheckReady();
		if (m_val == xbNULLCOLOR) 
			return;
		m_val = xbNULLCOLOR;
	}
	xbLock lckStart;
	if (IsNowStarted(&lckStart)) 
		g_xbColorEngine.SetUserColor(m_id,m_dfltVal);
}

bool xbColorType::ConfigRead()
{
	g_xbColorEngine.CheckReady();
	xbSectLock lck(&g_xbColorEngine.m_sect);
	CheckReady();
	if (m_pUserCfgPath && m_pName)
	{
		xbCfgKey key(*m_pUserCfgPath,m_pName);
		if (key.Exists())
		{
			xbString strClr(key.Value["Value"].AsString);
			if (!strClr.IsEmpty())
			{
				xbColor clr;
				if (clr.Set(strClr)) 
				{
					lck.Unlock();
					SetColor(clr.GetRef());
					return true;
				}
			}
		}
	}
	if (m_pCommonCfgPath && m_pName)
	{
		xbCfgKey key(*m_pCommonCfgPath,m_pName);
		if (key.Exists())
		{
			xbString strClr(key.Value["Value"].AsString);
			if (!strClr.IsEmpty())
			{
				xbColor clr;
				if (clr.Set(strClr)) 
				{
					lck.Unlock();
					SetColor(clr.GetRef());
					return true;
				}
			}
		}
	}
	return false;
}

void xbColorType::ConfigWrite()
{
	g_xbColorEngine.CheckReady();
	xbSectLock lck(&g_xbColorEngine.m_sect);
	CheckReady();
	if (!m_pUserCfgPath || !m_pName) return;

	xbCfgKey key(*m_pUserCfgPath,m_pName);
	if (m_val == xbNULLCOLOR)
	{
		if (key.Exists()) key.DeleteKey(true);
		return;
	}

	xbColor clr(m_val);
	if (m_pLabel) key.DefaultValue = m_pLabel;
	key.Value["Value"] = xbString(clr);
	if (m_pFamily) key.Value["Family"] = m_pFamily;
}

uint64 xbBlendColorInt( uint64 zero, uint64 one, DWORD ratiotwo )
{
	const DWORD		ratioone = 256-ratiotwo;

	uint64	r = (((zero&0xFF)*ratioone)+((one&0xFF)*ratiotwo))&0xFF00;
	uint64	g = (((zero&0xFF00)*ratioone)+((one&0xFF00)*ratiotwo))&0xFF0000;
	uint64	b = (((zero&0xFF0000)*ratioone)+((one&0xFF0000)*ratiotwo))&0xFF000000;
	uint64	a = (((zero&0xFF000000)*ratioone)+((one&0xFF000000)*ratiotwo))&0xFF00000000;

	return (r+g+b+a)>>8;
}

uint64 xbBlendColor( uint64 zero_color, uint64 one_color, double zero_to_one_ratio )
{
	return COLORREF(xbBlendColorInt(zero_color,one_color,DWORD(zero_to_one_ratio*256)));
}

uint64	xbBlendColor( uint64 clr1, uint64 clr2 )	//Mix between 2 colors equaly at 50%
{
	uint64	rest1	= clr1&0x01010101;
	clr1^=rest1;
	clr1>>=1;
	uint64	rest2	= clr2&0x01010101;
	clr2^=rest2;
	clr2>>=1;
	uint64	clr = clr1+clr2;
	clr+= (rest1&rest2);
	return clr;
}

uchar _norm(long val,char chOp,long ratio)
{
	if (ratio<0)
		ratio=(-ratio)*255/100;
	if (ratio>255)
		ratio=255;
	if (!chOp)
		return (uchar)ratio;
	if (chOp == '+')
		val += ratio;
	else
		val -= ratio;
	if (val < 0)
		return 0;
	if (val > 255)
		return 255;
	return uchar(val);
}

uchar _normDbl(double dval)
{
	long val = (long)dval;
	if (val < 0)
		return 0;
	if (val > 255)
		return 255;
	return uchar(val);
}

uchar _normLong(long val)
{
	if (val < 0)
		return 0;
	if (val > 255)
		return 255;
	return uchar(val);
}

void xblowColor::_opR(uint64 &clr,char chOp,long ratio)
{
	clr = (clr&~0xff)+_norm((long)clr&0xff,chOp,ratio);
}

void xblowColor::_opG(uint64 &clr,char chOp,long ratio)
{
	clr = (clr&~0xff00)+_norm((long)clr&0xff00,chOp,ratio);
}

void xblowColor::_opB(uint64 &clr,char chOp,long ratio)
{
	clr = (clr&~0xff0000)+_norm((long)clr&0xff0000,chOp,ratio);
}

void xblowColor::_opY(uint64 &clr,char chOp,long ratio)
{
	uint64 clrWk;
	_rgbToYuv(clr,clrWk);
	clrWk = (clrWk&~0xff)+_norm((long)clrWk&0xff,chOp,ratio);
	_yuvToRgb(clrWk,clr);
}

void xblowColor::_opU(uint64 &clr,char chOp,long ratio)
{
	uint64 clrWk;
	_rgbToYuv(clr,clrWk);
	clrWk = (clrWk&~0xff00)+_norm((long)clrWk&0xff00,chOp,ratio);
	_yuvToRgb(clrWk,clr);
}

void xblowColor::_opV(uint64 &clr,char chOp,long ratio)
{
	uint64 clrWk;
	_rgbToYuv(clr,clrWk);
	clrWk = (clrWk&~0xff0000)+_norm((long)clrWk&0xff0000,chOp,ratio);
	_yuvToRgb(clrWk,clr);
}

void xblowColor::_rgbToYuv(const uint64 &clr,uint64 &clrDest)
{
/*
Y =  0.299R + 0.587G + 0.114B
U = -0.147R - 0.289G + 0.436B
V =  0.615R - 0.515G - 0.100B
*/
/*	double R=double(clr&0xff);
	double G=double((clr&0xff00)>>8);
	double B=double((clr&0xff0000)>>16);
	clrDest = _normDbl(0.299*R + 0.587*G + 0.114*B)
		+ (_normDbl(-0.147*R - 0.289*G + 0.436*B)<<8)
		+ (_normDbl(0.615*R - 0.515*G - 0.100*B)<<16);*/

	long R=long(clr&0xff);
	long G=long((clr&0xff00)>>8);
	long B=long((clr&0xff0000)>>16);
	uchar Y = _normLong(( (  66 * R + 129 * G +  25 * B + 128) >> 8) +  16);
	uchar U = _normLong(( ( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128);
	uchar V = _normLong(( ( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128);

	clrDest = ulong(Y) + (ulong(U) << 8) + (ulong(V) << 16) + ulong(clr & 0xFF000000);
}

void xblowColor::_yuvToRgb(const uint64 &clr,uint64 &clrDest)
{
/*
R = Y + 1.140V
G = Y - 0.395U - 0.581V
B = Y + 2.032U
	double Y=double(clr&0xff);
	double U=double((clr&0xff00)>>8);
	double V=double((clr&0xff0000)>>16);
	clrDest = _normDbl(Y + 1.140*V)
		+ (_normDbl(Y - 0.395*U - 0.581*V)<<8)
		+ (_normDbl(Y + 2.032*U)<<16);
*/
	long C = long(clr&0xff) - 16;
	long D = long((clr&0xff00)>>8) - 128;
	long E = long((clr&0xff0000)>>16) - 128;
	uchar R = _normLong(( 298 * C           + 409 * E + 128) >> 8);
	uchar G = _normLong(( 298 * C - 100 * D - 208 * E + 128) >> 8);
	uchar B = _normLong(( 298 * C + 516 * D           + 128) >> 8);

	clrDest = ulong(R) + (ulong(G) << 8) + (ulong(B) << 16) + ulong(clr & 0xFF000000);
}

void xblowColor::_toRgb(uint64 &clr)
{
	if (clr == xblowCOLOR_NULL)
		return;
	
	if (!(clr & xblowCOLOR_SYSMASK)) 
		return;

	clr = g_xbColorEngine.GetTRGB(clr);
}

bool xblowColor::_set(uint64 &clr,const xbString &str,xbOpMode opm)
{
	if (str.IsEmpty()) 
	{
		if (opm == xbOp_Required)
			xbThrow xbEarg("Can't convert empty string to color");
		return false;
	}

	if (str.GetAt(0) != '#')
	{
		{
			xbColorNameSt *ptr = g_xbArrClrName;
			while(ptr->pName)
			{
				if (str.CompareNoCase(ptr->pName) == 0)
				{
					clr = ptr->ref;
					return true;
				}
				ptr++;
			}
		}
		
		uint64 ref = g_xbColorEngine.GetUserColor(str);
		if (ref != xbNULLCOLOR) 
		{
			clr = ref;
			return true;
		}

		if (opm == xbOp_Required) 
			xbThrow xbEarg("Can't convert string '"+str+"' to color");
		return false;
	}

	char *ptr = NULL;
	ULONG ref = strtoul((LPCSTR)str+1,&ptr,16);
	if (ptr != NULL && *ptr != 0)
	{
		if (opm == xbOp_Required) 
			xbThrow xbEarg("Can't convert string '"+str+"' to color");
		return false;
	}

	clr = (ref & 0xFF000000) + ((ref & 0xFF) << 16) + (ref & 0xFF00) + ((ref & 0xFF0000) >> 16);
	return true;
}

bool xblowColor::fromString(const xbString &str,xbOpMode opm)
{
	return _set(val,str,opm);
}

void xblowColor::toString(xbString *pStr) const
{
//	COLORREF ref = IsSysColor()?toVBcolor():COLORREF(val & 0xFFFFFFFF);
//	if (IsSysColor() || !(ref & 0xFF000000) || ref == xbNULLCOLOR)
	{
		xbColorNameSt *ptr = g_xbArrClrName;
		while(ptr->pName)
		{
			if (ptr->ref == val)
			{
				*pStr = ptr->pName;
				return;
			}
			ptr++;
		}
	}
	
	if (IsSysColor() && xbSYSCOLORGET(val) >= xbCOLOR_USER_START)
	{
		*pStr = g_xbColorEngine.GetUserColorName(xbSYSCOLORGET(val)-xbCOLOR_USER_START+1);
		return;
	}

	ULONG ref = TRGB; 
	ULONG clr = (ref & 0xFF000000)+ ((ref & 0xFF) << 16) + (ref & 0xFF00) + ((ref & 0xFF0000) >> 16);

	if (ref & 0xFF000000)
		*pStr = ::xbFormat("#%08lX",clr);
	else
		*pStr = ::xbFormat("#%06lX",clr);
}

bool xblowColor::fromString(LPCWSTR pstr,xbOpMode opm)
{
	xbString str(pstr);
	return fromString(str,opm);
}

void xblowColor::toString(xbBSTR *pStr) const
{
	xbString ret;
	toString(&ret);
	*pStr = ret;
}

void xblowColor::toString(xbStringW *pStr) const
{
	xbString ret;
	toString(&ret);
	*pStr = xbStringW(ret);
}

ulong xblowColor::sysCalc() const
{
	return g_xbColorEngine.GetTRGB(val);
}

int xblowColor::Compare(const xblowColor &clr2) const
{
	ulong cref1;
	ulong cref2;
	if (IsSysColor() || clr2.IsSysColor())
	{
		cref1 = (ulong)((xblowCOLOR_SYSMASK & val)>>32);
		cref2 = (ulong)((xblowCOLOR_SYSMASK & val)>>32);
	}
	else
	{
		cref1 = TRGB;
		cref2 = clr2.TRGB;
	}
	if (cref1 > cref2)
		return 1;
	if (cref1 < cref2)
		return -1;
	return 0;
}

ulong xblowColor::GetWinARGB() const
{
	ulong rgba=TRGB;
	if ((rgba & 0xFF000000))
		xbThrow xbEarg("Transparency not supported here (color:"+GetLabel()+")");
//	ulong alpha = (0xFF - (rgba>>24))<<24;
	return (rgba&0xFFFFFF);// + alpha;
}
