

namespace Pluto.Api
{
    using System;
    using RemObjects.SDK;
    using RemObjects.SDK.Types;
    using RemObjects.SDK.Server;
    using RemObjects.SDK.Server.ClassFactories;
    using PlutoServer.MSL.Connectors;
    using Yaulw.File;
    using Sdaleo;
    using Sdaleo.Systems.SQLServer;
    using Sdaleo.Systems.Advantage;
    using Sdaleo.Systems;
    using System.Net;
    using PlutoServer.MSL;
    using Yaulw.Assembly;
    using Yaulw.Tools;

    [RemObjects.SDK.Server.ClassFactories.StandardClassFactory()]
    [RemObjects.SDK.Server.Service(Name = "MSLSpecific", InvokerClass = typeof(MSLSpecific_Invoker), ActivatorClass = typeof(MSLSpecific_Activator))]
    public class MSLSpecific : RemObjects.SDK.Server.Service, IMSLSpecific
    {        
        private System.ComponentModel.Container components = null;
        public MSLSpecific() :
            base()
        {
            this.InitializeComponent();            
        }

        private void InitializeComponent()
        {
        }

        protected override void Dispose(bool aDisposing)
        {
            if (aDisposing)
            {
                if ((this.components != null))
                {
                    this.components.Dispose();
                }
            }
            base.Dispose(aDisposing);
        }

        /// <summary>
        /// Keep track of the Host GUID
        /// </summary>
        private static ISReadWrite s_ISReadWrite = new ISReadWrite("host");

        #region Logger Functions (public one is for Test Framework)

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

        /// <summary>
        /// Test Log File and Path
        /// </summary>
        internal static string TestLogFileNameNPath = String.Empty;

        /// <summary>
        /// Setup the Logger Object, should be done only once at when the application starts
        /// </summary>
        internal static void Setup_Logger()
        {
            if (Logger == null)
            {
                // Is Setup_Test_Logger() Called?
                bool bIsTestLogFileSet = !String.IsNullOrEmpty(TestLogFileNameNPath);

                // 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 LogFileNameNPath = String.Empty;
                if (!bIsTestLogFileSet)
                {
                    string Path = AssemblyW.SpecializedAssemblyInfo.GetAssemblyPath(AssemblyW.AssemblyST.Entry);
                    LogFileNameNPath = PathNaming.PathEndsWithSlash(Path) + AssemblyW.GetAssemblyName(AssemblyW.AssemblyST.Entry) + "." + FILE_EXTENSION_LOG_DEFAULT;
                }
                else
                {
                    // Allow Unit test framework to set the log file path
                    LogFileNameNPath = TestLogFileNameNPath;
                }                

                // 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.LogCallingFunction = false;
                config.LogCallingType = false;
                if (PlutoService.IsOnMcKessonNetwork || bIsTestLogFileSet)
                    config.Detail = Logging_Detail.DEBUG;
                else
                    config.Detail = Logging_Detail.INFO;
                config.UseExclusiveFileLock = false;

                // Now just create the Logger
                if (!bIsTestLogFileSet)                
                    Logger = Logging.AddGlobalLoggerConfiguration(AssemblyW.GetAssemblyName(AssemblyW.AssemblyST.Entry), config);
                else
                    Logger = Logging.AddGlobalLoggerConfiguration("Pluto.MSL.Test", config);

                // Make a Debug Message show, if we are on McKesson
                if (PlutoService.IsOnMcKessonNetwork)
                    Logger.Info("**** NOTE **** This is a McKesson Networked Machine. DEBUG Messages will only show when service is run from a McKesson Networked Machine.");
                else
                    Logger.Info("**** NOTE **** This is a Regularly Networked Machine. Only INFO and ERROR Messages will show when service in this Environment.");
                Logger.Info("Logger Started at:{0} with Service Version {1}", DateTime.Now.ToString(), AssemblyW.GetAssemblyVersion(AssemblyW.AssemblyST.Entry));
            }
        }

        /// <summary>
        /// Setup the Test Logger Object, should be done by the Unit test framework to put the file in c:\\
        /// </summary>
        public static void Setup_Test_Logger()
        {
            TestLogFileNameNPath = @"C:\\Pluto.MSL.Test.log";
            Setup_Logger();
        }

        #endregion

        #region Internal Static Helper Functions

        // Wipes all information known to use, both on the registration server as well as here
        // and starts over. logs the information to the user. System is auto-fixing itelf.
        // performing an internal re-install. The user may not like it, but that is just what
        // happend, we are in a bad state ERROR IT * BOLDLY in the Error log*
        internal static void MAJOR_ERROR_INTEGRITY_EVENT_OCCURED_ENTER_REINSTALL_STATE()
        {
            Logger.Fatal("******** FATAL ******** State occured. Service integrity invalid. All UserApiKeys will get deregistered as well as all hostGuid keys");
            Logger.Fatal("******** FATAL ******** Someone must have tried to tamper with the system (registry / files) loosing vital information needed to continue.");
            Logger.Fatal("******** FATAL ******** Service is attempting to auto-fix itself. All users must re-open Mobile->Help about and obtain a new user api key.");
        }

        /// <summary>
        /// Retrieve the UserApiKey for a given Practice
        /// </summary>
        /// <param name="bIsMedisoft"></param>
        /// <param name="credential"></param>
        /// <param name="DataSetOrDataBaseName"></param>
        /// <returns></returns>
        internal static string GetUserApiKeyForPractice(bool bIsMedisoft, IConnectDb credential, string DataSetOrDataBaseName, out bool bErrorOccured)
        {
            string UserApiKey = "";
            bErrorOccured = true;
            if (!String.IsNullOrEmpty(DataSetOrDataBaseName) && credential != null)
            {                
                if (bIsMedisoft)
                {
                    string retVal = MedisoftConnector.GetUserApiKeyForPractice((AdvantageCredential)credential, DataSetOrDataBaseName, out bErrorOccured);
                    if (!String.IsNullOrEmpty(retVal))
                        UserApiKey = retVal;                    
                }
                else
                {
                    string retVal = LytecConnector.GetUserApiKeyForPractice((SQLServerCredential)credential, DataSetOrDataBaseName, out bErrorOccured);
                    if (!String.IsNullOrEmpty(retVal))
                        UserApiKey = retVal;                    
                }
            }
            return UserApiKey;
        }

        /// <summary>
        /// Set the UserApiKey for a given Practice
        /// </summary>
        /// <param name="bIsMedisoft"></param>
        /// <param name="credential"></param>
        /// <param name="DataSetOrDataBaseName"></param>
        /// <returns></returns>
        internal static bool SetUserApiKeyForPractice(bool bIsMedisoft, IConnectDb credential, string DataSetOrDataBaseName, string UserApiKey)
        {
            if (!String.IsNullOrEmpty(DataSetOrDataBaseName) && credential != null)
            {
                if (bIsMedisoft)
                {
                    bool bSuccess = MedisoftConnector.SetUserApiKeyForPractice((AdvantageCredential)credential, DataSetOrDataBaseName, UserApiKey);
                    if(!bSuccess)
                        Logger.Error("Error setting UserApiKey for DataSetorDataBaseName {0}", DataSetOrDataBaseName);
                    return bSuccess;
                }
                else
                {
                    bool bSuccess = LytecConnector.SetUserApiKeyForPractice((SQLServerCredential)credential, DataSetOrDataBaseName, UserApiKey);
                    if (!bSuccess)
                        Logger.Error("Error setting UserApiKey for DataSetorDataBaseName {0}", DataSetOrDataBaseName);
                    return bSuccess;
                }
            }
            return false;
        }

        /// <summary>
        /// Retrieve the Host Guid for this instance,k if not exists,
        /// create one. Host Guid is stored in isolated storage * why not *
        /// </summary>
        /// <returns></returns>
        internal static Guid GetHostGUID()
        {
             string s = s_ISReadWrite.ReadFromIS();            
             if (String.IsNullOrEmpty(s))
             {
                 Guid g = Guid.NewGuid();
                 Logger.Debug("Creating new Host GUID {0}", g.ToString());
                 s_ISReadWrite.WriteToIS(g.ToString());
                 return g;
             }
             return new Guid(s);

            // New Untested Code * Still needs some hashing out *
            //string s = s_ISReadWrite.ReadFromIS();
            //Guid g = Guid.NewGuid();
            //if (String.IsNullOrEmpty(s))
            //{
            //    // Store bck HostGuid in registry to make sure this value is never lost
            //    Yaulw.Registry.RegKey.SetKey<string>(Yaulw.Registry.HKEYRoot.LocalMachine, PlutoService.LOCAL_MACHINE_SUBKEY, PlutoService.LOCAL_MACHINE_HOSTGUID, g.ToString());
            //    Logger.Debug("Creating new Host GUID {0}", g.ToString());
            //    s_ISReadWrite.WriteToIS(g.ToString());
            //    return g;
            //}
            //else
            //{
            //    // Read registry and retrieve backup guid first * if empty set it, Upgrade service issues * feature added in 1.0.2.3
            //    string bckGuid = Yaulw.Registry.RegKey.GetKey<string>(Yaulw.Registry.HKEYRoot.LocalMachine, PlutoService.LOCAL_MACHINE_SUBKEY, PlutoService.LOCAL_MACHINE_HOSTGUID, String.Empty);
            //    if (String.IsNullOrEmpty(bckGuid.ToString()))
            //    {
            //        Yaulw.Registry.RegKey.SetKey<string>(Yaulw.Registry.HKEYRoot.LocalMachine, PlutoService.LOCAL_MACHINE_SUBKEY, PlutoService.LOCAL_MACHINE_HOSTGUID, g.ToString());
            //    }
            //    else
            //    {
            //        // if not empty compare them (they should always be equal if not, we are kind of in mayor trouble)
            //        // we will pick the guid that is stored in online, that is our only choice, doubtful that both got
            //        // stored online (*that state is a major error state and would require wiping the host guid, and wiping
            //        // all hostkeys online and all userapikeys online. Even maybe the DBCacheFile. basically an internal re-install
            //        if (String.Compare(s, bckGuid, true) != 0)
            //        {
            //            // chech which guid is online, pick that one first



            //            // else Mayor event occured
            //            MAJOR_ERROR_INTEGRITY_EVENT_OCCURED_ENTER_REINSTALL_STATE();
            //        }
            //    }
            //}
            //return new Guid(s);
        }

        /// <summary>
        /// Quick check to see if the connection is a Medisoft / Advantage DB Connection
        /// </summary>
        /// <param name="ConnectionString"></param>
        /// <returns></returns>
        internal static bool ConnectionContainsAdvServerType(string ConnectionString)
        {
            if (!String.IsNullOrEmpty(ConnectionString))
                return (ConnStr.ContainsKey("Advantage Server Type", ConnectionString) || ConnStr.ContainsKey("ServerType", ConnectionString));
            return false;
        }

        #endregion        

        #region Private DBCache Related Helper Functions

        /// <summary>
        /// Shared Database Connection needs to be added
        /// </summary>
        /// <param name="SharedConnectionDataSource">Connection String of the Shared DB to be added to Cache</param>
        /// <param name="bTryToConnect">Will try to connect to the DB, if set to true</param>
        /// <param name="bCanConnect">if bTryToConnect is true, it will try to connect and assign out this value</param>
        /// <returns>true if it needed to be added and, if TryToConnect is set, and communication is working, false otherwise</returns>
        private bool AddConnectionIfNotAlreadyThere(string SharedConnectionDataSource, bool bTryToConnect)
        {
            if (!String.IsNullOrEmpty(SharedConnectionDataSource))
            {
                IConnectDb credential = ConvertSharedDataSourceToProperCredential(SharedConnectionDataSource);
                if (credential != null)
                {
                    // Is Connection of Type Medisoft
                    if (ConnectionContainsAdvServerType(SharedConnectionDataSource))
                    {
                        DBCache.AddSharedDataConnectionToDataStore((AdvantageCredential)credential, true);
                        
                        // Test the Connection = see if error occurs calling practice list
                        if (bTryToConnect)
                        {
                            bool bConnect = MedisoftConnector.GetPracticeList((AdvantageCredential)credential).IsValid;
                            return bConnect;
                        }
                        else
                            return true;
                    }
                    else // Or Lytec
                    {
                        DBCache.AddSharedDataConnectionToDataStore((SQLServerCredential)credential, true);

                        // Test the Connection = see if error occurs calling practice list
                        if (bTryToConnect)
                        {
                            bool bConnect = LytecConnector.GetPracticeList((SQLServerCredential)credential).IsValid;
                            return bConnect;
                        }
                        else
                            return true;
                    }                    
                }                
            }
            return false;
        }

        /// <summary>
        /// Converts a passed in SharedConnection (from Medisoft/Lytec) into a proper IConnectDb credential
        /// we can use to pass arround
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <returns></returns>
        private IConnectDb ConvertSharedDataSourceToProperCredential(string SharedConnectionDataSource)
        {
            if (!String.IsNullOrEmpty(SharedConnectionDataSource))
            {
                // Is Connection of Type Medisoft
                if (ConnectionContainsAdvServerType(SharedConnectionDataSource))
                {
                    string Type = ConnStr.RetrieveValue("Advantage Server Type", SharedConnectionDataSource);
                    if (String.IsNullOrEmpty(Type))
                        Type = ConnStr.RetrieveValue("ServerType", SharedConnectionDataSource);

                    bool bIsRemote = Type.ToUpper().Contains("REMOTE");
                    AdvantageCredential credential = new AdvantageCredential(Yaulw.Net.IPHostHelper.GetDataSource(SharedConnectionDataSource), "SharedDataUser", "AndPassword", bIsRemote ? AdvantageCredential.ServerType.REMOTE : AdvantageCredential.ServerType.LOCAL);
                    return credential;
                }
                else // Or Lytec
                {
                    string Server;
                    string Instance;
                    if (SQLServerUtilities.SplitServerOrServerNInstance(Yaulw.Net.IPHostHelper.GetDataSource(SharedConnectionDataSource), out Server, out Instance))
                    {
                        string UserID = ConnStr.RetrieveValue("User ID", SharedConnectionDataSource);
                        string Password = ConnStr.RetrieveValue("Password", SharedConnectionDataSource);
                        SQLServerCredential credential = new SQLServerCredential(Server, Instance, "Lytec SharedData", UserID, Password);
                        return credential;
                    }                    
                }
            }
            return null;
        }

        /// <summary>
        /// Reload the UserCredentials for a given SharedCredentials in the Cache
        /// </summary>
        /// <param name="SharedCredential"></param>
        /// <param name="bIsMedisoft"></param>
        private void ReloadUserDBConnectionForSharedCredential(IConnectDb SharedCredential, bool bIsMedisoft)
        {
            if (bIsMedisoft)
                DBCache.IterateSharedConnectionAndLoadUserApiKeyConnections((AdvantageCredential)SharedCredential);
            else
                DBCache.IterateSharedConnectionAndLoadUserApiKeyConnections((SQLServerCredential)SharedCredential);
        }

        #endregion

        #region IMSLMobileConnect

        /// <summary>
        /// Common Tasks to do for all of our MSLSpecific Calls
        /// </summary>
        /// <param name="SharedConnectionDataSource">Medisoft/Lytec SharedDataConnection</param>
        /// <param name="bAddSharedConnection">if true, add the connection to our DataStore</param>
        /// <param name="bTryToConnect">if true, trying to connect to the db to test</param>
        /// <param name="bIsMedisoft">out is Medisoft</param>
        /// <param name="Credential">out Credential</param>
        /// <returns>return true when everything is ok, false otherwise</returns>
        private bool CommonThingsToDoUponEveryMSLCall(string SharedConnectionDataSource, bool bAddSharedConnection, bool bTryToConnect, out bool bIsMedisoft, out IConnectDb Credential)
        {
            bIsMedisoft = false;
            Credential = null;
            if(!String.IsNullOrEmpty(SharedConnectionDataSource))
            {
                // Is Lytec or Medisoft?
                bIsMedisoft = ConnectionContainsAdvServerType(SharedConnectionDataSource);
                Credential = ConvertSharedDataSourceToProperCredential(SharedConnectionDataSource);
                if (Credential != null)
                {
                    // Local Host name and IP
                    if(bIsMedisoft)
                       Logger.Info("Determined DataSource to be Medisoft");
                    else
                       Logger.Info("Determined DataSource to be Lytec");
                    Logger.Info("Determined Host:{0} with Host Guid:'{1}'", Dns.GetHostName(), GetHostGUID());

                    // Should we add the connection?
                    if (bAddSharedConnection)
                    {
                        // will always return true, unless bTryToConnect is true and it will try to connect and fail
                        bool bConnect = AddConnectionIfNotAlreadyThere(SharedConnectionDataSource, bTryToConnect);
                        if (bTryToConnect && bConnect)
                            Logger.Debug("Successfully connected to SharedDataSource: {0}", SharedConnectionDataSource);
                        else if(bTryToConnect && !bConnect)
                            Logger.Error("Failed to Connect to passed in DataSource");

                        // Ensure that all UserApiKeys are unique - running into issues
                        // with Lytec and Medisoft somehow
                        if (bConnect)
                        {
                            if (bIsMedisoft)
                                MedisoftConnector.CleanUpDuplicateUserApiKeyExists((AdvantageCredential)Credential);
                            else
                                LytecConnector.CleanUpDuplicateUserApiKeyExists((SQLServerCredential)Credential);
                        }
                        return bConnect;
                    }
                }                
            }
            Logger.Debug("Something is wrong with this DataSource {0},", SharedConnectionDataSource);
            Logger.Error("Failed to Retrieve the information needed to continue");            
            return false;
        }

        /// <summary>
        /// Mobile About Dialog got called by Lytec/Medisoft. Here we do the main work of registering everything
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <param name="RegisteredName"></param>
        /// <param name="PracticeName"></param>
        /// <returns></returns>
        public virtual ApiKeyNPin MobileAboutDialogCalled(string SharedConnectionDataSource, string RegisteredName, string PracticeName, string DataBaseNameOrDataSetName)
        {
            Logger.Debug("called with SharedDataSource: {0}, RegisteredName: {1}, PracticeName: {2}, DataSetOrDBName: {3}", SharedConnectionDataSource, RegisteredName, PracticeName, DataBaseNameOrDataSetName);
            ApiKeyNPin apikey = new ApiKeyNPin();
            try
            {             
                // Perform this for all MSL Entry Calls * for Sanity *
                bool bIsMedisoft = false;
                IConnectDb Credential = null;
                bool bIsValid = CommonThingsToDoUponEveryMSLCall(SharedConnectionDataSource, true, false, out bIsMedisoft, out Credential);
                if (!bIsValid)
                    return apikey;

                // Try To Fetch the UserApikey
                bool bErrorOccured = true;
                string UserApiKey = GetUserApiKeyForPractice(bIsMedisoft, Credential, DataBaseNameOrDataSetName, out bErrorOccured);
                if (bErrorOccured)
                {
                    Logger.Error("Database Error Occured fetching UserApiKey - Mobile About Dialog cannot continue for DataSetorDataBaseName {0}", DataBaseNameOrDataSetName);
                    return apikey;
                }

                // To Do: UserApiKey could not match with the current host, if that ever happens,
                //        we should ideally re-register and not retrieve the PIN, because now the systems are out of sync
                //        (TBD)

                // If there is no UserApiKey already in the System,
                // it means we must register this practice as * new * 
                if (String.IsNullOrEmpty(UserApiKey))
                {
                    string strApiKey;
                    string strPin;
                    bool bSuccess = RegistrationWrapper.RegisterNewPractice(PracticeName, bIsMedisoft, out strApiKey, out strPin);
                    if (bSuccess)
                    {
                        Logger.Debug("Retrieved new UserApiKey {0} and Pin {1}", strApiKey, strPin);

                        // Set the Service State
                        PlutoService.state = RegistrationWrapper.HostGUIDstate.exists;                                                                        

                        // Save the UserApiKey in the DB (if successful save, then continue to reload user DB Connections)
                        // and only then assign out the api key! ~if it didn't save in the db it shouldn't show on the dialog
                        if (SetUserApiKeyForPractice(bIsMedisoft, Credential, DataBaseNameOrDataSetName, strApiKey))
                        {
                            apikey.UserApiKey = strApiKey;
                            apikey.UserPin = strPin;

                            // Reload the UserDB Cache
                            ReloadUserDBConnectionForSharedCredential(Credential, bIsMedisoft);
                        }
                        else
                        {
                            Logger.Error("Database Error Occured saving UserApiKey - Mobile About Dialog cannot continue");
                        }                                                
                        return apikey;
                    }                    
                }
                else
                {
                    // Practice already registered! So just retrieve the pin for the UserApikey and we are done                     
                    string Pin = RegistrationWrapper.RetrievePinForUserApiKey(UserApiKey);
                    if (!String.IsNullOrEmpty(Pin))
                    {
                        apikey.UserApiKey = UserApiKey;
                        apikey.UserPin = Pin;
                        Logger.Debug("MobileAboutDialogCalled() Retrieving Pin:{0} for UserApiKey:{1}", Pin, UserApiKey);
                    }
                    else
                    {
                        Logger.Error("Failed to retrieve Pin - Mobile About Dialog cannot continue");
                    }
                    return apikey;
                }
            }
            catch (Exception e) { Logger.Error("MobileAboutDialogCalled() Error", e); }
            return apikey;
        }

        /// <summary>
        /// Called upon every lytec/medisoft launch. Currently doesn't do very much. (It was kind of
        /// intended for Registration Purposes). 
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <param name="RegisteredName"></param>
        /// <returns></returns>
        public virtual bool ProductSetupCompleted(string SharedConnectionDataSource, string RegisteredName)
        {
            Logger.Debug("called with SharedDataSource: {0}, RegisteredName: {1}", SharedConnectionDataSource, RegisteredName);
            try
            {                
                // Perform this for all MSL Entry Calls * for Sanity *
                bool bIsMedisoft = false;
                IConnectDb Credential = null;
                CommonThingsToDoUponEveryMSLCall(SharedConnectionDataSource, true, true, out bIsMedisoft, out Credential);
                
                // Always return true, Medisoft/Lytec Checking that it can connect to this service expects
                // a true back, if it doesn't it will hinder the CheckConnectivity Test at step 1,
                // not allowing the real step 2 and step 3 to continue
                return true;
            }
            catch (Exception e) { Logger.Error("ProductSetupCompleted() Error", e); }
            return false;
        }

        /// <summary>
        /// Practice Name Change Occured
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <param name="NewPracticeName"></param>
        /// <returns></returns>
        public virtual bool PracticeNameChangeOccured(string SharedConnectionDataSource, string NewPracticeName, string DataBaseNameOrDataSetName)
        {
            Logger.Debug("called with SharedDataSource: {0}, NewPracticeName: {1}, DataSetOrDBName: {2}", SharedConnectionDataSource, NewPracticeName, DataBaseNameOrDataSetName);
            try
            {
                // Perform this for all MSL Entry Calls * for Sanity *
                bool bIsMedisoft = false;
                IConnectDb Credential = null;
                bool bIsValid = CommonThingsToDoUponEveryMSLCall(SharedConnectionDataSource, true, false, out bIsMedisoft, out Credential);
                if (!bIsValid)
                    return false;

                // Try To Fetch the UserApikey
                bool bErrorOccured = true;
                string UserApiKey = GetUserApiKeyForPractice(bIsMedisoft, Credential, DataBaseNameOrDataSetName, out bErrorOccured);
                if (bErrorOccured)
                {
                    Logger.Error("Database Error Occured fetching UserApiKey - PracticeNameChangeOccured cannot continue for DataSetorDataBaseName {0}", DataBaseNameOrDataSetName);
                    return false;
                }
                
                if (!String.IsNullOrEmpty(UserApiKey))
                {
                    // Auto-Retrieve the Pin                    
                    string Pin = RegistrationWrapper.RetrievePinForUserApiKey(UserApiKey);
                    if (!String.IsNullOrEmpty(Pin))
                    {
                        Logger.Debug("Retrieved UserApiKey:{0} Pin:{1} for Practice:{2}", UserApiKey, Pin, NewPracticeName);

                        // Update the Practice Name
                        bool bSuccess = RegistrationWrapper.UpdatePracticeName(UserApiKey, Pin, NewPracticeName);
                        return bSuccess;
                    }
                    else
                    {
                        Logger.Error("Failed to retrieve Pin - PracticeNameChangeOccured cannot continue");
                    }
                }                
                return false;
            }
            catch (Exception e) { Logger.Error("PracticeNameChangeOccured() Error", e); }
            return false;            
        }
       
        /// <summary>
        /// Check to see if the external IP can be accessed by the McKesson's Mobile Gateway
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <returns></returns>
        public virtual bool IsServerReachableFromTheInternet(string SharedConnectionDataSource)
        {
            Logger.Debug("called with SharedDataSource: {0}", SharedConnectionDataSource);
            try
            {
                // Perform this for all MSL Entry Calls * for Sanity *
                bool bIsMedisoft = false;
                IConnectDb Credential = null;
                bool bIsValid = CommonThingsToDoUponEveryMSLCall(SharedConnectionDataSource, true, false, out bIsMedisoft, out Credential);
                if (!bIsValid)
                    return false;

                // Check to see if this server is reachable from the internet
                bool bIsReachable = RegistrationWrapper.IsServerReachable();
                return bIsReachable;
            }
            catch (Exception e) { Logger.Error("IsServerReachableFromTheInternet() Error", e); }
            return false;
        }

        /// <summary>
        /// Update the Service if needed
        /// </summary>
        public virtual void UpdateServiceIfNeeded()
        {
            PlutoService.AppLogInfo("UpdateServiceIfNeeded() called - Will try to Update the Service, if new Service was found online");
            
            // Callback from MSLConnect, to try to update the service, 
            // we only need a little delay here, let this call return as quickly as possible
            AutoUpdate.TryToUpdateIfNeeded(5, false);
        }

        #endregion

        #region Pluto AddOns

        /// <summary>
        /// Retrieve the Host GUID for this machine
        /// </summary>
        /// <returns></returns>
        public virtual string GetHostSpecGUID()
        {
            return GetHostGUID().ToString();
        }

        /// <summary>
        /// Retrieve the External Port Set
        /// </summary>
        /// <returns>a valid port or -1 for none</returns>
        public virtual int GetExternalPort()
        {
            if (PlutoService.IsExtIPandExtPortSet)
                return (int) PlutoService.ExternalPort;
            else
                return -1;
        }

        /// <summary>
        /// Set and reload the External CHannel on a different port
        /// </summary>
        /// <param name="Port"></param>
        /// <returns></returns>
        public virtual bool SetExternalPort(int Port)
        {
            // never tested (coded), but never tested 
            // must test ReloadExtIPandPortConfiguration() at some point,
            // won't get called by Diagnose mobile at this time
            return PlutoService.ReloadExtIPandPortConfiguration(Port);
        }

        /// <summary>
        /// Retrieve the latest external IP
        /// </summary>
        /// <returns></returns>
        public virtual string GetExternalIP()
        {
            if (PlutoService.IsExtIPandExtPortSet)
                return PlutoService.ExternalIP.ToString();
            else
                return IPAddress.None.ToString();
        }

        /// <summary>
        /// Retrieve the latest Internal IP
        /// </summary>
        /// <returns></returns>
        public virtual string GetLocalIP()
        {
            return PlutoService.InternalIP.ToString();
        }

        #endregion

        #region Pluto Dev AddOns (To Do)

        /// <summary>
        /// Go thru all the Shared Database Connections and auto-generate a
        /// user api key for all practices * good for testing and debugging *
        /// this means lytec/medisoft is not needed to create the userapikeys
        /// </summary>
        public virtual void GenerateApiKeysForPracticesInLoadedSharedCredentials()
        {
            // TO DO:
        }

        /// <summary>
        /// Add a custom Shared Database Credential Medisoft or Lytec to the system
        /// *good for testing and debugging*
        /// </summary>
        /// <param name="credential"></param>
        /// <returns></returns>
        public virtual bool AddSharedCredential(ApiKeyNCredential credential)
        {
            // TO DO:
            return false;
        }

        /// <summary>
        /// Retrieve all currently loaded credentials good for support,
        /// dev, qa, etc to see what is going on
        /// </summary>
        /// <returns></returns>
        public virtual ApiKeyNCredential[] GetLoadedCredentials()
        {
            // TO DO:
            return null;
        }

        #endregion
    }
}
