using System;
using System.Collections.Generic;

namespace mgaia.Utils.Persistence
{
	/// <summary>
	/// Persistence manager que grava somente na memória RAM, gravando no disco somente quando requisitado
	/// </summary>
	/// <owner>Victor</owner>
	public class BufferedPersistenceManager : PersistenceManager
	{
		private Dictionary<string, object> mSettedKeysAndValues = new Dictionary<string, object>();
		private Dictionary<string, object> mGotKeysAndValues = new Dictionary<string, object>();
		private Dictionary<string, Type> mKeysAndTypes = new Dictionary<string, Type>();
		private IPersistenceManagerPostProcessor mPostProcessor;

		public BufferedPersistenceManager(IPersistenceFileInterface persistenceFileInterface, IPersistenceManagerPostProcessor postProcessor):base(persistenceFileInterface)
		{
			mPostProcessor = postProcessor;
		}

		public BufferedPersistenceManager(IPersistenceFileInterface persistenceFileInterface):this(persistenceFileInterface, new DefaultPersistenceManagerPostProcessor())
		{
		}

		/// <summary>
		/// Retorna un valor genérico
		/// </summary>
		private T GetValue<T>(string key, T defaultValue)
		{
			Type parameterType = typeof(T);

			if (mGotKeysAndValues.ContainsKey(key))
			{
				if (mKeysAndTypes[key].Equals(parameterType))
				{
					return (T)mGotKeysAndValues[key];
				}
				else
				{
					return defaultValue;
				}
			}
			else
			{
				T fileValue = (T)mPostProcessor.ProcessFromInterface(key, defaultValue, parameterType, PersistenceFileInterface);

				mGotKeysAndValues.Add(key, fileValue);
				mKeysAndTypes.Add(key, parameterType);

				return fileValue;
			}
		}

		/// <summary>
		/// Seta um valor genérico
		/// </summary>
		private void SetValue<T>(string key, T value)
		{
			Type parameterType = typeof(T);

			if (mGotKeysAndValues.ContainsKey(key))
			{
				mGotKeysAndValues[key] = value;
				mKeysAndTypes[key] = parameterType;
			}
			else
			{
				mGotKeysAndValues.Add(key, value);
				mKeysAndTypes.Add(key, parameterType);
			}

			if (mSettedKeysAndValues.ContainsKey(key))
			{
				mSettedKeysAndValues[key] = value;
				
			}
			else
			{
				mSettedKeysAndValues.Add(key, value);
			}
		}

		public override float GetFloat(string key, float defaultValue)
		{
			return GetValue<float>(key, defaultValue);
		}

		public override int GetInt32(string key, int defaultValue)
		{
			return GetValue<int>(key, defaultValue);
		}

		public override string GetString(string key, string defaultValue)
		{
			return GetValue<string>(key, defaultValue);
		}

		public override void SetFloat(string key, float value)
		{
			SetValue<float>(key, value);
		}

		public override void SetInt32(string key, int value)
		{
			SetValue<int>(key, value);
		}

		public override void SetString(string key, string value)
		{
			SetValue<string>(key, value);
		}

		public override void DeleteKey(string key)
		{
			base.DeleteKey(key);

			mSettedKeysAndValues.Remove(key);
			mGotKeysAndValues.Remove(key);
			mKeysAndTypes.Remove(key);
		}

		public override void DeleteAll()
		{
			string[] keysToRemove = mSettedKeysAndValues.Keys.ToArray();

			foreach (string key in keysToRemove)
			{
				mSettedKeysAndValues.Remove(key);
				mGotKeysAndValues.Remove(key);
				mKeysAndTypes.Remove(key);
			}

			base.DeleteAll();
		}

		public override bool HasKey(string key)
		{
			if (mGotKeysAndValues.ContainsKey(key))
			{
				return true;
			}
			else
			{
				return base.HasKey(key);
			}
		}

		/// <summary>
		/// Salva um valor que está na memória RAM diretamente no disco
		/// </summary>
		private void SaveValue(string key, object value, Type typeOfValue)
		{
			mPostProcessor.SaveToInterface(key, value, typeOfValue, PersistenceFileInterface); 
		}

		/// <summary>
		/// Salva todos os valores da memória RAM no disco
		/// </summary>
		public override void Save()
		{
			foreach (string key in mSettedKeysAndValues.Keys)
			{
				object value = mSettedKeysAndValues[key];
				Type typeOfValue = mKeysAndTypes[key];

				SaveValue(key, value, typeOfValue);
			}
		}
	}
}