using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Pipes;
using System.Text;
using System.Diagnostics;

using Microsoft.Win32;

using Newtonsoft.Json;

using C4IT.Logging;
using C4IT.F4SD.TAPI;
using static C4IT.Logging.cLogManager;

using FasdDesktopUi.Basics;

namespace F4SD_PhoneMonitor
{
    static class Program
    {
        public static string sendPipeName = null;
        public static string listenPipeName = null;
        public static string tapiLine = null;
        public static bool SignalOutgoingCall = false;
        public static string ExternalCallPrefix = "";
        public static bool IsSimulation = false;

        public static int SendError = 0;

        public static cCLMgrEvents lmg = null;

        public static System.Timers.Timer timerPolling = new System.Timers.Timer(100);

        public static cF4sdPipeServer pipeServer = null;

        public static bool StopImmediate = false;

        [STAThread]
        static int Main(string[] args)
        {
            try
            {
                cLogManagerFile.CreateInstance(false, SubFolder: "Logs");
                cLogManager.DefaultLogger.LogAssemblyInfo();
            }
            catch
            {
                return 1;
            }


            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (args.Length < 3 || args.Length > 6)
                {
                    LogEntry("No valid pipe names at command line. Terminating...", LogLevels.Fatal);
                    return -1;
                }

                sendPipeName = args[0];
                listenPipeName = args[1];
                tapiLine = args[2];

                if (args.Length >= 4)
                    ExternalCallPrefix = args[3];

                if (args.Length >= 5)
                {
                    if (args[4] == "1")
                        SignalOutgoingCall = true;
                }

                if (args.Length >= 6)
                {
                    if (args[5] == "simulation")
                        IsSimulation = true;
                }

                if (tapiLine.ToLowerInvariant() == "swyxitnative")
                {
                    if (!ConnectSwyxit())
                        return -1;
                }
                else
                {
                    if (!ConnectTapi(tapiLine))
                    {
                        System.Environment.Exit(-1);
                    }
                }

                timerPolling.Elapsed += TimerPolling_Elapsed;
                timerPolling.Start();

                var SessionId = 0;
                try
                {
                    SessionId = Process.GetCurrentProcess().SessionId;
                }
                catch { }

                pipeServer = new cF4sdPipeServer();
                pipeServer.PipeMessage += PipeServer_PipeMessage;
                pipeServer.Listen(listenPipeName, MaxSize: 255, LowerIntegrity: true);
                LogEntry($"Start listening on named pipe '{listenPipeName}'...", LogLevels.Debug);


                SystemEvents.SessionEnding += SystemEvents_SessionEnding;

                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.ApplicationExit += OnApplicationExit;
                Application.Run();

                return 0;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return -1;            
        }

        static bool ConnectSwyxit()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var regObj = Registry.GetValue("HKEY_CLASSES_ROOT\\CLSID\\{F8E552F8-4C00-11D3-80BC-00105A653379}\\VersionIndependentProgID", null, null);
                if (!(regObj is string strObj) || strObj.ToLowerInvariant() != "clmgr.clientlinemgr")
                {
                    LogEntry("SwyxIt client seems  not to be installed. Terminating...", LogLevels.Fatal);
                    return false;
                }

                lmg = new cCLMgrEvents();
                if (!lmg.Initialize())
                    return false;

                lmg.ActiveCallHandler += ActiveCallMessage;

                return true;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return false;    

        }

        static bool ConnectTapi(string Line)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                C4TapiHelper.Instance = new C4TapiHelper();
                C4TapiHelper.Instance.Initialize(TapiMessageHandler);
                C4TapiHelper.Instance.GetLines();
                if (C4TapiHelper.Instance.ConnectLine(Line))
                    return true;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return false;
        }

        private static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
        {
            LogEntry("User logout detected. Terminating...", LogLevels.Debug);
            Application.Exit();
        }

        private static void TimerPolling_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                timerPolling.Stop();
                if (StopImmediate)
                {
                    Application.Exit();
                    return;
                }

                timerPolling.Interval = 10000;
                bool res = false;
                lock (sendPipeName)
                {
                    res = Send("Hello", sendPipeName);
                };

                if (res)
                    SendError = 0;
                else
                {
                    SendError++;
                    if (SendError == 2)
                        Application.Exit();
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
            timerPolling.Start();
        }

        static private void OnApplicationExit(object sender, EventArgs e)
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                timerPolling.Stop();

                try
                {
                    if (lmg != null)
                    {
                        lmg.ActiveCallHandler -= ActiveCallMessage;
                        lmg.Disconnect();
                    }
                }
                catch { }

                try
                {
                    if (C4TapiHelper.Instance != null)
                    {
                        C4TapiHelper.Instance.Dispose();
                        C4TapiHelper.Instance = null;
                    }
                }
                catch { }

                if (pipeServer != null)
                {
                    LogEntry("stopping pipe server...", LogLevels.Debug);
                    pipeServer.Stop();
                    pipeServer.Dispose();
                    pipeServer = null;
                    LogEntry("...finished.", LogLevels.Debug);
                }

                //Application.ApplicationExit -= OnApplicationExit;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

        }

        static void ActiveCallMessage(string phone, string name, bool isOutgoing)
        {
            if (isOutgoing && !SignalOutgoingCall)
            {
                LogEntry("An outgoing call is signaled but signaling outgoing calls is not enabled.");
                return;
            }

            if (sendPipeName == null)
                return;

            if (string.IsNullOrWhiteSpace(phone))
                return;


            if (name == null)
                name = "";

            var phone2 = phone.Trim();
            if (!string.IsNullOrEmpty(ExternalCallPrefix) && phone2.StartsWith(ExternalCallPrefix))
                phone2 = phone2.Remove(0,ExternalCallPrefix.Length);

            if (phone2.StartsWith("+"))
            {
                var _r = Math.Min(phone2.Length, 3);
                phone2 = phone2.Remove(0, _r);
            }
            else if (phone2.StartsWith("00"))
            {
                var _r = Math.Min(phone2.Length, 4);
                phone2 = phone2.Remove(0, _r);
            }
            if (phone2.StartsWith("0"))
                phone2 = phone2.Remove(0, 1);

            var phone3 = "";
            foreach (var C in phone2)
                if ((C >= '0') && (C <= '9'))
                    phone3 += C;

            var name2 = name.Trim();
            var _strDir = isOutgoing ? "outgoing" : "incoming";
            LogEntry($"Signaling {_strDir} call: phone={phone2}, name={name2}", LogLevels.Debug);

            var Info = JsonConvert.SerializeObject(new cPhoneSearchParameters() { phone = phone3, name = name2 });
            lock (sendPipeName)
            {
                Send($"phonesearch: {Info}", sendPipeName);
            }
        }

        static public bool Send(string SendStr, string PipeName, int TimeOut = 1000)
        {
            try
            {
                if (IsSimulation)
                {
                    LogEntry($"simulatied pipe send: {SendStr}");
                    return true;
                }
                NamedPipeClientStream pipeStream = new NamedPipeClientStream
                   (".", PipeName, PipeDirection.Out, PipeOptions.WriteThrough);

                // The connect function will indefinitely wait for the pipe to become available
                // If that is not acceptable specify a maximum waiting time (in ms)
                pipeStream.Connect(TimeOut);

                byte[] _buffer = Encoding.UTF8.GetBytes(SendStr);
                pipeStream.Write(_buffer, 0, _buffer.Length);
                pipeStream.Flush();
                pipeStream.Close();
                pipeStream.Dispose();

                return true;
            }
            catch (TimeoutException oEX)
            {
                if (DefaultLogger.IsDebug)
                    cLogManager.DefaultLogger.LogException(oEX);
            }

            return false;
        }

        static private void PipeServer_PipeMessage(string Reply)
        {
            if (Reply.ToLowerInvariant() == "stop")
            {
                timerPolling.Stop();
                StopImmediate = true;
                timerPolling.Interval = 100;
                timerPolling.Start();

            }
        }

        private static void TapiMessageHandler(C4TapiHelper.C4TapiLineInfo lineInfo)
        {
            if (lineInfo.eventType == C4TapiHelper.eTapiEventType.connected)
            {

                var CM = MethodBase.GetCurrentMethod();
                LogMethodBegin(CM);
                try
                {
                    ActiveCallMessage(lineInfo.participantPhoneNumber, lineInfo.participantName, lineInfo.IsOutbound);
                }
                catch (Exception E)
                {
                    LogException(E);
                }
                finally
                {
                    LogMethodEnd(CM);
                }
            }
        }

        public class cPhoneSearchParameters
        {
            public string phone { get; set; }
            public string name { get; set; } = null;
        }
    }

}
