using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sdaleo;
using Sdaleo.Systems.SQLServer;
using System.Data;
using Sdaleo.Systems;
using System.Net;
using RegistrationServer;

namespace Pluto.Registration
{
    internal static class DataAccessLayer
    {
        private static SQLServerCredential credential = null;
        private static DB db = null;
        private const int PORT_EMPTY = 0;

        internal static string MAKE_SURE_APOSTROPHY_IS_SQL_READY(string str)
        {
            if (!String.IsNullOrEmpty(str))
            {
                str = str.Replace("'", "''");
                return str;
            }
            return String.Empty;
        }

        internal static string SqlDTString(DateTime dt)
        {
            if (dt.Year < 1753)  // sql server limit check.
            {
                return "01/01/1753 01:01:01 AM";
            }

            string s = dt.ToShortDateString() + " " + dt.ToLongTimeString();
            return s;
        }

        /// <summary>
        /// Const will be used when registering in order to distinguish an internal Practice from a real Customer
        /// </summary>
        public const string INTERNAL_TEST_PRACTICE = " (Mck)";

        /// <summary>
        /// Data Layer
        /// </summary>
        static DataAccessLayer()
        {
            credential = new SQLServerCredential(Configuration.SQLServer, Configuration.SQLInstance, Configuration.SQLDatabaseName, Configuration.SQLUsername, Configuration.SQLPassword);
            db = new DB(credential);
        }

        /// <summary>
        /// Verify General Connectivity to the SQL Server
        /// </summary>
        /// <returns>true if it can connect to the SQL Server Instance</returns>
        internal static bool VerifyConnectivity()
        {
            SQLServerVersion version;
            DBError dbError = SQLServerGeneral.GetSQLServerVersion(credential, out version);
            return !dbError.ErrorOccured;
        }

        #region Sanity Existence Checking 

        /// <summary>
        /// Check that a Host Guid Exists in the Table
        /// </summary>
        /// <param name="Host_Guid"></param>
        /// <returns></returns>
        internal static bool Host_Guid_Exist(Guid Host_Guid)
        {
            // Check Host GUID
            if (Host_Guid == null || String.IsNullOrEmpty(Host_Guid.ToString()))
                return false;  

            DBRetVal retVal = db.ExecuteScalar(String.Format("Select [ID] From [Hosts_Guids] Where [Host_Guid] = '{0}'", Host_Guid));
            if (retVal.ErrorOccured)
                Registration.Logger.Error("Host_Guid_Exist Error:{0}", retVal.ErrorMsg);
            return retVal.IsValid;
        }

        /// <summary>
        /// Check that the specified System Api Key Exists
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <returns></returns>
        internal static bool System_Api_Key_Exist(string SystemApiKey)
        {
            if (!String.IsNullOrEmpty(SystemApiKey))
            {
                DBRetVal retVal = db.ExecuteScalar(String.Format("Select [ID] From [Hosts_Guids] Where [SystemApiKey] = '{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(SystemApiKey)));
                if (retVal.ErrorOccured)
                    Registration.Logger.Error("System_Api_Key_Exist Error:{0}", retVal.ErrorMsg);
                return retVal.IsValid;
            }
            return false;
        }

        #endregion

        #region Retrieve IP n Port Stuff

        /// <summary>
        /// Retrieve the Wifi IP and Port
        /// </summary>
        /// <param name="systemApiKey"></param>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="practiceName"></param>
        /// <returns></returns>
        internal static bool RetrieveIPWifiAndPort(string systemApiKey, out string ip, out int port, out string practiceName)
        {
            ip = String.Empty;
            port = PORT_EMPTY;
            practiceName = String.Empty;
            if (!String.IsNullOrEmpty(systemApiKey))
            {
                DBRetVal retVal = db.FillDataTable(String.Format("Select [Internal_IP],[Port],[Practice_Name] From [Hosts_Guids] Where [SystemApiKey] = '{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(systemApiKey)));
                if (retVal.IsValid)
                {
                    DataRow row = retVal.GetDataTableFirstRow();
                    ip = DataRet.Retrieve(row[0]);
                    port = DataRet.Retrieve<int>(row[1], PORT_EMPTY);
                    practiceName = DataRet.Retrieve(row[2]);
                    return true;
                }
                if (retVal.ErrorOccured)
                    Registration.Logger.Error("RetrieveIPWifiAndPort Error:{0}", retVal.ErrorMsg);
            }
            return false;
        }

        /// <summary>
        /// Retrieve the Mobile IP and Port
        /// </summary>
        /// <param name="systemApiKey"></param>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="practiceName"></param>
        /// <returns></returns>
        internal static bool RetrieveIPMobileAndPort(string systemApiKey, out string ip, out int port, out string practiceName)
        {
            ip = String.Empty;
            port = PORT_EMPTY;
            practiceName = String.Empty;
            if (!String.IsNullOrEmpty(systemApiKey))
            {
                DBRetVal retVal = db.FillDataTable(String.Format("Select [External_IP],[Port],[Practice_Name] From [Hosts_Guids] Where [SystemApiKey] = '{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(systemApiKey)));
                if (retVal.IsValid)
                {
                    DataRow row = retVal.GetDataTableFirstRow();
                    ip = DataRet.Retrieve(row[0]);
                    port = DataRet.Retrieve<int>(row[1], PORT_EMPTY);
                    practiceName = DataRet.Retrieve(row[2]);
                    return true;
                }
                if (retVal.ErrorOccured)
                    Registration.Logger.Error("RetrieveIPMobileAndPort Error:{0}", retVal.ErrorMsg);
            }            
            return false;
        }

        #endregion

        #region MSLConnect Call-Ins

        /// <summary>
        /// Register a New Server Practice (For this to work SystemApiKey MUST be UNIQUE) and Practice Name must not be ""
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="Host_Guid"></param>
        /// <param name="Internal_IP"></param>
        /// <param name="External_IP"></param>
        /// <param name="Port"></param>
        /// <param name="PracticeName"></param>
        /// <returns></returns>
        internal static bool RegisterNewServerPractice(string SystemApiKey, Guid Host_Guid, string Internal_IP, string External_IP, uint Port, string PracticeName)
        {
            // Check Host GUID 
            if (Host_Guid == null || String.IsNullOrEmpty(Host_Guid.ToString()))
            {
                Registration.Logger.Error("RegisterNewServerPractice Invalid Host_Guid passed in");
                return false;
            }
            
            // Check Validity of Internal IP
            IPAddress ip;
            if (!String.IsNullOrEmpty(Internal_IP) && !IPAddress.TryParse(Internal_IP, out ip) &&
                Yaulw.Net.IPHostHelper.IsValidIPv4Address(ip, false))
            {
                Registration.Logger.Error("RegisterNewServerPractice Internal IP passed in is Invalid");
                return false;
            }

            // Check Validity of External IP
            if (!String.IsNullOrEmpty(External_IP) && !IPAddress.TryParse(External_IP, out ip) &&
                Yaulw.Net.IPHostHelper.IsValidIPv4Address(ip, false))
            {
                Registration.Logger.Error("RegisterNewServerPractice External IP passed in is Invalid");
                return false;
            }

            // Check port validity
            if (Port <= PORT_EMPTY)
            {
                Registration.Logger.Error("RegisterNewServerPractice Invalid Port passed in");
                return false;
            }

            // Check Validity of Practice Name
            if (String.IsNullOrEmpty(PracticeName))
            {
                Registration.Logger.Error("RegisterNewServerPractice Invalid Practice Name passed in");
                return false;
            }

            // For Logging Purposes * Last Sanity Check *
            if (System_Api_Key_Exist(SystemApiKey))
            {
                Registration.Logger.Error("RegisterNewServerPractice SystemApiKey is not unique");
                return false;
            }

            // Make sure ' are preserved and don't cause an issue with SQL
            PracticeName = MAKE_SURE_APOSTROPHY_IS_SQL_READY(PracticeName);

            // Is it a Internal Practice
            bool bIsInternal = PracticeName.EndsWith(INTERNAL_TEST_PRACTICE);
            string sql = String.Format("INSERT INTO [Hosts_Guids] ([SystemApiKey],[Host_Guid],[Internal_IP],[External_IP],[Port],[Practice_Name],[IsInternal],[LastServerUpdate]) VALUES ('{0}','{1}','{2}','{3}',{4},'{5}',{6},'{7}')", SystemApiKey, Host_Guid, Internal_IP, External_IP, Port, PracticeName, bIsInternal ? 1 : 0, SqlDTString(DateTime.Now));
            DBRetVal retVal = db.ExecuteNonQuery(sql);
            if (retVal.ErrorOccured)
                Registration.Logger.Error("RegisterNewServerPractice Error:{0}", retVal.ErrorMsg);

            bool bSuccess = retVal.IsValid;
            if (!bSuccess)
                Registration.Logger.Error("RegisterNewServerPractice returning false for SQL String: {0}", sql);
            return bSuccess;
        }

        /// <summary>
        /// Update a Server with an IP change
        /// </summary>
        /// <param name="Host_Guid"></param>
        /// <param name="Internal_IP"></param>
        /// <param name="External_IP"></param>
        /// <param name="Port"></param>
        /// <returns></returns>
        internal static bool UpdateServer(Guid Host_Guid, string Internal_IP, string External_IP, uint Port)
        {
            // Check Host GUID 
            if (Host_Guid == null || String.IsNullOrEmpty(Host_Guid.ToString()))
            {
                Registration.Logger.Error("UpdateServer Invalid Host_Guid passed in");
                return false;
            }            

            // Check Host GUID - Ultimate Sanity Check
            if (!Host_Guid_Exist(Host_Guid))
            {
                Registration.Logger.Error("UpdateServer Invalid Host_Guid passed in. Host GUID does not exist");
                return false;
            }

            // Check Validity of Internal IP
            IPAddress ip;
            if (!String.IsNullOrEmpty(Internal_IP) && !IPAddress.TryParse(Internal_IP, out ip) &&
                Yaulw.Net.IPHostHelper.IsValidIPv4Address(ip, false))
            {
                Registration.Logger.Error("UpdateServer Internal IP passed in is Invalid");
                return false;
            }

            // Check Validity of External IP
            if (!String.IsNullOrEmpty(External_IP) && !IPAddress.TryParse(External_IP, out ip) &&
                Yaulw.Net.IPHostHelper.IsValidIPv4Address(ip, false))
            {
                Registration.Logger.Error("UpdateServer External IP passed in is Invalid");
                return false;
            }

            // Check port validity
            if (Port <= PORT_EMPTY)
            {
                Registration.Logger.Error("UpdateServer Invalid Port passed in");
                return false;
            }

            DBRetVal retVal = db.ExecuteNonQuery(String.Format("UPDATE [Hosts_Guids] SET [Internal_IP]='{0}',[External_IP]='{1}',[Port]={2},[LastServerUpdate]='{3}' WHERE [Host_Guid]='{4}'", Internal_IP, External_IP, Port,SqlDTString(DateTime.Now), Host_Guid));
            if (retVal.ErrorOccured)
                Registration.Logger.Error("UpdateServer Error:{0}", retVal.ErrorMsg);
            return retVal.IsValid;
        }

        /// <summary>
        /// Update the Practice Name
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="PracticeName"></param>
        /// <returns></returns>
        internal static bool UpdatePracticeName(string SystemApiKey, string PracticeName)
        {
            // Check Validity of Practice Name
            if (String.IsNullOrEmpty(PracticeName))
            {
                Registration.Logger.Error("UpdatePracticeName Invalid Practice Name passed in");
                return false;
            }            

            // Check Validity of System Key
            if (!System_Api_Key_Exist(SystemApiKey))
            {
                Registration.Logger.Error("UpdatePracticeName Invalid SystemApiKey Passed in. Does not exist");
                return false;
            }

            // Make sure ' are preserved and don't cause an issue with SQL
            PracticeName = MAKE_SURE_APOSTROPHY_IS_SQL_READY(PracticeName);

            DBRetVal retVal = db.ExecuteNonQuery(String.Format("UPDATE [Hosts_Guids] SET [Practice_Name]='{0}' WHERE [SystemApiKey]='{1}'", PracticeName, SystemApiKey));
            if (retVal.ErrorOccured)
                Registration.Logger.Error("UpdatePracticeName Error:{0}", retVal.ErrorMsg);
            return retVal.IsValid;
        }

        /// <summary>
        /// Retrieve External or Internal Clients
        /// </summary>
        /// <param name="bInternal"></param>
        /// <returns></returns>
        internal static Client[] GetClients(bool bInternal)
        {
            string sql = String.Format("SELECT [SystemApiKey],[External_IP],[Internal_IP],[Port],[Practice_Name] FROM [Hosts_Guids] WHERE [IsInternal]={0}", bInternal ? 1 : 0);
            DBRetVal retVal = db.FillDataTable(sql);
            if (retVal.IsValid)
            {
                List<Client> clients = new List<Client>();
                foreach (DataRow row in retVal.GetDataTableRetVal().Rows)
                {
                    Client client = new Client();
                    client.host = new Host();
                    client.host.practiceName = DataRet.Retrieve(row["Practice_Name"]);
                    if(bInternal)
                       client.host.host = DataRet.Retrieve(row["Internal_IP"]);
                    else
                       client.host.host = DataRet.Retrieve(row["External_IP"]);
                    client.host.port = DataRet.Retrieve<int>(row["Port"], PORT_EMPTY); ;
                    client.systemApiKey = DataRet.Retrieve(row["SystemApiKey"]);
                    clients.Add(client);
                }
                return clients.ToArray();
            }
            return null;
        }

        /// <summary>
        /// Host Has been updated to a newer version, let's track it
        /// </summary>
        /// <param name="HostGuid"></param>
        /// <param name="Version"></param>
        internal static void HostHasBeenUpdated(Guid HostGuid, string Version)
        {
            // Check Host GUID 
            if (HostGuid == null || String.IsNullOrEmpty(HostGuid.ToString()))
            {
                Registration.Logger.Error("HostHasBeenUpdated Invalid HostGuid passed in");
                return;
            }

            // Check Host GUID - Ultimate Sanity Check
            if (!Host_Guid_Exist(HostGuid))
            {
                Registration.Logger.Error("RegisterNewServerPractice Invalid Host_Guid passed in. Host GUID does not exist");
                return;
            }

            if (String.IsNullOrEmpty(Version))
            {
                Registration.Logger.Error("HostHasBeenUpdated Invalid Version passed in");
                return;
            }

            DBRetVal retVal = db.ExecuteNonQuery(String.Format("UPDATE [Hosts_Guids] SET [UpdatedToVersion]='{0}' WHERE [Host_Guid]='{1}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(Version), HostGuid.ToString()));
            if (retVal.ErrorOccured)
                Registration.Logger.Error("HostHasBeenUpdated Error:{0}", retVal.ErrorMsg);            
        }

        #endregion
    }
}
