using StructureHelperCommon.Infrastructures.Exceptions;
using StructureHelperCommon.Models;
using StructureHelperCommon.Models.Projects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;

namespace StructureHelperCommon.Infrastructures.Interfaces
{
    /// <summary>
    /// Find object in refernce dictionary, if it exists return existing object, else return new one by child logic 
    /// </summary>
    /// <typeparam name="T">Type of target object</typeparam>
    /// <typeparam name="V">Type of source object</typeparam>
    public class DictionaryConvertStrategy<T, V> : IDictionaryConvertStrategy<T, V> where T : ISaveable
        where V : ISaveable
    {
        public IShiftTraceLogger? TraceLogger { get; set; }
        public IConvertStrategy<T, V> ConvertStrategy { get; set; }
        public Dictionary<(Guid id, Type type), ISaveable> ReferenceDictionary { get; set; }

        public DictionaryConvertStrategy(IBaseConvertStrategy baseConvertStrategy, IConvertStrategy<T, V> convertStrategy)
        {
            ReferenceDictionary = baseConvertStrategy.ReferenceDictionary;
            TraceLogger = baseConvertStrategy.TraceLogger;
            ConvertStrategy = convertStrategy;
        }

        public DictionaryConvertStrategy(Dictionary<(Guid id, Type type), ISaveable> referenceDictionary, IShiftTraceLogger? traceLogger, IConvertStrategy<T, V> convertStrategy)
        {
            ReferenceDictionary = referenceDictionary;
            TraceLogger = traceLogger;
            ConvertStrategy = convertStrategy;
        }
        public DictionaryConvertStrategy()
        {

        }

        public DictionaryConvertStrategy(Dictionary<(Guid id, Type type), ISaveable> referenceDictionary,
            IConvertStrategy<T, V> convertStrategy,
            IShiftTraceLogger traceLogger)
        {
            ReferenceDictionary = referenceDictionary;
            ConvertStrategy = convertStrategy;
            TraceLogger = traceLogger;
        }

        public T Convert(V source)
        {
            CheckInputData();
            T val;
            var key = (source.Id, typeof(T));
            if (ReferenceDictionary.ContainsKey(key))
            {
                ISaveable existValue;
                ReferenceDictionary.TryGetValue(key, out existValue);
                val = (T)existValue;
                TraceLogger?.AddMessage($"Value of {typeof(T)} (Id = {existValue.Id}) exists already", TraceLogStatuses.Debug);
            }
            else
            {
                val = ConvertStrategy.Convert(source);
                ReferenceDictionary.Add(key, val);
                TraceLogger?.AddMessage($"New value of {typeof(T)} (Id = {val.Id}) was added to dictionary", TraceLogStatuses.Debug);
            }
            return val;
        }
        private void CheckInputData()
        {
            if (ReferenceDictionary is null)
            {
                string errorString = ErrorStrings.ParameterIsNull + ": Reference Dictionary";
                TraceLogger?.AddMessage(errorString, TraceLogStatuses.Error);
                throw new StructureHelperException(errorString);
            }
        }

    }
}
