using System;
using System.Collections.Generic;
using UnityEngine;
using mgaia.Utils.Security;

namespace mgaia.Utils.Persistence
{
	/// <summary>
	/// Classe que encapsula o acesso a um arquivo de persistência, utilizando um processo de criptografia e consistência para evitar injeção de valores forçados
	/// </summary>
	/// <owner>Victor</owner>
	public class SafePersistenceManager : PersistenceManager
	{
		private string mSalt;
		private ICryptoProcessor mCryptoProcessor;

		public SafePersistenceManager(IPersistenceFileInterface persistenceFileInterface, ICryptoProcessor cryptoProcessor, string salt) : base(persistenceFileInterface)
		{
			mSalt = salt;
			mCryptoProcessor = cryptoProcessor;
		}

		/// <summary>
		/// Retorna uma string, caso não exista ou se a integridade estiver violada, retorna o valor padrão
		/// </summary>
		public override string GetString(string key, string defaultValue)
		{
			return GetStringValue(key, defaultValue);
		}

		/// <summary>
		/// Retorna um int32, caso não exista ou se a integridade estiver violada, retorna o valor padrão
		/// </summary>
		public override int GetInt32(string key, int defaultValue)
		{
			int valueToReturn = defaultValue;

			if (int.TryParse(GetStringValue(key, defaultValue), out valueToReturn))
			{
                return valueToReturn;
			}
			else
			{
				return defaultValue;
			}
		}

		/// <summary>
		/// Retorna um float, caso não exista ou se a integridade estiver violada, retorna o valor padrão
		/// </summary>
		public override float GetFloat(string key, float defaultValue)
		{
			float valueToReturn = defaultValue;

			if (float.TryParse(GetStringValue(key, defaultValue), out valueToReturn))
			{
				return valueToReturn;
			}
			else
			{
				return defaultValue;
			}
		}

		public override void SetString(string key, string value)
		{
			SetStringValue(key, value);
		}

		public override void SetInt32(string key, int value)
		{
			SetStringValue(key, value);
		}

		public override void SetFloat(string key, float value)
		{
			SetStringValue(key, value);
		}

		/// <summary>
		/// Tenta buscar o valor em string na chave, caso não exista ou se a integridade estiver violada, retorna o valor padrão
		/// </summary>
		private string GetStringValue(string key, object defaultValue)
		{
			string defaultValueString = defaultValue.ToString();
			string defaultEncryptedValue = EncryptValue(defaultValueString);
			string fileEncryptedValue = base.GetString(key, defaultEncryptedValue);

            try
			{
				string decryptedValue = mCryptoProcessor.Decrypt(fileEncryptedValue);

                if (decryptedValue.IndexOf(mSalt) == 0)
				{
					return decryptedValue.Substring(mSalt.Length);
				}
				else
				{
					return defaultValueString;
				}
			}
			catch (UnableToDecryptException)
			{
				return defaultValueString;
			}
		}

		/// <summary>
		/// Converte o valor do objeto enviado para string, criptografa ele e salva no arquivo na chave enviada
		/// </summary>
		private void SetStringValue(string key, object value)
		{
            string encryptedValue = EncryptValue(value.ToString());
			base.SetString(key, encryptedValue);
		}

		/// <summary>
		/// Criptograda um valor em string 
		/// </summary>
		private string EncryptValue(string value)
		{
			return mCryptoProcessor.Encrypt(mSalt + value);
		}
	}
}