using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;

namespace IndianHealthService.BMXNet.WinForm.Configuration
{
    /// <summary>
    /// Provides settings interface and implementation for settings from command-line,
    /// stored in PersistentStore, or hardcoded in application.
    /// </summary>
    public class Settings
    {
        [XmlIgnore()]
        private bool _willPersistFutureSets = false;

        /// <summary>
        /// After set to true, every setting update will be flushed the PersistentStore if
        /// the Settings is configured with one
        /// </summary>
        public bool WillPersistFutureSets
        {
            get { return _willPersistFutureSets; }
            set { _willPersistFutureSets = value; }
        }


        /// <summary>
        /// Answer true if the current Settings IsPersistent.  It is possible that the Backup
        /// Settings IsPersistennt
        /// </summary>
        [XmlIgnore()]
        public bool IsPersistent
        {
            get { return this.Store != null; }
        }


        private List<Setting> _settingList = new List<Setting>();

        internal List<Setting> SettingList
        {
            get { return _settingList; }
            set { _settingList = value; }
        }

        private Dictionary<String, Setting> _settingLookup = null;

        [XmlIgnore()]
        internal Dictionary<String, Setting> SettingLookup
        {
          get {
              if (_settingLookup == null)
              {
                  _settingLookup = this.BuildLookup(this.SettingList);
              }
              return _settingLookup; }
          set { _settingLookup = value; }
        }

        /// <summary>
        /// Update the Setting value aName to aBool
        /// </summary>
        /// <param name="aName">Name of the Setting</param>
        /// <param name="aBool">Boolean value to set</param>
        /// <returns>True if the setting was changed, False if the setting was the same value</returns>
        public bool Set(String aName, bool aBool)
        {
            return this.Set(aName, aBool ? "True" : "False");
        }

        /// <summary>
        /// Update the Setting value aName to anInt
        /// </summary>
        /// <param name="aName">Name of the Setting</param>
        /// <param name="aBool">Integer value to set</param>
        /// <returns>True if the setting was changed, False if the setting was the same value</returns>
        public bool Set(String aName, int anInt)
        {
            return this.Set(aName, anInt.ToString());
        }

        /// <summary>
        /// Update the Setting value aName to a string
        /// </summary>
        /// <param name="aName">Name of the Setting</param>
        /// <param name="aBool">String value to set</param>
        /// <returns>True if the setting was changed, False if the setting was the same value</returns>
        public bool Set(String aName, String aValue)
        {
            Setting current = this.GetLocalSetting(aName);

            if (current == null)
            {
                if (this.WillPersistFutureSets)
                {
                    current = new Setting() { Name = aName, Value = aValue };
                    this.AddSetting(current);
                }
            }
            else
            {
                if (current.Value == aValue)
                    return false;
                current.Value = aValue;
            }


            if (this.IsPersistent)
            {
                this.Save();
            }
            else if (this.BackupSettings != null)
            {
                this.BackupSettings.Set(aName, aValue);
            }
            return true;
        }

            
        public Settings BackupSettings { get; set; }

        internal void Clear()
        {
            this.SettingLookup.Clear();
        }

        private string[] _storageKeys = null;

        internal string[] StorageKeys
        {
            get { return _storageKeys; }
            set { _storageKeys = value; }
        }

        private PersistentStore _store = null;

        internal PersistentStore Store
        {
            get { return _store; }
            set { _store = value; }
        }


        private XmlSerializer _serializer = null;

        internal XmlSerializer Serializer
        {
            get
            {
                if (this._serializer == null)
                {
                    this._serializer = new XmlSerializer(typeof(Settings));
                }
                return _serializer;              
            }
        }

        /// <summary>
        /// Get the string Setting named aString.  Answer the defaultValue if the Setting named
        /// aString does not exist in the receiver or it's Backup chain.
        /// </summary>
        /// <param name="aString">Name of the setting</param>
        /// <param name="defaultValue">Default value</param>
        /// <returns></returns>
        public String Get(String aString, String defaultValue)
        {
            Setting hit = this.GetSetting(aString);

            return (hit == null) ? defaultValue : hit.Value;
        }

        /// <summary>
        /// Get the boolean Setting named aString.  Answer the defaultValue if the Setting named
        /// aString does not exist in the receiver or it's Backup chain.
        /// </summary>
        /// <param name="aString">Name of the setting</param>
        /// <param name="defaultValue">Default value</param>
        /// <returns></returns>
        public bool Get(String aString, bool defaultValue)
        {
            Setting hit = this.GetSetting(aString);

            return (hit == null) ? defaultValue : "True".Equals(hit.Value,StringComparison.InvariantCultureIgnoreCase);    
        }

        /// <summary>
        /// Get the Integer Setting named aString.  Answer the defaultValue if the Setting named
        /// aString does not exist in the receiver or it's Backup chain.
        /// </summary>
        /// <param name="aString">Name of the setting</param>
        /// <param name="defaultValue">Default value</param>
        /// <returns></returns>
        public int Get(String aString, int defaultValue)
        {
            Setting hit = this.GetSetting(aString);

            int value = defaultValue;

            return (hit == null) ? defaultValue : (int.TryParse(hit.Value, out value) ? value : defaultValue);
        }


        internal Setting GetLocalSetting(String aString)
        {
            Setting result = null;

            if (this.SettingLookup.TryGetValue(aString.ToLower(), out result))
            {
                return result;
            }
            else
            {
                return null;
            }
        }


        internal Setting GetSetting(String aString)
        {
            Setting result = this.GetLocalSetting(aString);

            if (result !=null)
            {
                return result;
            }
            else if (this.BackupSettings != null)
            {
                return this.BackupSettings.GetSetting(aString);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Merge the settings from someSettings into the receiver
        /// </summary>
        /// <param name="someSettings">Settings to copy</param>
        public void MergeSettings(Settings someSettings)
        {
            someSettings.SettingList.ForEach(each => this.AddSetting(each));
        }

        internal Dictionary<String, Setting> BuildLookup(List<Setting> someSettings)
        {
            Dictionary<String, Setting> answer = new Dictionary<string, Setting>();
            foreach (Setting each in someSettings)
            {
                answer[each.Name.ToLower()] = each;
            }
            return answer;
        }

        internal void AddSetting(Setting aSetting)
        {
            Setting hit = this.GetSetting(aSetting.Name.ToLower());
            if (hit != null)
            {
                this.SettingList.Remove(hit);
            }

            this.SettingList.Add(aSetting);
            this._settingLookup = null;
         }


       /// <summary>
       /// Load the receiver's settings from the PersistentStore
       /// </summary>
        public void Load()
        {
            this.SettingList = this.Store.Read<List<Setting>>(this.StorageKeys);
            if (this.SettingList == null)
                this.SettingList = new List<Setting>();

        }

        /// <summary>
        /// Save the receiver's settings to the PersistentStore
        /// </summary>
        public void Save()
        {         
            this.Store.Write<List<Setting>>(this.SettingList, this.StorageKeys);
        }

    }
}
