Animator/AnimationDictionary.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using Sandbox;

#nullable enable
namespace CrosshairMaker.Animator
{
	public sealed class AnimationDictionary : IDictionary<int,CrosshairAnimation> , IDictionary
	{
		//Init
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		public void Init(AnimationValueSetter setterCollection )
		{
			if (AnimationSize.Length != 0) this.Clear();
			ValueSetters = setterCollection;

		}
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		//Init

		//General features
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		/// <summary>
		/// Add an animation the same size as the others in this AnimationDictionary with given key , returns the key if sucessful
		/// throws an error if AnimationDictionary is empty
		/// throws an error if key is already contained in this AnimationDictionary
		/// </summary>
		/// <param name="key">key of animation</param>
		/// <returns></returns>
		/// <exception cref="InvalidOperationException"></exception>
		public int AddNewAnimation( int key )
		{
			if ( AnimationSize.Length == 0 ) throw new InvalidOperationException( "Cannot create an animation, no setters found" );
			if ( _internalDic.ContainsKey( key ) ) throw new InvalidOperationException( $"Cannot create an animation in an already existing key, animation {key} is {_internalDic[key]}" );
			CrosshairAnimation newAnim = CrosshairAnimation.FromPropertyCount( AnimationSize );
			_internalDic.Add( key, newAnim );
			return key;
		}
		/// <summary>
		/// Add an animation the same size as the others in this AnimationDictionary in first avaliable key between 0 -> (int.MaxValue - 1) , returns the key if sucessful
		/// throws an exception if AnimationDictionary is empty
		/// throws an exception if all keys from 0 to (int.MaxValue - 1) are filled
		/// </summary>
		/// <returns></returns>
		/// <exception cref="InvalidOperationException"></exception>
		public int AddNewAnimation()
		{
			_internalDic ??= new();
			if ( AnimationSize.Length == 0 ) throw new InvalidOperationException( "Cannot create an animation, no setters found" );
			for ( int i = 0; true; i++ )
			{
				if ( i == int.MaxValue ) throw new InvalidOperationException( $"AnimationDictionary could not find a key smaller than {int.MaxValue} to add a new animation" );
				if ( _internalDic.ContainsKey( i ) ) continue;
				CrosshairAnimation newAnim = CrosshairAnimation.FromPropertyCount( AnimationSize );
				_internalDic.Add( i, newAnim ); //Skips the implemented Add method, since both validity conditions are met
				return i;
			}
		}
		/// <summary>
		/// Returns the expected animation size based on the setters
		/// </summary>
		public int[] AnimationSize => ValueSetters?.Sizes ?? Array.Empty<int>();
		public override string ToString()
		{
			int[] s = AnimationSize;
			return (s.Length == 3) ? 
				$"AnimationSize : [float:{s[0]}, int:{s[1]}, Color:{s[2]}]" : 
				"No AnimationSize set : length = " + s.Length;
		}
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		//General features
		//Overriden Dictionary behaviour
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		public void Add( int key, CrosshairAnimation value )
		{
			_internalDic ??= new Dictionary<int,CrosshairAnimation>();
			if ( CrosshairAnimation.SameSize( AnimationSize,value )) _internalDic.Add( key, value );
		}
		public CrosshairAnimation this[int key]
		{
			get => _internalDic[key];
			set
			{
				if ( CrosshairAnimation.SameSize( value,AnimationSize ) ) _internalDic[key] = value;
				else throw new InvalidOperationException( "Cannot insert an animation of a different size from the others" );
			}
		}
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		//Overriden Dictionary behaviour

		//Editor features
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
#pragma warning disable
#pragma warning enable
		public string[] FloatsPropertyNames;
		public string[] IntsPropertyNames;
		public string[] ColorsPropertyNames;
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		//Editor features

		//Internal properties
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		public AnimationValueSetter? ValueSetters { get; private set; }
		private Dictionary<int, CrosshairAnimation> _internalDic;
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		//Internal properties

		//Unchanged IDictionary Implementations
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		public void Add( KeyValuePair<int, CrosshairAnimation> item ) => Add( item.Key, item.Value );
		public bool ContainsKey( int key ) => _internalDic.ContainsKey( key );
		public bool Remove( int key ) => _internalDic.Remove( key );
		public bool TryGetValue( int key, [MaybeNullWhen( false )] out CrosshairAnimation value ) => _internalDic.TryGetValue( key, out value );
		public void Clear() => _internalDic.Clear();
		public bool Contains( KeyValuePair<int, CrosshairAnimation> item ) => _internalDic.Contains( item );
		private void CopyTo( KeyValuePair<int, CrosshairAnimation>[] array, int index )
		{
			if ( array == null ) throw new ArgumentNullException();
			if ( (uint)index > (uint)array.Length ) throw new ArgumentOutOfRangeException( "Array is too short to handle offset" );
			if ( array.Length - index < Count ) throw new ArgumentOutOfRangeException( "Array is too short to handle offset" );

			foreach ( var kvp in _internalDic )
			{
				array[index++] = new KeyValuePair<int, CrosshairAnimation>( kvp.Key, kvp.Value );
			}
		}
		public bool Remove( KeyValuePair<int, CrosshairAnimation> item )
		{
			bool success = _internalDic.TryGetValue( item.Key, out CrosshairAnimation correct );
			if ( !success ) return false;
			success = correct == item.Value;
			if ( success ) _internalDic.Remove( item.Key );
			return success;
		}
		public IEnumerator<KeyValuePair<int, CrosshairAnimation>> GetEnumerator() => _internalDic.GetEnumerator();
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();

		void ICollection<KeyValuePair<int, CrosshairAnimation>>.CopyTo( KeyValuePair<int, CrosshairAnimation>[] array, int arrayIndex ) => CopyTo( array, arrayIndex );

		public void Add( object key, object value )
		{
			if(key is not int ik) throw new InvalidCastException();
			if(value is not CrosshairAnimation av) throw new InvalidCastException();
			Add( ik, av );
		}

		public bool Contains( object key ) => key is int ik ? _internalDic.ContainsKey( ik ) : false;

		IDictionaryEnumerator IDictionary.GetEnumerator() => _internalDic.GetEnumerator();

		public void Remove( object key )
		{
			if( key is int ik) _internalDic.Remove( ik );
		}

		public void CopyTo( Array array, int index )
		{
			if(array == null) throw new ArgumentNullException();
			if(array.Rank != 1) throw new ArgumentException("Array cannot be multi-dimentional");
			if ( array.GetLowerBound( 0 ) != 0 ) throw new ArgumentException( "Array's lower bound must be 0" );
			if ( (uint)index > (uint)array.Length ) throw new ArgumentOutOfRangeException( "Array is too short to handle offset" );
			if ( array.Length - index < Count ) throw new ArgumentOutOfRangeException( "Array is too short to handle offset" );

			if ( array is KeyValuePair<int, CrosshairAnimation>[] kvpArr )
			{
				CopyTo( kvpArr, 0 );
			}
			else if ( array is DictionaryEntry[] deArr )
			{
				foreach(var kvp in _internalDic )
				{
					deArr[index++] = new DictionaryEntry( kvp.Key, kvp.Value );
				}
			}
			else if ( array is object[] objArr )
			{

			}
			else throw new ArgumentException( "Array type is not compatible" );

		}

		public ICollection<int> Keys => _internalDic.Keys;
		public ICollection<CrosshairAnimation> Values => _internalDic.Values;
		public int Count => _internalDic.Count;
		public bool IsReadOnly => false;

		public bool IsFixedSize => false;

		ICollection IDictionary.Keys => _internalDic.Keys;

		ICollection IDictionary.Values => _internalDic.Values;

		public bool IsSynchronized => false;

		public object SyncRoot => _internalDic;

		public object this[object key] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
		//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//
		//Unchanged IDictionary Implementations
	}
}