using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Net;
using System.Net.NetworkInformation;
using Pluto.MSL.Api;
using System.IO;
using Yaulw.Assembly;

namespace MSLConnect
{
    [ComVisible(true), Guid("3F04286D-86CF-4C30-85E2-9FF7C0DA9DEB")]
    public interface IMSLMobileConnect
    {
        bool MobileAboutDialog_GotCalled(string SharedConnectionDataSource, string RegisteredName, string PracticeName, string DataBaseNameOrDataSetName, out string UserApiKey, out string UserPin);
        bool ProductSetupCompleted_PriorUserLogin(string SharedConnectionDataSource, string RegisteredName);
        bool PracticeName_ChangeOccured(string SharedConnectionDataSource, string NewPracticeName, string DataBaseNameOrDataSetName);
        bool IsServerReachableFromTheInternet(string SharedConnectionDataSource);
        string CheckConnectivity(string SharedConnectionDataSource, string RegisteredName, string PracticeName, string DataBaseNameOrDataSetName);
    }

    [ComVisible(true), ClassInterface(ClassInterfaceType.None)]
    [Guid("803E5D8F-FD6D-4CC8-9DD7-0A2322C565AE")]
    public class MSLMobileConnect : IMSLMobileConnect
    {
        // Hard-coded in * Internal channel always uses this port
        internal const int INTERNAL_CHANNEL_PORT = 1945;

        // Use this host to check, if we are on the McKesson Network
        internal const string MCK_HOST_TO_CHECK_ON_MCK_NETWORK = "ndh1fs01.mckesson.com";        

        #region Logging

        /// <summary>
        /// FileWriter Object
        /// </summary>
        //private Yaulw.File.LoggerQuick _log = null;        

        /// <summary>
        /// Is this DLL running on McKesson Network
        /// </summary>
        private bool IsRunningOnMcKessonNetwork = false;
        private bool DidWeCheckForMcKessonNetwork = false;

        /// <summary>
        /// Setup the logger upon HasConnectivity Calls, this way it won't be done in the constructor
        /// with Medisoft stuff, you just never know
        /// </summary>
        //private void SetupLoggingIfNotSetup()
        //{
        //    if (!DidWeCheckForMcKessonNetwork)
        //    {
        //        IsRunningOnMcKessonNetwork = Yaulw.Net.IPHostHelper.CanResolveHost(MCK_HOST_TO_CHECK_ON_MCK_NETWORK,true);
        //        DidWeCheckForMcKessonNetwork = true;
        //    }

        //    if (_log == null)
        //    {
        //        string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\MSLConnect\\";
        //        _log = new Yaulw.File.LoggerQuick("MSLConnect", IsRunningOnMcKessonNetwork, path);
        //    }
        //}

        #endregion        

        #region Constructor

        /// <summary>
        /// Construct the beautiful 
        /// </summary>
        public MSLMobileConnect()
        {            
        }

        #endregion

        #region Connectivity

        /// <summary>
        /// Check to make sure we can communicate with the Mobile Service.
        /// Check the Server's ip & port for connectivity as well as that this
        /// computer has connectivity. 
        /// This way we won't throw any connectivity errors, which is good.
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <remarks>In order for this call to work port INTERNAL_CHANNEL_PORT outgoing must be open,
        /// Medisoft, Lytec unified Installer will be responsible for making sure Lytec.exe and MAPA.exe
        /// can open up port INTERNAL_CHANNEL_PORT by adding an exception to the windows firewall 
        /// netsh firewall add allowedprogram \"[Path]\[Program.exe]\" \"[Name]\" ENABLE ALL
        /// </remarks>
        /// <returns></returns>
        private bool HasConnectivity(string SharedConnectionDataSource)
        {
            //SetupLoggingIfNotSetup();
            //_log.Info("HasConnectivity called");
            //_log.Debug("With the following SharedConnectionString {0}", SharedConnectionDataSource);

            // Retrieve the IP from the DataSource and set it in the API
            string host = Yaulw.Net.IPHostHelper.GetServerNameFromAConnectionString(SharedConnectionDataSource);
            IPAddress ip = Yaulw.Net.IPHostHelper.GetIpForHost(host);
            //_log.Info("Determined the following Host:{0} and IP:{1}", host, ip.ToString());

            // Test if we can connect
            bool bCanConnect = Yaulw.Net.IPHostHelper.HasConnectivity(SharedConnectionDataSource, INTERNAL_CHANNEL_PORT);
            if (bCanConnect)
            {                
                API.SetNetworkSettings(ip.ToString(), INTERNAL_CHANNEL_PORT);
                //_log.Info("Network Connectivity to Pluto.MSL Service was established on IP:{0} and Internal Port:{1}", ip, INTERNAL_CHANNEL_PORT);
            }
            else
            {
                //_log.Error("Network Connectivity to Pluto.MSL Service could not be established to IP:{0} and Internal Port:{1} HasConnection={2}", ip, INTERNAL_CHANNEL_PORT, Yaulw.Net.IPHostHelper.HasConnection());
            }
            return bCanConnect;
        }          

        #endregion

        #region IMSLMobileConnect

        /// <summary>
        /// Called by Lytec/Medisoft when the mobile About Dialog is called to retrieve the UserApiKey and Pin
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <param name="PracticeName"></param>
        /// <param name="UserApiKey"></param>
        /// <param name="UserPin"></param>
        /// <returns></returns>
        public bool MobileAboutDialog_GotCalled(string SharedConnectionDataSource, string RegisteredName, string PracticeName,string DataBaseNameOrDataSetName,  out string UserApiKey, out string UserPin)
        {
            /* Ensure no nulls */
            if(SharedConnectionDataSource == null)
               SharedConnectionDataSource = String.Empty;
            if(RegisteredName == null)
               RegisteredName = String.Empty;
            if(PracticeName == null)
               PracticeName = String.Empty;
            if (DataBaseNameOrDataSetName == null)
                DataBaseNameOrDataSetName = String.Empty;

            //SetupLoggingIfNotSetup();
            //_log.Info("MobileAboutDialog_GotCalled Called");
            //_log.Debug("With the following SharedConnectionDataSource:{0},RegisteredName:{1},PracticeName:{2},DataBaseOrDataSetName:{3}", SharedConnectionDataSource, RegisteredName, PracticeName, DataBaseNameOrDataSetName);
            UserApiKey = String.Empty;
            UserPin = String.Empty;
            try
            {
                if (HasConnectivity(SharedConnectionDataSource))
                {
                    bool bSuccess = API.MobileAboutDialog_GotCalled(SharedConnectionDataSource, RegisteredName, PracticeName, DataBaseNameOrDataSetName, out UserApiKey, out UserPin);
                    //if(bSuccess)
                       //_log.Info("MobileAboutDialog_GotCalled returns true");
                    //else
                       //_log.Info("MobileAboutDialog_GotCalled returns false");
                    return bSuccess;
                }
            }
            catch (Exception e) 
            {
                //_log.Error("Exception Thrown {0}", e.Message);
            }
            //_log.Info("MobileAboutDialog_GotCalled returns false");
            return false;
        }

        /// <summary>
        /// Called by Lytec/Medisoft once product setup is complete (product is registered and 
        /// connected to a database), but before the user logs in. This will setup the Mobile service
        /// on the server.
        /// </summary>        
        /// <param name="SharedConnectionDataSource"></param>
        /// <param name="RegisteredName"></param>
        /// <returns></returns>
        public bool ProductSetupCompleted_PriorUserLogin(string SharedConnectionDataSource, string RegisteredName)
        {
            /* Ensure no nulls */
            if (SharedConnectionDataSource == null)
                SharedConnectionDataSource = String.Empty;
            if (RegisteredName == null)
                RegisteredName = String.Empty;            

            //SetupLoggingIfNotSetup();
            //_log.Info("ProductSetupCompleted_PriorUserLogin Called");
            //_log.Debug("With the following SharedConnectionDataSource:{0},RegisteredName:{1}", SharedConnectionDataSource, RegisteredName);
            try
            {
                if (HasConnectivity(SharedConnectionDataSource))
                {
                    bool bSuccess = API.ProductSetupCompleted_PriorUserLogin(SharedConnectionDataSource, RegisteredName);
                    if (bSuccess)
                    //    _log.Info("ProductSetupCompleted_PriorUserLogin returns true");
                    //else
                   //     _log.Info("ProductSetupCompleted_PriorUserLogin returns false");
                    return bSuccess;
                }
            }
            catch (Exception e)
            {
                //_log.Error("Exception Thrown {0}", e.Message);
            }
           // _log.Info("ProductSetupCompleted_PriorUserLogin returns false");
            return false;
        }

        /// <summary>
        /// Called by Lytec/Medisoft after a Practice Name Change occurred on their end to let
        /// the Mobile solution know and update it's information
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <param name="NewPracticeName"></param>
        /// <returns></returns>
        public bool PracticeName_ChangeOccured(string SharedConnectionDataSource, string NewPracticeName, string DataBaseNameOrDataSetName)
        {
            /* Ensure no nulls */
            if (SharedConnectionDataSource == null)
                SharedConnectionDataSource = String.Empty;
            if (NewPracticeName == null)
                NewPracticeName = String.Empty;            
            if (DataBaseNameOrDataSetName == null)
                DataBaseNameOrDataSetName = String.Empty;

            //SetupLoggingIfNotSetup();
            //_log.Info("PracticeName_ChangeOccured Called");
            //_log.Debug("With the following SharedConnectionDataSource:{0},NewPracticeName:{1},DataBaseOrDataSetName:{2}", SharedConnectionDataSource, NewPracticeName, DataBaseNameOrDataSetName);
            try
            {
                if (HasConnectivity(SharedConnectionDataSource))
                {
                    bool bSuccess = API.PracticeName_ChangeOccured(SharedConnectionDataSource, NewPracticeName, DataBaseNameOrDataSetName);
                    //if (bSuccess)
                    //    _log.Info("PracticeName_ChangeOccured returns true");
                    //else
                    //    _log.Info("PracticeName_ChangeOccured returns false");
                    return bSuccess;
                }
            }
            catch (Exception e)
            {
               // _log.Error("Exception Thrown {0}", e.Message);
            }
            //_log.Info("PracticeName_ChangeOccured returns false");
            return false;
        }

        /// <summary>
        /// Is the Server Reachable from the internet? if not, show error icon or give message
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <returns></returns>
        public bool IsServerReachableFromTheInternet(string SharedConnectionDataSource)
        {
            /* Ensure no nulls */
            if (SharedConnectionDataSource == null)
                SharedConnectionDataSource = String.Empty;            

            //SetupLoggingIfNotSetup();
            //_log.Info("IsServerReachableFromTheInternet Called");
            //_log.Debug("With the following SharedConnectionDataSource:{0}", SharedConnectionDataSource);
            try
            {
                if (HasConnectivity(SharedConnectionDataSource))
                {
                    bool bSuccess = API.IsServerReachableFromTheInternet(SharedConnectionDataSource);
                    if (bSuccess)
                    //    _log.Info("IsServerReachableFromTheInternet returns true");
                    //else
                    //    _log.Info("IsServerReachableFromTheInternet returns false");
                    return bSuccess;
                }
            }
            catch (Exception e)
            {
                //_log.Error("Exception Thrown {0}", e.Message);
            }
            //_log.Info("IsServerReachableFromTheInternet returns false");
            return false;
        }
        
        /// <summary>
        /// Checks for CheckConnectivity
        /// </summary>
        private enum Checks
        {
            Skipped,
            Passed,
            Failed
        }

        /// <summary>
        /// Check Connectivity Function simplifies the above calls by summarizing issues that occur when
        /// connecting the Mobile Device
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <param name="RegisteredName"></param>
        /// <param name="PracticeName"></param>
        /// <returns></returns>
        public string CheckConnectivity(string SharedConnectionDataSource, string RegisteredName, string PracticeName, string DataBaseNameOrDataSetName)
        {
            /* Ensure no nulls */
            if (SharedConnectionDataSource == null)
                SharedConnectionDataSource = String.Empty;
            if (RegisteredName == null)
                RegisteredName = String.Empty;
            if (PracticeName == null)
                PracticeName = String.Empty;
            if (DataBaseNameOrDataSetName == null)
                DataBaseNameOrDataSetName = String.Empty;

           // SetupLoggingIfNotSetup();
           // _log.Info("CheckConnectivity Called");
            //_log.Debug("With the following SharedConnectionDataSource:{0},RegisteredName:{1},PracticeName:{2},DataBaseOrDataSetName:{3}", SharedConnectionDataSource, RegisteredName, PracticeName, DataBaseNameOrDataSetName);
            StringBuilder sb = new StringBuilder();
            sb.Append("Checking connectivity to 'Mobile Service': {0}\n");
            sb.Append("Checking outgoing connectivity: {1}\n");
            sb.Append("Checking incoming connectivity: {2}\n");

            // 3 Checks
            Checks check1 = Checks.Skipped;
            Checks check2 = Checks.Skipped;
            Checks check3 = Checks.Skipped;

            // Check 1
            if (ProductSetupCompleted_PriorUserLogin(SharedConnectionDataSource, RegisteredName))
            {
                check1 = Checks.Passed;
                
                // Check 2
                string strUserApiKey;
                string strApiPin;
                if (MobileAboutDialog_GotCalled(SharedConnectionDataSource, RegisteredName, PracticeName, DataBaseNameOrDataSetName, out strUserApiKey, out strApiPin))
                {
                    check2 = Checks.Passed;

                    // Check 3
                    if (IsServerReachableFromTheInternet(SharedConnectionDataSource))
                    {
                        check3 = Checks.Passed;
                    }
                    else
                    {
                        check3 = Checks.Failed;
                    }
                }
                else
                {
                    check2 = Checks.Failed;
                }

                // * Newly Added * Once Check 1 passes we know we can connect to the service,
                // so might as well try to update the service, if needed, to make sure they are running the latest
                // however let's do it here, at the end, after performing all of our checks
                // ~Let's not do this, let them try to connect with their device (this was/is either too clever or
                // too silly) after re-consideration, i think it is best to skip
                //UpdateServiceIfNeeded(SharedConnectionDataSource);
            }
            else
            {
                check1 = Checks.Failed;
            }
           
            string retVal = String.Format(sb.ToString(), check1.ToString(), check2.ToString(), check3.ToString());
            //_log.Info("CheckConnectivity returns string:{0}", retVal);
            return retVal;
        }

        #endregion

        #region Pluto AddOns

        /// <summary>
        /// Try to Update the Service, if needed
        /// </summary>
        /// <param name="SharedConnectionDataSource"></param>
        /// <returns></returns>
        public void UpdateServiceIfNeeded(string SharedConnectionDataSource)
        {
            //SetupLoggingIfNotSetup();
            //_log.Info("UpdateServiceIfNeeded");
            //_log.Debug("With the following SharedConnectionDataSource:{0}", SharedConnectionDataSource);
            try
            {
                if (HasConnectivity(SharedConnectionDataSource))
                    API.UpdateServiceIfNeeded();
            }
            catch (Exception e) 
            {
                //_log.Error("Exception Thrown {0}", e.Message);
            }
        }

        #endregion
    }

}
