#pragma warning disable CS8601 // Possible null reference assignment.

using System.Runtime.CompilerServices;
using eNavigator.Interfaces;
using MarketData.MarketDataModel;

namespace eNavigator.Models
{
  public class StateContainer : IStateContainer
  {
    private Dictionary<String, StateItem> stateContainer = new Dictionary<String, StateItem>();

    public StateContainer()
    {
    }

    public T Get<T>(String name, [CallerMemberName] string callerMember = null)
    {
      T result = default(T);

      if(!Has(name))
      {
        String message=$"Item {name}, referenced from {callerMember} does not exist in the state container";
        throw new ArgumentOutOfRangeException(message);
      }

      StateItem stateItem = stateContainer[name];
      Object value = stateItem.StateObject;

      if(value is null)return result;

      String typeName = value.GetType().Name;

      if(value.GetType().Equals(typeof(T)) || !(value is IConvertible))result = (T)value;
      else result = (T)Convert.ChangeType(stateContainer[name].StateObject,typeof(T));
      return result;
    }

    public T Coalesce<T>(String name, T defaultValue = default(T),[CallerMemberName] String callerMember = null)
    {
      T result = default(T);

      if(!Has(name))return defaultValue;

      StateItem stateItem = stateContainer[name];
      Object value = stateItem.StateObject;

      if(value is null)return result;

      String typeName = value.GetType().Name;

      if(value.GetType().Equals(typeof(T)) || !(value is IConvertible))result = (T)value;
      else result = (T)Convert.ChangeType(stateContainer[name].StateObject,typeof(T));
      return result;      
    }

    public List<String> GetNames()
    {
      List<String> names = stateContainer.Keys.ToList<String>();
      names.Sort();
      return names;
    }

    public void Clear()
    {
      stateContainer = new Dictionary<String, StateItem>();
    }

    public IStateContainer InsertOrUpdate(String name, Object value, [CallerMemberName] String callerMember = null)
    {
      if(Has(name))
      {
        StateItem stateItem = stateContainer[name];
        stateItem.CallerMember = callerMember;
        stateItem.StateObject = value;
        return this;
      }
      else
      {
        stateContainer.Add(name, new StateItem(){StateObject = value, CallerMember= callerMember});
        return this;
      }
    }

    public void Remove(String name)
    {
      if(!stateContainer.ContainsKey(name))return;

      Object o = Get<Object>(name);
      if(0 != default && o is IDisposable)(o as IDisposable).Dispose();
      stateContainer.Remove(name);
    }

    public bool Has(String name)
    {
      return stateContainer.ContainsKey(name);
    }
  }
}
#pragma warning restore CS8601 // Possible null reference assignment.
