//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:2.0.50727.4971
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Pluto.Registration
{
    using System;
    using RemObjects.SDK;
    using RemObjects.SDK.Types;
    using RemObjects.SDK.Server;
    using RemObjects.SDK.Server.ClassFactories;
    using System.Net;
    using System.Net.Sockets;
    using System.Diagnostics;
    using RegistrationServer;
    using Yaulw.Assembly;
    using Yaulw.File;
    using Yaulw.Tools;    

    [RemObjects.SDK.Server.ClassFactories.StandardClassFactory()]
    [RemObjects.SDK.Server.Service(Name = "Registration", InvokerClass = typeof(Registration_Invoker), ActivatorClass = typeof(Registration_Activator))]
    public class Registration : RemObjects.SDK.Server.Service, IRegistration
    {
        private System.ComponentModel.Container components = null;

        public Registration() :
            base()
        {
            this.InitializeComponent();
        }
        private void InitializeComponent()
        {            
        }
        protected override void Dispose(bool aDisposing)
        {
            if (aDisposing)
            {
                if ((this.components != null))
                {
                    this.components.Dispose();
                }
            }
            base.Dispose(aDisposing);
        }

        #region Logging

        /// <summary>
        /// For Logging to a File
        /// </summary>
        internal static Logging Logger = null;

        /// <summary>
        /// Setup the Logger Object, should be done only once at when the application starts
        /// </summary>
        internal static void Setup_Logger()
        {
            if (Logger == null)
            {
                // Default Log File Settings
                const int LOG_FILE_FILE_SIZE_IN_MB = 4;
                const int LOG_FILE_NUM_OF_BACKUPS = 2;
                const string FILE_EXTENSION_LOG_DEFAULT = "log";

                // Default Log File Name N Path Settings
                string Path = AssemblyW.SpecializedAssemblyInfo.GetAssemblyPath(AssemblyW.AssemblyST.Entry);
                string LogFileNameNPath = PathNaming.PathEndsWithSlash(Path) + AssemblyW.GetAssemblyName(AssemblyW.AssemblyST.Entry) + "." + FILE_EXTENSION_LOG_DEFAULT;

                // Log Config
                Logging_Configuration config = new Logging_Configuration();
                config.LogFileNameNPath = LogFileNameNPath;
                config.maxFileSizeInMB = LOG_FILE_FILE_SIZE_IN_MB;
                config.numOfBackupLogFiles = LOG_FILE_NUM_OF_BACKUPS;
                config.Log4NetDetailPatternLayout = "%date{dd MMM HH:mm:ss,fff} %level - %message%newline";
                config.LogCallingType = false;
                config.LogCallingFunction = false;
                config.Detail = Logging_Detail.DEBUG;  
                config.UseExclusiveFileLock = false;

                // Now just create the Logger
                Logger = Logging.AddGlobalLoggerConfiguration(AssemblyW.GetAssemblyName(AssemblyW.AssemblyST.Entry), config);

                // Make a Debug Message show,                 
                Logger.Info("Logger Started at:{0}", DateTime.Now.ToString());
            }
        }

        #endregion

        #region IMP Get Registration Server (could be Important for Migration)

        /// <summary>
        /// Get Host and IP of the Registration Server, 
        /// useful when we plan on switching later
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <returns></returns>
        public virtual Host GetRegistrationServer(string SystemApiKey)
        {
            // For now only pass out * this service's configuration *
            var host = new Host();
            host.host = Configuration.Url;
            host.port = Configuration.Port;
            return host;
        }

        #endregion

        #region Retrieve IP n Port Stuff

        /// <summary>
        /// Get Mobile Host
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <returns></returns>
        public virtual Host GetApiHostMobile(string SystemApiKey)
        {
            Host host = new Host();
            try
            {
                int p;
                string h;
                string n; 
                if (DataAccessLayer.RetrieveIPMobileAndPort(SystemApiKey, out h, out p, out n))
                {
                    host.host = h;
                    host.port = p;
                    host.practiceName = n;
                }
            }
            catch (Exception e)
            {
                Logger.Error("GetApiHostMobile Error", e);                
            }
            return host;
        }

        /// <summary>
        /// Get Wifi Host
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <returns></returns>
        public virtual Host GetApiHostWifi(string SystemApiKey)
        {
            Host host = new Host();
            try
            {                
                int p;
                string h;
                string n; 
                if (DataAccessLayer.RetrieveIPWifiAndPort(SystemApiKey, out h, out p, out n))
                {
                    host.host = h;
                    host.port = p;
                    host.practiceName = n;
                }                
            }
            catch (Exception e)
            {
                Logger.Error("GetApiHostMobile Error", e);
            }
            return host;
        }

        #endregion

        #region MSLConnect Call-Ins

        /// <summary>
        /// Register a New Server Practice
        /// </summary>
        /// <param name="PracticeName"></param>
        /// <param name="server"></param>
        /// <param name="serverType"></param>
        /// <param name="UserApiKey"></param>
        /// <param name="UserApiPin"></param>
        /// <returns></returns>
        public virtual bool RegisterNewServerPractice(string PracticeName, Server server, string serverType, out string UserApiKey, out string UserApiPin)
        {
            UserApiKey = String.Empty;
            UserApiPin = String.Empty;
            try
            {                
                // One IP must exist! bound to a Practice ~otherwise, what is the point
                if ((String.IsNullOrEmpty(server.InternalIP) && String.IsNullOrEmpty(server.ExternalIP)) || String.IsNullOrEmpty(PracticeName))
                {
                    Logger.Error("RegisterNewServerPractice at least one IP must be passed in and a Practice Name");
                    return false;
                }

                if (String.IsNullOrEmpty(server.HostGUID))
                {
                    Logger.Error("Server HostGUID must be passed in");
                    return false;
                }

                if (String.IsNullOrEmpty(serverType))
                {
                    Logger.Error("serverType must be passed in");
                    return false;
                }

                // create GUID from HostGUID
                Guid guid = new Guid(server.HostGUID);
                Logger.Info("RegisterNewServerPractice called() for Practice {0} Product {1} Host_GUID {2}", PracticeName, serverType, server.HostGUID);

                // Make sure that the SystemApiKey doesn't already exist * Do this up to 15 times just in case* 
                // more than that would be terrible system api key algorigthm
                bool bIsUniqueSystemKey = false;
                string SystemApiKey = SystemAccessVerifier.GenerateNewSystemApiKey(serverType[0]);
                for (int i = 0; i < 10; ++i)
                {
                    bIsUniqueSystemKey = !DataAccessLayer.System_Api_Key_Exist(SystemApiKey);
                    if (bIsUniqueSystemKey)
                        break;
                    else
                        SystemApiKey = SystemAccessVerifier.GenerateNewSystemApiKey(serverType[0]);
                }

                // We should only get here if we can validly insert a record with non-existing System Key
                if (bIsUniqueSystemKey)
                {
                    bool bRegisterSuccess = DataAccessLayer.RegisterNewServerPractice(SystemApiKey, guid, server.InternalIP, server.ExternalIP, (uint)server.Port, PracticeName);
                    if (bRegisterSuccess)
                    {
                        SystemAccessVerifier verifier = new SystemAccessVerifier(SystemApiKey);
                        UserApiKey = verifier.UserApiKey;
                        UserApiPin = verifier.Pin;
                        return bRegisterSuccess;
                    }                    
                }
                else
                {
                    Logger.Error("RegisterNewServerPractice Error: System Key '{0}' already exists. 10x wasn't enought to generate a new one. Very Illogical!", SystemApiKey);
                }
            }
            catch (Exception e)
            {
                Logger.Error("RegisterNewServerPractice Error", e);                
            }
            return false;                       
        }

        /// <summary>
        /// Retrieve the System API Key for a UserApiKey and UserApiPin
        /// </summary>
        /// <param name="UserApiKey"></param>
        /// <param name="UserApiPin"></param>
        /// <returns></returns>
        public virtual string RegisterNewClient(string UserApiKey, string UserApiPin)
        {
            try
            {
                // Check all our inputs 
                if (!String.IsNullOrEmpty(UserApiKey) && !String.IsNullOrEmpty(UserApiPin) )
                {
                    // Retrieve the System Key from their credentials                    
                    string SystemApiKey = String.Empty;
                    SystemAccessVerifier verifier = new SystemAccessVerifier(UserApiKey, UserApiPin);
                    if (!String.IsNullOrEmpty(verifier.SystemApiKey))
                    {
                        // Check to make sure that this system key exists
                        if (!DataAccessLayer.System_Api_Key_Exist(verifier.SystemApiKey))
                        {
                            Logger.Error("System Key '{2}' retrieval failed for registering a New Client with UserApiKey '{0}' and Pin '{1}'", UserApiKey, UserApiPin, verifier.SystemApiKey);
                            return String.Empty;
                        }
                        return verifier.SystemApiKey;
                    }
                    else
                    {
                        Logger.Error("System Key '{2}' verification failed for registering a New Client with UserApiKey '{0}' and Pin '{1}'", UserApiKey, UserApiPin, verifier.SystemApiKey);                        
                    }
                }
            }
            catch (Exception e)
            {
                Logger.Error("RegisterNewServerPractice Error", e);
            }
            return String.Empty;  
        }

        /// <summary>
        /// Retrieve Pin for a Client
        /// </summary>
        /// <param name="UserApiKey"></param>
        /// <returns></returns>
        public virtual string RetrieveNewClientPin(string UserApiKey)
        {
            try
            {
                if (!String.IsNullOrEmpty(UserApiKey))
                {
                    string Pin = SystemAccessVerifier.RetrieveUserApkiKeyPin(UserApiKey);
                    if (!String.IsNullOrEmpty(Pin))
                        return Pin;
                    else
                        Logger.Error("Failed to retrieve Pin for UserApiKey:{0}", UserApiKey);
                }
            }
            catch (Exception e)
            {
                Logger.Error("RetrieveNewClientPin Error", e);
            }
            return String.Empty;
        }    
    
        /// <summary>
        /// Update the Server 
        /// </summary>
        /// <param name="server"></param>
        /// <returns></returns>
        public virtual bool UpdateServer(Server server)
        {
            try
            {
                Guid guid = new Guid(server.HostGUID);             
                bool bSuccess = DataAccessLayer.UpdateServer(guid, server.InternalIP, server.ExternalIP, (uint)server.Port);                    
                return bSuccess;
            }
            catch (Exception e)
            {
                Logger.Error("UpdateServer Error", e);
            }
            return false;
        }

        /// <summary>
        /// Update Practice Name
        /// </summary>
        /// <param name="UserApiKey"></param>
        /// <param name="UserApiPin"></param>
        /// <param name="PracticeName"></param>
        /// <returns></returns>
        public virtual bool UpdatePracticeName(string UserApiKey, string UserApiPin, string PracticeName)
        {
            try
            {
                // Retrieve the System Key from their credentials                    
                string SystemApiKey = String.Empty;
                SystemAccessVerifier verifier = new SystemAccessVerifier(UserApiKey, UserApiPin);
                if (!String.IsNullOrEmpty(verifier.SystemApiKey))
                {
                    bool bSuccess = UpdatePracticeName2(verifier.SystemApiKey, PracticeName);
                    return bSuccess;
                }
                else
                    Logger.Error("Failed to Retrieve SystemApiKey for UserApiKey:{0} and Pin:{1}", UserApiKey, UserApiPin);
            }
            catch (Exception e)
            {
                Logger.Error("UpdatePracticeName Error", e);                
            }
            return false;            
        }

        /// <summary>
        /// Update Practice Name 2
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="PracticeName"></param>
        /// <returns></returns>
        public virtual bool UpdatePracticeName2(string SystemApiKey, string PracticeName)
        {
            try
            {
                bool bSuccess = DataAccessLayer.UpdatePracticeName(SystemApiKey, PracticeName);
                return bSuccess;                    
            }
            catch (Exception e)
            {
                Logger.Error("UpdatePracticeName2 Error", e);                
            }
            return false;
        }

        /// <summary>
        /// Same really as IsServerReachable except that it can be called by just passing in
        /// a System API Key
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <returns></returns>
        public virtual bool IsClientReachable(string SystemApiKey)
        {
            Host host = GetApiHostMobile(SystemApiKey);
            Server server = new Server();
            server.ExternalIP = host.host;
            server.Port = host.port;
            bool bCanConnect = IsServerReachable(server);
            if (!bCanConnect)
                Logger.Error("IsClientReachable call to IsServerReachable returned false");
            return bCanConnect;
        }

        /// <summary>
        /// Check to see if the specified external IP address and port can be reached from here
        /// Must be on a different port than this server is running on
        /// </summary>
        /// <param name="server"></param>
        /// <returns></returns>
        public virtual bool IsServerReachable(Server server)
        {                        
            // To Do: in the future! - Check to see if the IP address is not private range, 
            // no need to do a port open on a private IP address, we should never be called with a private IP!
            // if we do that is an error.
            IPAddress ip = IPAddress.None;
            if (String.IsNullOrEmpty(server.ExternalIP) || server.Port == Configuration.Port ||
                !IPAddress.TryParse(server.ExternalIP, out ip) || !Yaulw.Net.IPHostHelper.IsValidIPv4Address(ip, false))
            {
                Logger.Error("Invalid IP/configuration passed into IsServerReachable IP:{0} Port:{1}", ip, server.Port);
                return false;
            }

            bool bCanConnect = Yaulw.Net.IPHostHelper.IsPortOpen(ip, server.Port);
            return bCanConnect;
        }

        /// <summary>
        /// Calls Pluto API on the specified server and port to try to get the HOST GUID
        /// from there. This is done on purpose to check that the service that is up could
        /// actually belong to ourselves
        /// </summary>
        /// <param name="server">Valid Server Object with the external IP set and valid port</param>
        /// <returns>will return the HOST GUID of the Pluto Instance, if found, otherwise ""</returns>
        public virtual string RetrieveHostGUIDForServerFromInternet(Server server)
        {
            try
            {
                IPAddress ip = IPAddress.None;
                if (String.IsNullOrEmpty(server.ExternalIP) || server.Port == Configuration.Port ||
                    !IPAddress.TryParse(server.ExternalIP, out ip) || !Yaulw.Net.IPHostHelper.IsValidIPv4Address(ip, false))
                {
                    Logger.Error("Invalid IP/configuration passed into RetrieveHostGUIDForServerFromInternet IP:{0} Port:{1}", ip, server.Port);
                    return String.Empty;
                }

                // Try to use Pluto Api to retrieve the Pluto instance's HOST GUID
                Pluto.MSL.Api.API.SetNetworkSettings(ip.ToString(), (uint)server.Port);
                string FoundHostGuid = Pluto.MSL.Api.API.GetHostGUID();
                if (!String.IsNullOrEmpty(FoundHostGuid))
                     return FoundHostGuid;                                                
            }
            catch (Exception e)
            {
                Logger.Error("RetrieveHostGUIDForServerFromInternet Error", e);
            }
            return String.Empty;
        }
        
        #endregion

        #region Pluto AddOns

        /// <summary>
        /// Checks to see if the Host GUID exists on the Database
        /// </summary>
        /// <param name="HostGUID"></param>
        /// <returns></returns>
        public virtual bool DoesServerHostGUIDExist(string HostGUID)
        {
            try
            {
                Guid guid = new Guid(HostGUID);
                bool bExists = DataAccessLayer.Host_Guid_Exist(guid);
                return bExists;
            }
            catch (Exception e)
            {
                Logger.Error("DoesServerHostGUIDExist Error", e);
            }
            return false;
        }

        /// <summary>
        /// Updates the version for the specified Host GUID
        /// </summary>
        /// <param name="HostGUID"></param>
        /// <param name="Version"></param>
        public virtual void ServerHasBeenUpdated(string HostGUID, string Version)
        {
            try
            {
                Guid guid = new Guid(HostGUID);
                DataAccessLayer.HostHasBeenUpdated(guid, Version);
            }
            catch (Exception e)
            {
                Logger.Error("ServerHasBeenUpdated Error", e);
            }
        }

        /// <summary>
        /// GetClients used for internal / corporate 
        /// </summary>
        /// <param name="IsInternal"></param>
        /// <returns></returns>
        public virtual Client[] GetClients(bool IsInternal)
        {
            try
            {
                Client[] clients = DataAccessLayer.GetClients(IsInternal);
                return clients;                
            }
            catch (Exception e)
            {
                Logger.Error("GetClients Error", e);
            }
            return null;
        }

        #endregion

    }
}