context: #region Copyright & License Information
  /*
   * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation, either version 3 of
   * the License, or (at your option) any later version. For more
   * information, see COPYING.
   */
  #endregion
  
  using System;
  using System.Globalization;
  using OpenRA.Scripting;
  
  namespace OpenRA.Primitives
  {
  	public struct Color : IEquatable<Color>, IScriptBindable
  	{
  		readonly long argb;
  
  		public static Color FromArgb(int red, int green, int blue)
  		{
  			return FromArgb(byte.MaxValue, red, green, blue);
  		}
  
  		public static Color FromArgb(int alpha, int red, int green, int blue)
  		{
  			return new Color(((byte)alpha << 24) + ((byte)red << 16) + ((byte)green << 8) + (byte)blue);
  		}
  
  		public static Color FromAhsl(int alpha, float h, float s, float l)
  		{
  			// Convert from HSL to RGB
  			var q = (l < 0.5f) ? l * (1 + s) : l + s - (l * s);
  			var p = 2 * l - q;
  
  			float[] trgb = { h + 1 / 3.0f, h, h - 1 / 3.0f };
  			float[] rgb = { 0, 0, 0 };
  
  			for (var k = 0; k < 3; k++)
  			{
  				while (trgb[k] < 0) trgb[k] += 1.0f;
  				while (trgb[k] > 1) trgb[k] -= 1.0f;
  			}
  
  			for (var k = 0; k < 3; k++)
  			{
  				if (trgb[k] < 1 / 6.0f)
  					rgb[k] = p + ((q - p) * 6 * trgb[k]);
  				else if (trgb[k] >= 1 / 6.0f && trgb[k] < 0.5)
  					rgb[k] = q;
  				else if (trgb[k] >= 0.5f && trgb[k] < 2.0f / 3)
  					rgb[k] = p + ((q - p) * 6 * (2.0f / 3 - trgb[k]));
  				else
  					rgb[k] = p;
  			}
  
  			return FromArgb(alpha, (int)(rgb[0] * 255), (int)(rgb[1] * 255), (int)(rgb[2] * 255));
  		}
  
  		public static Color FromAhsl(int h, int s, int l)
  		{
  			return FromAhsl(255, h / 255f, s / 255f, l / 255f);
  		}
  
  		public static Color FromAhsl(float h, float s, float l)
  		{
  			return FromAhsl(255, h, s, l);
  		}
  
  		public static Color FromAhsv(int alpha, float h, float s, float v)
  		{
  			var ll = 0.5f * (2 - s) * v;
  			var ss = (ll >= 1 || v <= 0) ? 0 : 0.5f * s * v / (ll <= 0.5f ? ll : 1 - ll);
  			return FromAhsl(alpha, h, ss, ll);
  		}
  
  		public static Color FromAhsv(float h, float s, float v)
  		{
  			return FromAhsv(255, h, s, v);
  		}
  
  		public void ToAhsv(out int a, out float h, out float s, out float v)
  		{
  			var ll = 2 * GetBrightness();
  			var ss = GetSaturation() * ((ll <= 1) ? ll : 2 - ll);
  
  			a = A;
  			h = GetHue() / 360f;
  			s = (2 * ss) / (ll + ss);
  			v = (ll + ss) / 2;
  		}
  
  		Color(long argb)
  		{
  			this.argb = argb;
  		}
  
  		public int ToArgb()
  		{
  			return (int)argb;
  		}
  
  		public static Color FromArgb(int alpha, Color baseColor)
  		{
  			return FromArgb(alpha, baseColor.R, baseColor.G, baseColor.B);
  		}
  
  		public static Color FromArgb(int argb)
  		{
  			return FromArgb((byte)(argb >> 24), (byte)(argb >> 16), (byte)(argb >> 8), (byte)argb);
  		}
  
  		public static Color FromArgb(uint argb)
  		{
  			return FromArgb((byte)(argb >> 24), (byte)(argb >> 16), (byte)(argb >> 8), (byte)argb);
  		}
  
  		public static bool TryParse(string value, out Color color)
  		{
  			color = default(Color);
  			value = value.Trim();
  			if (value.Length != 6 && value.Length != 8)
  				return false;
  
  			byte red, green, blue, alpha = 255;
  			if (!byte.TryParse(value.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out red)
  			    || !byte.TryParse(value.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out green)
  			    || !byte.TryParse(value.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out blue))
  				return false;
  
  			if (value.Length == 8
  			    && !byte.TryParse(value.Substring(6, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out alpha))
  				return false;
  
  			color = FromArgb(alpha, red, green, blue);
  			return true;
  		}
  
  		public static bool operator ==(Color left, Color right)
  		{
  			return left.argb == right.argb;
  		}
  
  		public static bool operator !=(Color left, Color right)
  		{
  			return !(left == right);
  		}
  
  		public float GetBrightness()
  		{
  			var min = Math.Min(R, Math.Min(G, B));
  			var max = Math.Max(R, Math.Max(G, B));
  			return (max + min) / 510f;
  		}
  
  		public float GetSaturation()
  		{
  			var min = Math.Min(R, Math.Min(G, B));
  			var max = Math.Max(R, Math.Max(G, B));
  			if (max == min)
  				return 0.0f;
  
  			var sum = max + min;
  			if (sum > byte.MaxValue)
  				sum = 510 - sum;
  
  			return (float)(max - min) / sum;
  		}
  
  		public float GetHue()
  		{
  			var min = Math.Min(R, Math.Min(G, B));
  			var max = Math.Max(R, Math.Max(G, B));
  			if (max == min)
  				return 0.0f;
  
  			var diff = (float)(max - min);
  			var rNorm = (max - R) / diff;
  			var gNorm = (max - G) / diff;
  			var bNorm = (max - B) / diff;
  
  			var hue = 0.0f;
  			if (R == max)
  				hue = 60.0f * (6.0f + bNorm - gNorm);
  			if (G == max)
  				hue = 60.0f * (2.0f + rNorm - bNorm);
  			if (B == max)
  				hue = 60.0f * (4.0f + gNorm - rNorm);
  
  			if (hue > 360.0f)
  				hue -= 360f;
  
  			return hue;
  		}
  
  		public byte A { get { return (byte)(argb >> 24); } }
  		public byte R { get { return (byte)(argb >> 16); } }
  		public byte G { get { return (byte)(argb >> 8); } }
  		public byte B { get { return (byte)argb; } }
  
  		public bool Equals(Color other)
  		{
  			return this == other;
  		}
  
  		public override bool Equals(object obj)
  		{
  			if (!(obj is Color))
  				return false;
  
  			return this == (Color)obj;
  		}
  
  		public override int GetHashCode()
  		{
  			return (int)(argb ^ argb >> 32);
  		}
  
  		public override string ToString()
  		{
  			if (A == 255)
  
gt: 				return R.ToString("X2") + G.ToString("X2") + B.ToString("X2");