using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.IO;
using System.Xml.Serialization;

namespace digioz
{
    public static class SettingsManager
    {
        static SettingsManager()
        {
            coPortalSettings = PortalSettingsManagement.GetPortalSettings();
            coDB = DataFactory.GetDataObject(coPortalSettings);
        }

        public static Setting GetSettingByName(string psFullName)
        {
            return GetSettingByName<string>(psFullName);
        }

        public static Setting<tValue> GetSettingByName<tValue>(string psFullName)
        {
            SettingList<tValue> loSettings = new SettingList<tValue>();
            Setting<tValue> loSetting = null;
            DbParameter[] loParameters = null;
            DataTable loTable;
            int liCount;
            if (coPortalSettings.DBRelational)
            {
                BuildGetByNameParameterList(psFullName, ref loParameters);

                coDB.openConnection();
                loTable = coDB.QueryDBDatasetProc("spGetSettingByName", out liCount, loParameters);
                coDB.closeConnection();

                loSettings = CreateSetting<tValue>(loTable);

                foreach (Setting<tValue> loCurrentSetting in loSettings)
                {
                    if (loCurrentSetting.FullName == psFullName)
                    {
                        loSetting = loCurrentSetting;
                        break;
                    }
                }
            }
            return loSetting;
        }

        public static Setting GetSettingById(int piId)
        {
            return GetSettingById<string>(piId);
        }

        public static Setting<tValue> GetSettingById<tValue>(int piId)
        {
            SettingList<tValue> loSettings = new SettingList<tValue>();
            Setting<tValue> loSetting = null;
            DbParameter[] loParameters = null;
            DataTable loTable;
            int liCount;
            if (coPortalSettings.DBRelational)
            {
                BuildGetByIdParameterList(piId, ref loParameters);

                coDB.openConnection();
                loTable = coDB.QueryDBDatasetProc("spGetSettingById", out liCount, loParameters);
                coDB.closeConnection();

                loSettings = CreateSetting<tValue>(loTable);

                foreach (Setting<tValue> loCurrentSetting in loSettings)
                {
                    if (loCurrentSetting.Id == piId)
                    {
                        loSetting = loCurrentSetting;
                        break;
                    }
                }
            }
           return loSetting;
        }

        public static bool Save(Setting poSetting)
        {
            return Save(new Setting<string>(ref poSetting));
        }

        public static bool Save<tValue>(Setting<tValue> poSetting)
        {
            bool lbReturnValue = false;
            if (poSetting != null)
            {
                if (coPortalSettings.DBRelational)
                {
                    int liCount = 0;
                    DbParameter[] loParameters = null;
                    BuildSaveParameterList(poSetting, ref loParameters);

                    coDB.openConnection();
                    coDB.ExecDBProc("spSaveSetting", out liCount, loParameters);
                    coDB.closeConnection();

                    poSetting.Key.Id = (int)loParameters[1].Value;

                    lbReturnValue = string.IsNullOrEmpty(coDB.sErr);
                }
            }
            return lbReturnValue;
        }

        public static bool Delete(int piInt)
        {
            int liCount;
            DbParameter[] loParameters = null;
            BuildDeleteParameterList(piInt, ref loParameters);
            coDB.openConnection();
            coDB.ExecDBProc("spDeleteSettings", out liCount, loParameters);
            coDB.closeConnection();
            return string.IsNullOrEmpty(coDB.sErr);
        }

        private static Setting<tValue> CreateSetting<tValue>(DataRow poRow)
        {
            Setting<tValue> loSetting = null;
            tValue lgValue = default(tValue);
            XmlSerializer loXmlSerializer;
            StringReader loStringReader;
            string lsXml;
            if (poRow != null)
            {
                lsXml = poRow["value"].ToString();
                if (!String.IsNullOrEmpty(lsXml))
                {
                    loXmlSerializer = new XmlSerializer(typeof(tValue));
                    loStringReader = new StringReader(lsXml);
                    lgValue = (tValue)loXmlSerializer.Deserialize(loStringReader);
                }
                loSetting = new Setting<tValue>((int)poRow["id"], (int)poRow["parent_id"], (string)poRow["fullname"], (string)poRow["name"], lgValue, (bool)poRow["enabled"]);
            }
            return loSetting;
        }

        private static SettingList<tValue> CreateSetting<tValue>(DataTable poTable)
        {
            SettingList<tValue> loSettings = new SettingList<tValue>();
            Setting<tValue> loCurrentSetting;
            foreach (DataRow loRow in poTable.Rows)
            {
                loCurrentSetting = CreateSetting<tValue>(loRow);
                foreach (Setting loPreviousSetting in loSettings)
                {
                    if (loPreviousSetting.Key.Id == loCurrentSetting.Key.ParentId)
                    {
                        loPreviousSetting.Children.Add(loCurrentSetting);
                    }
                    else if (loCurrentSetting.Key.Id == loPreviousSetting.Key.ParentId)
                    {
                        loCurrentSetting.Children.Add(loPreviousSetting);
                    }
                }
                loSettings.Add(loCurrentSetting);
            }
            return loSettings;
        }

        private static void BuildSaveParameterList<tValue>(Setting<tValue> poSetting, ref DbParameter[] poParameters)
        {
            XmlSerializer loSerializer = new XmlSerializer(typeof(tValue));
            StringWriter loWriter = new StringWriter();
            loSerializer.Serialize(loWriter, poSetting.Value);

            poParameters = new DbParameter[5];

            poParameters[0] = new SqlParameter();
            poParameters[0].ParameterName = "@parent_id";
            poParameters[0].DbType = DbType.Int32;
            poParameters[0].Value = poSetting.Key.ParentId;

            poParameters[1] = new SqlParameter();
            poParameters[1].ParameterName = "@id";
            poParameters[1].DbType = DbType.Int32;
            poParameters[1].Direction = ParameterDirection.InputOutput;
            poParameters[1].Value = poSetting.Key.Id;

            poParameters[2] = new SqlParameter();
            poParameters[2].ParameterName = "@name";
            poParameters[2].DbType = DbType.String;
            poParameters[2].Value = poSetting.Name;

            poParameters[3] = new SqlParameter();
            poParameters[3].ParameterName = "@value";
            poParameters[3].DbType = DbType.Xml;
            poParameters[3].Value = loWriter.ToString();

            poParameters[4] = new SqlParameter();
            poParameters[4].ParameterName = "@enabled";
            poParameters[4].DbType = DbType.Boolean;
            poParameters[4].Value = poSetting.Enabled;
        }

        private static void BuildGetByNameParameterList(string psFullName, ref DbParameter[] poParameters)
        {
            poParameters = new DbParameter[2];

            poParameters[0] = new SqlParameter();
            poParameters[0].ParameterName = "@fullname";
            poParameters[0].DbType = DbType.String;
            poParameters[0].Value = psFullName;

            poParameters[1] = new SqlParameter();
            poParameters[1].ParameterName = "@delimiter";
            poParameters[1].DbType = DbType.String;
            poParameters[1].Size = 1;
            poParameters[1].Value = Setting.DELIMITER;
        }

        private static void BuildGetByIdParameterList(int piId, ref DbParameter[] poParameters)
        {
            poParameters = new DbParameter[2];

            poParameters[0] = new SqlParameter();
            poParameters[0].ParameterName = "@id";
            poParameters[0].DbType = DbType.Int32;
            poParameters[0].Value = piId;

            poParameters[1] = new SqlParameter();
            poParameters[1].ParameterName = "@delimiter";
            poParameters[1].DbType = DbType.String;
            poParameters[1].Size = 1;
            poParameters[1].Value = Setting.DELIMITER;
        }

        private static void BuildDeleteParameterList(int piInt, ref DbParameter[] poParameters)
        {
            poParameters = new DbParameter[1];

            poParameters[0] = new SqlParameter();
            poParameters[0].ParameterName = "@id";
            poParameters[0].DbType = DbType.Int32;
            poParameters[0].Value = piInt;
        }

        private static PortalSettings coPortalSettings;
        private static IDataRelational coDB;
    }
}