using System;
using System.Drawing;
using UtilityLibrary.Win32;

namespace UtilityLibrary.General
{
	/// <summary>
	/// Summary description for ColorConvert.
	/// </summary>
	public class ColorUtil
	{
		
		static Color backgroundColor = Color.Empty;
		static Color selectionColor = Color.Empty;
		static Color controlColor = Color.Empty;
		static Color pressedColor = Color.Empty;
		static Color checkedColor = Color.Empty;
		static Color borderColor = Color.Empty;
		static bool useCustomColor = false;
		
		// No need to construct this object
		private ColorUtil()
		{
		}

		static public bool UsingCustomColor
		{
			get { return useCustomColor;}
		}


		// Knowncolor names
		#region

		//Transparentを使うことはないのでコメントアウトした。後に使う必要が出たら適当な処置を施すこと
		static public string[] KnownColorNames = 
		{ /*"Transparent", */"Black", "DimGray", "Gray", "DarkGray", "Silver", "LightGray", "Gainsboro", "WhiteSmoke", "White",
		  "RosyBrown", "IndianRed", "Brown", "Firebrick", "LightCoral", "Maroon", "DarkRed", "Red", "Snow", "MistyRose",
		  "Salmon", "Tomato", "DarkSalmon", "Coral", "OrangeRed", "LightSalmon", "Sienna", "SeaShell", "Chocolate",
		  "SaddleBrown", "SandyBrown", "PeachPuff", "Peru", "Linen", "Bisque", "DarkOrange", "BurlyWood", "Tan", "AntiqueWhite",
		  "NavajoWhite", "BlanchedAlmond", "PapayaWhip", "Mocassin", "Orange", "Wheat", "OldLace", "FloralWhite", "DarkGoldenrod",
		  "Cornsilk", "Gold", "Khaki", "LemonChiffon", "PaleGoldenrod", "DarkKhaki", "Beige", "LightGoldenrod", "Olive",
		  "Yellow", "LightYellow", "Ivory", "OliveDrab", "YellowGreen", "DarkOliveGreen", "GreenYellow", "Chartreuse", "LawnGreen",
		  "DarkSeaGreen", "ForestGreen", "LimeGreen", "PaleGreen", "DarkGreen", "Green", "Lime", "Honeydew", "SeaGreen", "MediumSeaGreen",
		  "SpringGreen", "MintCream", "MediumSpringGreen", "MediumAquaMarine", "YellowAquaMarine", "Turquoise", "LightSeaGreen",
		  "MediumTurquoise", "DarkSlateGray", "PaleTurquoise", "Teal", "DarkCyan", "Aqua", "Cyan", "LightCyan", "Azure", "DarkTurquoise",
		  "CadetBlue", "PowderBlue", "LightBlue", "DeepSkyBlue", "SkyBlue", "LightSkyBlue", "SteelBlue", "AliceBlue", "DodgerBlue",
		  "SlateGray", "LightSlateGray", "LightSteelBlue", "CornflowerBlue", "RoyalBlue", "MidnightBlue", "Lavender", "Navy",
		  "DarkBlue", "MediumBlue", "Blue", "GhostWhite", "SlateBlue", "DarkSlateBlue", "MediumSlateBlue", "MediumPurple",
		  "BlueViolet", "Indigo", "DarkOrchid", "DarkViolet", "MediumOrchid", "Thistle", "Plum", "Violet", "Purple", "DarkMagenta",
		  "Magenta", "Fuchsia", "Orchid", "MediumVioletRed", "DeepPink", "HotPink", "LavenderBlush", "PaleVioletRed", "Crimson",
		  "Pink", "LightPink" };
		#endregion
		
		// Systemcolors names
		#region
		static public string[] SystemColorNames = 
		{
			"ActiveBorder", "ActiveCaption", "ActiveCaptionText", "AppWorkspace", "Control", "ControlDark", "ControlDarkDark",
			"ControlLight", "ControlLightLight", "ControlText", "Desktop", "GrayText", "HighLight", "HighLightText", 
			"HotTrack", "InactiveBorder", "InactiveCaption", "InactiveCaptionText", "Info", "InfoText", "Menu", "MenuText",
			"ScrollBar", "Window", "WindowFrame", "WindowText" };
		#endregion
		
		// Conversion between RGB and Hue, Saturation and Luminosity function helpers
		#region
		static public void HSLToRGB(float h, float s, float l, ref float r, ref float g, ref float b)
		{
			// given h,s,l,[240 and r,g,b [0-255]
			// convert h [0-360], s,l,r,g,b [0-1]
			h=(h/240)*360;
			s /= 240;
			l /= 240;
			r /= 255;
			g /= 255;
			b /= 255;
					
			// Begin Foley
			float m1,m2;
			
			// Calc m2
			if (l<=0.5f) 
			{
				//m2=(l*(l+s)); seems to be typo in Foley??, replace l for 1
				m2=(l*(1+s));				
			} 
			else 
			{
				m2=(l+s-l*s);
			}
			
			//calc m1
			m1=2.0f*l-m2;
			
			//calc r,g,b in [0-1]
			if (s==0.0f) 
			{	// Achromatic: There is no hue
				// leave out the UNDEFINED part, h will always have value
				r=g=b=l;
			} 
			else 
			{	// Chromatic: There is a hue
				r= getRGBValue(m1,m2,h+120.0f);
				g= getRGBValue(m1,m2,h);
				b= getRGBValue(m1,m2,h-120.0f);
			}
						
			// End Foley
			// convert to 0-255 ranges
			r*=255;
			g*=255;
			b*=255;
            
		}

		static private float getRGBValue(float n1, float n2, float hue)
		{
			// Helper function for the HSLToRGB function above
			if (hue>360.0f) 
			{
				hue-=360.0f;
			} 
			else if (hue<0.0f) 
			{
				hue+=360.0f;
			}
		
			if (hue<60.0) 
			{
				return n1+(n2-n1)*hue/60.0f;
			} 
			else if (hue<180.0f) 
			{
				return n2;
			} 
			else if (hue<240.0f) 
			{
				return n1+(n2-n1)*(240.0f-hue)/60.0f;
			} 
			else 
			{
				return n1;
			}
		}


		static public void RGBToHSL(int r, int g, int b, ref float h, ref float s, ref float l)
		{

			//Computer Graphics - Foley p.595
			float delta;
			float fr = (float)r/255;
			float fg = (float)g/255;
			float fb = (float)b/255;
			float max = Math.Max(fr,Math.Max(fg,fb));
			float min = Math.Min(fr,Math.Min(fg,fb));
			
			//calc the lightness
			l = (max+min)/2;
			
			if (max==min)
			{
				//should be undefined but this works for what we need
				s = 0;
				h = 240.0f;		
			} 
			else 
			{
			
				delta = max-min;						
			
				//calc the Saturation
				if (l < 0.5) 
				{
					s = delta/(max+min);
				} 
				else 
				{
					s = delta/(2.0f-(max+min));
				}
				
				//calc the hue
				if (fr==max) 
				{
					h = (fg-fb)/delta;
				} 
				else if (fg==max) 
				{
					h = 2.0f + (fb-fr)/delta;
				} 
				else if (fb==max) 
				{
					h = 4.0f + (fr-fg)/delta;
				}
				
				//convert hue to degrees
				h*=60.0f;
				if (h<0.0f) 
				{
					h+=360.0f;
				}
			}
			//end foley

			//convert to 0-255 ranges
			//h [0-360], h,l [0-1]
			l*=240;
			s*=240;
			h=(h/360)*240;
            
		}
		#endregion

		// Visual Studio .NET colors calculation helpers
		#region
       
		static public Color VSNetBackgroundColor
		{
			get 
			{
				if ( useCustomColor && backgroundColor != Color.Empty )
					return backgroundColor;
				else
					return CalculateColor(SystemColors.Window, SystemColors.Control, 220);
			}
			set
			{
				// Flag that we are going to use custom colors instead
				// of calculating the color based on the system colors
				// -- this is a way of hooking up into the VSNetColors that I use throughout
				// the UtilityLibrary
				useCustomColor = true;
				backgroundColor = value;
			}
		}

		static public Color VSNetSelectionColor
		{
			get 
			{
				if ( useCustomColor && selectionColor != Color.Empty )
					return selectionColor;
				else
					return CalculateColor(SystemColors.Highlight, SystemColors.Window, 70);
			}
			set
			{
				// Flag that we are going to use custom colors instead
				// of calculating the color based on the system colors
				// -- this is a way of hooking up into the VSNetColor that I use throughout
				// the UtilityLibrary
				useCustomColor = true;
				selectionColor = value;
			}
		}


		static public Color VSNetControlColor
		{
			get 
			{	if ( useCustomColor && controlColor != Color.Empty )
					return controlColor;
				else
					return CalculateColor(SystemColors.Control, VSNetBackgroundColor, 195);
			}
			set
			{
				// Flag that we are going to use custom colors instead
				// of calculating the color based on the system colors
				// -- this is a way of hooking up into the VSNetColors that I use throughout
				// the UtilityLibrary
				useCustomColor = true;
				controlColor = value;
			}

		}

		static public Color VSNetPressedColor
		{
			get 
			{
				if ( useCustomColor && pressedColor != Color.Empty )
					return pressedColor;
				else
					return CalculateColor(SystemColors.Highlight, ColorUtil.VSNetSelectionColor, 70);
			}
			set
			{
				// Flag that we are going to use custom colors instead
				// of calculating the color based on the system colors
				// -- this is a way of hooking up into the VSNetColors that I use throughout
				// the UtilityLibrary
				useCustomColor = true;
				pressedColor = value;
			}
		}


		static public Color VSNetCheckedColor
		{
			get 
			{
				if ( useCustomColor && pressedColor != Color.Empty )
					return checkedColor;
				else
					return CalculateColor(SystemColors.Highlight,  SystemColors.Window, 30);
			}
			set
			{
				// Flag that we are going to use custom colors instead
				// of calculating the color based on the system colors
				// -- this is a way of hooking up into the VSNetColors that I use throughout
				// the UtilityLibrary
				useCustomColor = true;
				checkedColor = value;
			}
		}

		static public Color VSNetBorderColor
		{
			get 
			{
				if ( useCustomColor && borderColor != Color.Empty )
					return borderColor;
				else
				{
					// This color is the default color unless we are using 
					// custom colors
					return SystemColors.Highlight;
				}
			}
			set
			{
				// Flag that we are going to use custom colors instead
				// of calculating the color based on the system colors
				// -- this is a way of hooking up into the VSNetColors that I use throughout
				// the UtilityLibrary
				useCustomColor = true;
				borderColor = value;
			}
		}

		private static Color CalculateColor(Color front, Color back, int alpha)
		{
			
			// Use alpha blending to brigthen the colors but don't use it
			// directly. Instead derive an opaque color that we can use.
			// -- if we use a color with alpha blending directly we won't be able 
			// to paint over whatever color was in the background and there
			// would be shadows of that color showing through
			Color frontColor = Color.FromArgb(255, front);
			Color backColor = Color.FromArgb(255, back);
									
			float frontRed = frontColor.R;
			float frontGreen = frontColor.G;
			float frontBlue = frontColor.B;
			float backRed = backColor.R;
			float backGreen = backColor.G;
			float backBlue = backColor.B;
			
			float fRed = frontRed*alpha/255 + backRed*((float)(255-alpha)/255);
			byte newRed = (byte)fRed;
			float fGreen = frontGreen*alpha/255 + backGreen*((float)(255-alpha)/255);
			byte newGreen = (byte)fGreen;
			float fBlue = frontBlue*alpha/255 + backBlue*((float)(255-alpha)/255);
			byte newBlue = (byte)fBlue;

			return  Color.FromArgb(255, newRed, newGreen, newBlue);

		}
		#endregion

		// General functions
		#region
		static public Color ColorFromPoint(Graphics g, int x, int y)
		{
			IntPtr hDC = g.GetHdc();
			// Get the color of the pixel first
			uint colorref = WindowsAPI.GetPixel(hDC, x, y);
			byte Red = GetRValue(colorref);
			byte Green = GetGValue(colorref);
			byte Blue = GetBValue(colorref);
			g.ReleaseHdc(hDC);
			return  Color.FromArgb(Red, Green, Blue);
		}

		static public bool IsKnownColor(Color color, ref Color knownColor, bool useTransparent)
		{

			// Using the Color structrure "FromKnowColor" does not work if 
			// we did not create the color as a known color to begin with
			// we need to compare the rgbs of both color 
			Color currentColor = Color.Empty;
			bool badColor = false;
			for (KnownColor enumValue = 0; enumValue <= KnownColor.YellowGreen; enumValue++)
			{
				currentColor = Color.FromKnownColor(enumValue);
				string colorName = currentColor.Name;
				if ( !useTransparent ) 
					badColor = (colorName == "Transparent");
				if ( color.A == currentColor.A && color.R == currentColor.R && color.G == currentColor.G 
					&& color.B == currentColor.B && !currentColor.IsSystemColor
					&& !badColor )
				{
					knownColor = currentColor;
					return true;
				}
				
			}
			return false;

		}

		static public bool IsSystemColor(Color color, ref Color knownColor)
		{

			// Using the Color structrure "FromKnowColor" does not work if 
			// we did not create the color as a known color to begin with
			// we need to compare the rgbs of both color 
			Color currentColor = Color.Empty;
			for (KnownColor enumValue = 0; enumValue <= KnownColor.YellowGreen; enumValue++)
			{
				currentColor = Color.FromKnownColor(enumValue);
				string colorName = currentColor.Name;
				if ( color.R == currentColor.R && color.G == currentColor.G 
					&& color.B == currentColor.B && currentColor.IsSystemColor )
				{
					knownColor = currentColor;
					return true;
				}
				
			}
			return false;
		}

		static public uint GetCOLORREF(Color color)
		{
			return RGB(color.R, color.G, color.B);
		}


		static public Color ColorFromRGBString(string text)
		{
			
			Color rgbColor = Color.Empty;
			string[] RGBs = text.Split(','); 
			if ( RGBs.Length != 3 ) 
			{
				// If we don't have three pieces of information, then the
				// string is not properly formatted, inform the use
				throw new Exception("RGB color string is not well formed");
			}
		
			string stringR = RGBs[0];
			string stringG = RGBs[1];
			string stringB = RGBs[2];
			int R, G, B;
				
			try 
			{
				R = Convert.ToInt32(stringR);
				G = Convert.ToInt32(stringG);
				B = Convert.ToInt32(stringB);
				if ( ( R < 0 || R > 255 ) || ( G < 0 || G > 255 ) || ( B < 0 || B > 255 ) ) 
				{
					throw new Exception("Out of bounds RGB value");
				}
				else 
				{
					// Convert to color 
					rgbColor = Color.FromArgb(R, G, B);
					// See if we have either a web color or a systgem color
					Color knownColor = Color.Empty;
					bool isKnown = ColorUtil.IsKnownColor( rgbColor, ref knownColor, true);
					if ( !isKnown )
						isKnown = ColorUtil.IsSystemColor(rgbColor, ref knownColor);
					if ( isKnown )
						rgbColor = knownColor;
				}
			}
			catch ( InvalidCastException )
			{
				throw new Exception("Invalid RGB value");
			}

			return rgbColor;
		}
       	#endregion
		
		// Windows RGB related macros
		#region
		static public byte GetRValue(uint color)
		{
			return (byte)color;
		}

		static public byte GetGValue(uint color)
		{
			return ((byte)(((short)(color)) >> 8));
		}

		static public byte GetBValue(uint color)
		{
			return ((byte)((color)>>16));
		}

		static public uint RGB(int r, int g, int b)
		{
			return ((uint)(((byte)(r)|((short)((byte)(g))<<8))|(((short)(byte)(b))<<16)));

		}
		#endregion

	}
}
