using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Utils
{
	// Normalizes the vector and returns the magnitude
	public static float NormalizeMag( ref Vector2 vector )
	{
		if ( ApproximatelyZero(vector.x, float.Epsilon) )
		{
			if ( ApproximatelyZero(vector.y,float.Epsilon) )
			{
				vector = Vector2.zero;
				return 0.0f;
			}
			else 
			{				
				float mag = Mathf.Abs(vector.y);
				vector.Set(0, Mathf.Sign(vector.y));
				return mag;
			}
		}
		else if ( ApproximatelyZero(vector.y, float.Epsilon) )
		{
			float mag = Mathf.Abs(vector.x);
			vector.Set(Mathf.Sign(vector.x),0);
			return mag;
		}
		else 
		{	
			float mag = vector.magnitude;
			if ( mag == 0 ) // Maybe could happen - check to avoid NaN
				vector = Vector2.zero;
			else 		
				vector /= mag;
				
			return mag;
		}
	}
	
	// Returns the Vector2 rotated 90 counterclockwise
	public static Vector2 GetVector2Tangent( Vector2 vector )
	{				
		return new Vector2(-vector.y, vector.x);
	}
	
	// Returns the Vector2 rotated 90 counterclockwise
	public static Vector2 GetVector2TangentR( Vector2 vector )
	{				
		return new Vector2(vector.y, -vector.x);
	}
		
 	// Snap to a grid value
	public static Vector3 Snap( Vector3 pos, float snapTo )
	{	
		return new Vector3( Snap(pos.x, snapTo), Snap(pos.y, snapTo), Snap(pos.z, snapTo) );
	}
	
	public static Vector2 Snap( Vector2 pos, float snapTo )
	{	
		return new Vector2( Snap(pos.x, snapTo), Snap(pos.y, snapTo) );
	}
	
	public static float Snap( float pos, float snapTo )
	{		
		if ( snapTo < 0.1f )
			return pos;
        return Mathf.Round(pos / snapTo) * snapTo;
	}
	
	public static bool Approximately(float a, float b, float epsilon)
	{
		return Mathf.Abs(a-b) < epsilon;
	}
	
	public static bool ApproximatelyZero( float a, float epsilon )
	{	
		return ( a > 0 ) ? (a < epsilon) : a > -epsilon;
	}
	
	public static bool IsInLayerMask(GameObject obj, LayerMask mask)
	{
       return ((mask.value & (1 << obj.layer)) > 0);
    }
	
	public static float EaseCubic( float ratio )
	{
		ratio = Mathf.Clamp01(ratio);
		return (-2.0f*ratio*ratio*ratio + 3.0f*ratio*ratio);
	}
	
	public static float EaseCubic( float start, float end, float ratio )
	{
		return start + ((end-start) * EaseCubic(ratio));
	}
	
	public static float Interpolate(float from, float to, float min, float max, float val)
	{
		float denom = max-min;
		if ( denom == 0 )
			return from;
		
		return Mathf.Lerp( from, to, 
				Mathf.Clamp01( (val-min)/ (denom) ) );
	}
		
	public static float Loop( float val, float min, float max )
	{
		while ( val < min ) val += (max-min);
		while ( val > max ) val -= (max-min);
		return val;
	}
	
	public static Quaternion GetDirectionRotation( Vector2 direction )
	{		
		if ( ApproximatelyZero( direction.y, float.Epsilon ) == false ) // When too close to zero, FromToRotation doesn't work
		{
			Quaternion result = Quaternion.FromToRotation( Vector3.right, direction );		
			if ( Approximately( result.z, 1.0f, float.Epsilon ) )
				return result;
		}
		
		return Quaternion.Euler(0,0,Mathf.Rad2Deg * Mathf.Atan2(direction.y, direction.x));
		//Quaternion result = Quaternion.identity;
		//result.eulerAngles = new Vector3(0,0, Mathf.Rad2Deg * Mathf.Atan2(direction.y, direction.x));
		//return result;
	}

	public static void Swap<T>(ref T lhs, ref T rhs)
	{
	    T temp;
	    temp = lhs;
	    lhs = rhs;
	    rhs = temp;
	}
	
	public static float ClampAngle( float angle, float min, float max )
	{
		// Rotate so 0degrees is opposite to the center (makes flipping left/right work)
							
		float offsetNeg180 = (180.0f - ((min + max)*0.5f));
		
		min = Mathf.Repeat(min+offsetNeg180, 360.0f);
		max = Mathf.Repeat(max+offsetNeg180, 360.0f);
		angle = Mathf.Repeat(angle+offsetNeg180, 360.0f);
		
		return Mathf.Clamp( angle, min, max) - offsetNeg180;
					
	}
	
	public static bool IsWithinAngle( float angle, float min, float max )
	{
		angle = Mathf.Repeat( angle-min, 360 );
		max = Mathf.Repeat( max-min, 360 );		
		return angle >= 0 && angle < max;		
	}
	
	public static bool IsPointInPolygon( Vector2 point, List<Vector2> polygon )
	{
		bool intersects = false;
		
		// From 
		int nvert = polygon.Count;
		int i,j;
		for ( i = 0, j = nvert-1; i < nvert; j = i++) 
		{			
			if ( ( (polygon[i].y > point.y) != (polygon[j].y > point.y) ) 
				&& (point.x < (polygon[j].x-polygon[i].x) * (point.y-polygon[i].y) / (polygon[j].y-polygon[i].y) + polygon[i].x) )
			{
				intersects = !intersects;
			}
		}
		
		return intersects;
	}
	
	// Returns seconds since unix epoch
	public static int GetUnixTimestamp()
	{
		var epochStart = new System.DateTime(1970, 1, 1, 8, 0, 0, System.DateTimeKind.Utc);
		return (int)(System.DateTime.UtcNow - epochStart).TotalSeconds;
	}
	
	
	public static bool GetTimeIncrementPassed( float time )
	{	
		// Time has ticked over if the remainder of previous time is higher than the current (ie. time has gone backwards)
		return ( ((Time.timeSinceLevelLoad - Time.deltaTime) % time) > (Time.timeSinceLevelLoad % time) );
	}
	
	/*
	public static void ShuffleList<T>( ref List<T> list )
	{
		T temp;
		int j = 0;
		int count = list.Count;
		for ( int i = count - 1; i >= 1; --i )
		{
			j = Random.Range(0,i+1);
			// swap
			temp = list[i];
			list[i] = list[j];
			list[j] = temp;
		}
	}
	
	public static List<T> ShuffleListCopy<T>( List<T> list )
	{
		int count = list.Count;
		List<T> result = new List<T>(count);
		result[0] = list[0];
		
		int j = 0;
		for ( int i = 1; i < count; ++i )
		{
			j = Random.Range (0,i+1);
			if ( j != i )
			{
				result[i] = result[j];
			}
			result[j] = list[i];
		}
		return result;
	}
	
	public static void ShuffleArray<T>( ref T[] list )
	{
		T temp;
		int j = 0;
		int count = list.Length;
		for ( int i = count - 1; i >= 1; --i )
		{
			j = Random.Range(0,i+1);
			// swap
			temp = list[i];
			list[i] = list[j];
			list[j] = temp;
		}
	}
	
	public static T[] ShuffleArrayCopy<T>( T[] list )
	{
		int count = list.Length;
		T[] result = new T[count];
		result[0] = list[0];
		
		int j = 0;
		for ( int i = 1; i < count; ++i )
		{
			j = Random.Range (0,i+1);
			if ( j != i )
			{
				result[i] = result[j];
			}
			result[j] = list[i];
		}
		return result;
	}*/
	

	
	public static T[] CreateFilledArray<T>( int size, T value )
	{
		T[] arr = new T[size];
		for ( int i = 0; i < arr.Length; ++i ) 
		{
			arr[i] = value;
		}
		return arr;
	}
	
	static readonly float ASPECT_16_9 = 16.0f/9.0f;
	static readonly float ASPECT_16_9_INV = 1.0f/ASPECT_16_9;
	
	// Converts a ratio in screen coords that was assuming 16:9 so it's the equivalent in 4:3, etc.
	// Useful if you want ghosts to not go outside the left 10% of screen, and have good values that work in 1080p
	public static float NormalizeScreenRatioTo1080X( Camera camera, float ratio )
	{	
		if ( camera == null )
			return ratio;
		return ratio * (camera.aspect * ASPECT_16_9_INV);
	}
	
	public static float NormalizeScreenRatioTo1080Y( Camera camera, float ratio )
	{	
		if ( camera == null )
			return ratio;
		return ratio * ((1.0f/camera.aspect) * ASPECT_16_9);
	}
	
	public static int 	MaskSetAt(int mask, int index) { return mask | (1 << index); }
	public static int 	MaskUnsetAt(int mask, int index) { return mask & ~(1 << index); }
	public static bool 	MaskIsSet(int mask, int index) { return (mask & 1 << index) != 0; }


    public static Vector3 FindCenter(Vector3[] targets)
    {
        Vector3 min = targets[0];  
        Vector3 max = targets[0];

        for (int i = 1; i < targets.Length; i++)
        {
            min = Vector3.Min(min, targets[i]);
            max = Vector3.Max(max, targets[i]);
        }

        return min + (max - min) * 0.5f;
    }
}


public static class ExtentionMethods
{	
	
	
	
	public static Rect Encapsulate( this Rect rect, Rect other )
	{
		return Rect.MinMaxRect( Mathf.Min(rect.xMin, other.xMin), Mathf.Min(rect.yMin, other.yMin),
			 Mathf.Max(rect.xMax, other.xMax), Mathf.Max(rect.yMax, other.yMax) );
	}


	// Returns the Vector3 from vector2 and x
	public static Vector2 WithX( this Vector2 vector, float x )
	{				
		return new Vector2(x, vector.y);
	}
	public static Vector3 WithX( this Vector3 vector, float x )
	{				
		return new Vector3(x, vector.y, vector.z);
	}
	// Returns the Vector3 from vector2 and y
	public static Vector2 WithY( this Vector2 vector, float y )
	{				
		return new Vector2(vector.x, y);
	}
	public static Vector3 WithY( this Vector3 vector, float y )
	{				
		return new Vector3(vector.x, y, vector.z);
	}
	// Returns the Vector3 from vector2 and z
	public static Vector3 WithZ( this Vector2 vector, float z )
	{				
		return new Vector3(vector.x, vector.y, z);
	}
	public static Vector3 WithZ( this Vector3 vector, float z )
	{				
		return new Vector3(vector.x, vector.y, z);
	}
	
	// Returns the Vector2 rotated 90 counterclockwise
	public static Vector2 GetTangent( this Vector2 vector )
	{				
		return new Vector2(-vector.y, vector.x);
	}
	
	// Returns the Vector2 rotated 90 counterclockwise
	public static Vector2 GetTangentR( this Vector2 vector )
	{				
		return new Vector2(vector.y, -vector.x);
	}
		
 	// Snap to a grid value
	public static Vector3 Snap( this Vector3 pos, float snapTo )
	{	
		return new Vector3( Utils.Snap(pos.x, snapTo), Utils.Snap(pos.y, snapTo), Utils.Snap(pos.z, snapTo) );
	}
	
	
	public static Vector2 Snap( this Vector2 pos, float snapTo )
	{	
		return new Vector2( Utils.Snap(pos.x, snapTo), Utils.Snap(pos.y, snapTo) );
	}
	
	public static bool IsInLayerMask(this GameObject obj, LayerMask mask)
	{
       return ((mask.value & (1 << obj.layer)) > 0);
    }
		
	public static Quaternion GetDirectionRotation( this Vector2 direction )
	{		
		if ( direction.y != 0.0f )
			return Quaternion.FromToRotation( Vector3.right, direction );		
		
		Quaternion result = Quaternion.identity;
		result.eulerAngles = new Vector3(0,0, Mathf.Rad2Deg * Mathf.Atan2(direction.y, direction.x));
		return result;
	}		
	public static float GetDirectionAngle( this Vector2 direction )
	{		
		if ( Mathf.Approximately( direction.y, 0 ) )
		{
			if ( direction.x >= 0 ) 
				return 0.0f;
			return 180.0f;
		}
		if ( Mathf.Approximately( direction.x, 0 ) )
		{
			if ( direction.y >= 0 )
				return 90.0f;
			 
			return 270.0f;
		}		
		
		return Mathf.Rad2Deg * Mathf.Atan2(direction.y, direction.x);
	}
	
	
	public static T GetComponentInParents<T>(this GameObject gameObject) where T : Component
	{
		for(Transform t = gameObject.transform; t != null; t = t.parent)
		{
			T result = t.GetComponent<T>();
			if(result != null)
				return result;
		}
		
		return null;
	}
	
	// Removes elements from a list that are null (if nullable) or zero if a float or something.
	public static void RemoveDefaultElements<T>( this List<T> list ) 
	{
		int count = list.Count;
		for ( int i = count-1; i >= 0; i-- )
		{
			if ( object.Equals(list[i], default(T) ) )
			{
				list.RemoveAt(i);
			}
		}
	}
	
	// Shuffles the list
	public static void Shuffle<T>( this List<T> list )
	{
		T temp;
		int j = 0;
		int count = list.Count;
		for ( int i = count - 1; i >= 1; --i )
		{
			j = Random.Range(0,i+1);
			// swap
			temp = list[i];
			list[i] = list[j];
			list[j] = temp;
		}
	}
	
	// Returns a copy of the shuffled list
	public static List<T> ShuffleListCopy<T>( this List<T> list )
	{
		int count = list.Count;
		List<T> result = new List<T>(list);
		//result.AddRange(list);
		
		int j = 0;
		for ( int i = 1; i < count; ++i )
		{
			j = Random.Range (0,i+1);
			if ( j != i )
			{
				result[i] = result[j];
			}
			result[j] = list[i];
		}
		return result;
	}
	
	// Shuffles the array
	public static void Shuffle<T>( this T[] list )
	{
		T temp;
		int j = 0;
		int count = list.Length;
		for ( int i = count - 1; i >= 1; --i )
		{
			j = Random.Range(0,i+1);
			// swap
			temp = list[i];
			list[i] = list[j];
			list[j] = temp;
		}
	}
	
	// Returns a copy of the shuffled array
	public static T[] ShuffleCopy<T>( this T[] list )
	{
		int count = list.Length;
		T[] result = new T[count];
		result[0] = list[0];
		
		int j = 0;
		for ( int i = 1; i < count; ++i )
		{
			j = Random.Range (0,i+1);
			if ( j != i )
			{
				result[i] = result[j];
			}
			result[j] = list[i];
		}
		return result;
	}

	
	/*
	public static int BitSetAt(this int val, int index ) { val |= 1 << index; return val;}	
	public static int BitUnsetAt(this int val, int index) { val &= ~(1 << index); return val;}
	public static bool BitIsSet(this int val, int index) { return (val & 1 << index) != 0; }	
	
	public static int BitSetAt( this int val,  params int[] bitsSet )
	{
		for( int i = 0; i< bitsSet.Length; ++i )
		{
			val |= 1 <<  bitsSet[i];
		}
		return val;
	}*/
	/*
	public int Value { get{ return m_value; } }
	public void SetAt(int index) { m_value |= 1 << index; }
	public void UnsetAt(int index) { m_value |= ~(1 << index); }
	public bool IsSet(int index) { return (m_value & 1 << index) != 0; }
	*/
	
	public static Color WithAlpha(this Color col, float alpha )
	{
		return new Color(col.r,col.g,col.b,alpha);
	}
	
	public static bool IsIndexValid<T>( this List<T> list, int index )
	{
		if ( list == null || index < 0 )
			return false;
		return ( index < list.Count );
	}
	
	public static bool IsIndexValid<T>( this T[] list, int index )
	{
		if ( list == null || index < 0 )
			return false;
		return ( index < list.Length );
	}	

	public static T[] Populate<T>(this T[] arr, T value )
	{
		for ( int i = 0; i < arr.Length; ++i ) {
			arr[i] = value;
		}
		return arr;
	}
}

// Construct with in
public class ShuffledIndex
{
	int m_current = -1;
	int[] m_ids = null;
	
	public ShuffledIndex(int count)
	{
		m_ids = new int[count];
		for (int i = 0; i < count; ++i )
		{
			m_ids[i] = i;
		}
	}
	
	public int Next()
	{
		m_current++;
		return (int)this;
	}
	
	public static implicit operator int(ShuffledIndex m)
	{
		if ( m.m_ids.Length == 0 )
			return -1;
		if ( m.m_ids.IsIndexValid(m.m_current) == false )
		{
			m.m_ids.Shuffle();
			m.m_current = 0;
		}
		return m.m_ids[m.m_current];
	}
}

[System.Serializable]
public class MinMaxRange
{
	public MinMaxRange( ) {}
	
	public MinMaxRange( float val )
	{
		m_min = val;
		m_max = val;
		m_value = val;
		m_hasMax = false;
	}
	
	public MinMaxRange( float min, float max )
	{
		m_min = min;
		m_max = max;
		m_value = min;
		m_hasMax = true;
	}
	
	// Public for editor... damnit. there's gotta be a better way :(
	public float m_min = 0;
	public float m_max = 0;
	public bool m_hasMax = false;	
	public bool m_hasValue = false;
	
	float m_value = 0;
	
	public float Min { get { return m_min; } }
	public float Max { get { return m_hasMax ? m_max : m_min; } }
	public float Value { get { return (float)this; } }
	
	public static implicit operator float(MinMaxRange m) 
	{
		if ( m.m_hasValue == false )
		{		
			if ( m.m_hasMax )
			{
				m.m_value = Random.Range(m.m_min, m.m_max);
				m.m_hasValue = true;
				return m.m_value;
			}
			else
			{
				m.m_value = m.m_min;
				m.m_hasValue = true;
			}
		}
		return m.m_value;
	}
	
	public static implicit operator int(MinMaxRange m) 
	{
		return Mathf.RoundToInt((float)m);
	}
	
	public void Randomise()
	{
	
		if ( m_hasMax )
		{
			m_value = Random.Range(m_min, m_max);
			m_hasValue = true;
		}
		else
		{
			m_value = m_min;
			m_hasValue = true;
		}
		
	}
	
	public float GetRandom()
	{
		if ( m_hasMax )
		{
			return Random.Range(m_min, m_max);
		}
		return m_min;		
	}
	
	public bool IsZero()
	{
		return m_min == 0 && m_hasMax == false;
	}
}


[System.Serializable]
public class Message
{		
	public void SendTo(GameObject toObject)
	{
		toObject.SendMessage(m_message, m_params, SendMessageOptions.DontRequireReceiver);
	}
	public void SendUpwardsTo(GameObject toObject)
	{
		toObject.SendMessageUpwards(m_message, m_params, SendMessageOptions.DontRequireReceiver);
	}
	public string m_message = string.Empty;
	public MsgParams m_params = null;
}

[System.Serializable]
public class MsgParams
{
	public GameObject m_object = null;
	public string m_string = string.Empty;
	public float m_float = 0.0f;
}


public class TimedList<T>
{
	
	Dictionary< T, float > m_list = new Dictionary<T, float>();
	
	List<T> m_keys = new List<T>();

	public void Add( T source )
	{
		m_list[source] = 0;
		m_keys = new List<T>(m_list.Keys);
		//Debug.Log("TimedList " +source+" count: "+m_list.Count);
	}
	
	public void Add( T source, float time )
	{
		m_list[source] = time;
		m_keys = new List<T>(m_list.Keys);
		//Debug.Log("TimedList " +source+" count: "+m_list.Count);
	}

	public void AddAdditive( T source, float time )
	{
		if ( m_list.ContainsKey(source) )
			m_list[source] += time;
		else 
		{
			m_list[source] = time;
			m_keys = new List<T>(m_list.Keys);			
		}
		//Debug.Log("TimedList " +source+" count: "+m_list.Count);
	}
	
	public bool Remove( T source )
	{
		if ( m_list.Remove(source) )
		{
			m_keys = new List<T>(m_list.Keys);
			return true;
		}
		return false;
		//Debug.Log("TimedList " +source+" count: "+m_list.Count);
	}	
	
	
	public bool Empty() { return m_list.Count == 0; }
	public void Clear() { m_list.Clear(); m_keys.Clear();  }
	public int Count() { return m_list.Count; }
	public bool Contains( T source ) { return m_list.ContainsKey(source); }
	public Dictionary< T, float > GetList() { return m_list; }
	
	
	// updates timers, returns false if there's no entries remaining in the list	
	public bool Update()
	{
		// Update list		
		
		foreach (T key in m_keys)
		{
			float timeRemaining = m_list[key];
			if ( timeRemaining > 0 )
			{
				timeRemaining -= Time.smoothDeltaTime;
				if ( timeRemaining <= 0 )
				{
					m_list.Remove(key);
					m_keys = new List<T>(m_list.Keys);
				}
				else
				{
					m_list[key] = timeRemaining;
				}
			}
		}	
		return m_list.Count > 0;
		
	}
	
	// updates timers, returns true if any change to list was made
	public bool UpdateReturnModified()
	{
		// Update list		
		bool modified = false;
		foreach (T key in m_keys)
		{
			float timeRemaining = m_list[key];
			if ( timeRemaining > 0 )
			{
				timeRemaining -= Time.smoothDeltaTime;
				if ( timeRemaining <= 0 )
				{
					modified = true;
					m_list.Remove(key);
					m_keys = new List<T>(m_list.Keys);
				}
				else
				{
					m_list[key] = timeRemaining;
				}
			}
		}	
		return modified;
		
	}
	
	// updates timers, returns false if there's no entries remaining in the list, and returns removed items
	public bool Update( out List<T> removed )
	{
		// Update list
		removed = null;		
		foreach (T key in m_keys)
		{
			float timeRemaining = m_list[key];
			if ( timeRemaining > 0 )
			{
				timeRemaining -= Time.smoothDeltaTime;
				if ( timeRemaining <= 0 )
				{
					if ( removed == null ) removed = new List<T>();
					removed.Add(key);
					m_list.Remove(key);
					m_keys = new List<T>(m_list.Keys);
				}
				else
				{
					m_list[key] = timeRemaining;
				}
			}
		}	
		return m_list.Count > 0;
		
	}

}


// For queuing a list of events to act on a bit of data. Not really messages...
public class MessageTimeline<T>
{
	// Definitions
	private class Message
	{
		public T m_data;
		public float m_time;
		public DelegateMsg m_callback;		
	}	
	
	public delegate void DelegateMsg( T data );
	
	// Private vars
	
	List<Message> m_messages = new List<Message>();
	//float m_time;
	
	
	// Public functions
		
	public void Add( T data, float delay, DelegateMsg callback )
	{
		m_messages.Insert(0, new Message(){ m_data = data, m_callback = callback, m_time = Time.timeSinceLevelLoad + delay } );
	}
	
	public void Update()
	{
		// iterate backwards so we can remove elements
		for ( int i = m_messages.Count - 1; i >= 0; --i )
		{	
			Message message = m_messages[i];
			if ( Time.timeSinceLevelLoad >= message.m_time )
			{
				message.m_callback( message.m_data );				
				m_messages.RemoveAt(i);
			}			
		}	
	}	
}

public struct BitMask
{	
	public BitMask( int mask )
	{
		m_value = mask;
	}
	
	public BitMask( params int[] bitsSet )
	{
		m_value = 0;
		for( int i = 0; i< bitsSet.Length; ++i )
		{
			m_value |= 1 <<  bitsSet[i];
		}
	}
	
	public static implicit operator int(BitMask m) 
	{
		return m.m_value;	
	}
	
	public int Value { get{ return m_value; } set { m_value = value; } }
	public void SetAt(int index) { m_value |= 1 << index; }
	public void UnsetAt(int index) { m_value &= ~(1 << index); }
	public bool IsSet(int index) { return (m_value & 1 << index) != 0; }
	public void Clear() { m_value = 0; }
	
	int m_value;
}

 
public class BitMaskAttribute : PropertyAttribute
{
    public System.Type propType;
    public BitMaskAttribute(System.Type aType)
    {
        propType = aType;
    }
}


/*
public class MinMaxRangeAttribute : MinMaxRangeAttribute
{
	public MinMaxRangeAttribute()
	{		
	}
}
*/

public class RunningAverage
{
	float m_total = 0.0f;
	float m_num = 0.0f;
	float m_average = 0.0f;

	public void SetAverage( int average ) 
	{ 
		m_total = 0; 
		m_num = 0; 
		m_average = 0; 
		AddValue(average); 
	}	
	
	public void AddValue(int value)
	{
		m_total += value;
		++m_num;		
		m_average = m_total / m_num;	
	}
	public float GetAverage() { return m_average; }
}

public class ColorX {
	
    static readonly string ALPHA_STRING = "0123456789abcdef";
	
    private static string GetHex(int num) {
        return ALPHA_STRING[num].ToString();
    }

    private static int HexToInt(char hexChar) {
        switch (hexChar) {
            case '0': return 0;
            case '1': return 1;
            case '2': return 2;
            case '3': return 3;
            case '4': return 4;
            case '5': return 5;
            case '6': return 6;
            case '7': return 7;
            case '8': return 8;
            case '9': return 9;
            case 'a': return 10;
            case 'b': return 11;
            case 'c': return 12;
            case 'd': return 13;
            case 'e': return 14;
            case 'f': return 15;
        }
        return -1;
    }

    public static string RGBToHex(Color color) {
        float red = color.r * 255;
        float green = color.g * 255;
        float blue = color.b * 255;

        string a = GetHex(Mathf.FloorToInt(red / 16));
        string b = GetHex(Mathf.RoundToInt(red) % 16);
        string c = GetHex(Mathf.FloorToInt(green / 16));
        string d = GetHex(Mathf.RoundToInt(green) % 16);
        string e = GetHex(Mathf.FloorToInt(blue / 16));
        string f = GetHex(Mathf.RoundToInt(blue) % 16);

        return a + b + c + d + e + f;
    }

    public static Color HexToRGB(string color) {
        float red = (HexToInt(color[1]) + HexToInt(color[0]) * 16f) / 255f;
        float green = (HexToInt(color[3]) + HexToInt(color[2]) * 16f) / 255f;
        float blue = (HexToInt(color[5]) + HexToInt(color[4]) * 16f) / 255f;
        Color finalColor = new Color { r = red, g = green, b = blue, a = 1 };
        return finalColor;
    }
}