﻿using System;

using UnityEngine;


namespace Spineless
{
	/// <summary>
	/// Encapsulate a int value with upper and lower bounds, with event notifications.
	/// </summary>
	[System.Serializable]
	public class IntegerRange :
		System.ICloneable
	{

		#region Fields
		[SerializeField]
		private int value;

		[SerializeField]
		private int minimum;

		[SerializeField]
		private int maximum;		
		#endregion


		public IntegerRange(int minimum, int maximum) : this(minimum, maximum, maximum) { ;}

		public IntegerRange (int minimum, int maximum, int value)
		{
			this.minimum = minimum;
			this.maximum = maximum;
			this.value = value;
		}


		#region Operators
		public static IntegerRange operator -(IntegerRange range, int x) 
		{
			range.Current -= x;
			return range;
		}


		public static IntegerRange operator --(IntegerRange range)
		{
			range.Current--;
			return range;
		}


		public static IntegerRange operator +(IntegerRange range, int x)
		{
			range.Current += x;
			return range;
		}


		public static IntegerRange operator ++(IntegerRange range)
		{
			range.Current++;
			return range;
		}
		#endregion


		#region Events
		/// <summary>
		/// Raised when the current value of the Range changes.
		/// </summary>
		public event Action<int> ValueChanged;


		/// <summary>
		/// Raised when the Maximum or Minimum of the Range changes.
		/// </summary>
		public event Action<int, int> CapacityChanged;
		#endregion


		#region Properties
		public int Current
		{
			get { return this.value; }
			set 
			{ 
				if (value < this.minimum)
					value = this.minimum;
				if (value > this.maximum)
					value = this.maximum;
				//TODO: Set this back.
				//if (this.value != value)
					ValueChanged.Raise(value);
				this.value = value;
			}
		}

		
		/// <summary>
		/// Maximum value of the range.
		/// </summary>
		public int Maximum 
		{
			get { return this.maximum; }
			set 
			{ 
				if (this.maximum != value)
					CapacityChanged.Raise(this.minimum, this.maximum);
				this.maximum = value;
			}
		}


		/// <summary>
		/// Minimum value of the range.
		/// </summary>
		public int Minimum
		{
			get { return this.minimum; }
			set 
			{
				if (this.minimum != value)
					CapacityChanged.Raise(this.minimum, this.maximum);
				this.minimum = value;
			}
		}
		#endregion


		#region ICloneable
		public object Clone()
		{
			return this.MemberwiseClone();
		}
		#endregion


		/// <summary>
		/// Determines if another range is inside the bounds of this range.
		/// </summary>
		/// <param name="range">The child range to test.</param>
		/// <returns>True if range is inside, else false.</returns>
		public Boolean ContainsRange(IntegerRange range)
		{
			return 
				this.IsValid() && 
				range.IsValid() && 
				this.ContainsValue(range.Minimum) && 
				this.ContainsValue(range.Maximum);
		}


		/// <summary>
		/// Determines if the provided value is inside the range.
		/// </summary>
		/// <param name="value">The value to test.</param>
		/// <returns>True if the value is inside Range, else false.</returns>
		public Boolean ContainsValue(int value)
		{
			return 
				(Minimum.CompareTo(value) <= 0) && 
				(value.CompareTo(Maximum) <= 0);
		}

		
		/// <summary>
		/// Determines if this Range is inside the bounds of another range.
		/// </summary>
		/// <param name="range">The parent range to test on.</param>
		/// <returns>True if range is inclusive, else false.</returns>
		public Boolean IsInsideRange(IntegerRange range)
		{
			return 
				this.IsValid() && 
				range.IsValid() && 
				range.ContainsValue(this.Minimum) && 
				range.ContainsValue(this.Maximum);
		}


		/// <summary>
		/// Determines if the range is valid.
		/// </summary>
		/// <returns>True if range is valid, else false.</returns>
		public Boolean IsValid()
		{
			return Minimum.CompareTo(Maximum) <= 0;
		}


		/// <summary>
		/// Presents the Range in readable format.
		/// </summary>
		/// <returns>String representation of the Range.</returns>
		public override string ToString()
		{
			return String.Format("[{0} - {1} - {3}]", 
				this.minimum,
				this.value,
				this.maximum);
		}

	}
}