using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

using IpPbx.CLMgrLib;

using C4IT.Logging;

using static C4IT.Logging.cLogManager;
using System.Runtime.Remoting.Channels;

namespace F4SD_PhoneMonitor
{
    public delegate void ActiveCallMessage(string phone, string name, bool isOutgoing);

    public class cCLMgrEvents
    {
        public ActiveCallMessage ActiveCallHandler;

        private ClientLineMgrClass pCLMgr = null;
        private ClientSdkEventSink MyEventSink = null;

        private PubCLMgrLineDetails lastChangedLineDetails;

        public bool Initialize()
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                lastChangedLineDetails.m_sPeerName = null;
                lastChangedLineDetails.m_sPeerNumber = null;

                pCLMgr = new ClientLineMgrClass();
                if (pCLMgr == null)
                {
                    LogEntry("Error conntection SwyxIt client interface. Terminating...", LogLevels.Fatal);
                    return false;
                }

                pCLMgr.GetVersion(out var Info);
                if (Info.dwMajorVersion < 11)
                {
                    LogEntry("Incompatible SwyxIt client version. Terminating...", LogLevels.Fatal);
                    return false;
                }

                MyEventSink = new ClientSdkEventSink();
                MyEventSink.Connect(pCLMgr, new LineManagerMessageHandler(OnLineManagerMessage));

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

            return false;
        }

        private void OnLineManagerMessage(ClientSdkEventArgs e)
        {
            var _msg = e.Msg.ToString();
            if (int.TryParse(_msg, out _))
                return;

            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                LogEntry($"Client event signaled {e.Msg.ToString()}", LogLevels.Debug);

                switch (e.Msg)
                {
                    case CLMgrMessage.CLMgrLineStateChangedMessage:
                        IClientLinePub changedLinePub = (IClientLinePub)pCLMgr.DispGetLine(e.Param);
                        PubCLMgrLineDetails changedLineDetails;
                        changedLinePub.PubGetDetails(out changedLineDetails);
                        lastChangedLineDetails = changedLineDetails;
                        break;
                    case CLMgrMessage.CLMgrLineStateChangedMessageEx:
                        //Get Line
                        int line = e.Param & 0xff;
                        int high = e.Param >> 8;

                        LineState NewLineState;
                        //Get the LineState
                        NewLineState = (LineState)high;
                        LogEntry($"New line state: {NewLineState}", LogLevels.Debug);
                        if (NewLineState == LineState.Active)
                        {
                            if (lastChangedLineDetails.m_sPeerNumber != null)
                            {
                                var IsOutgoing = lastChangedLineDetails.m_bIsOutgoing == 1;
                                var _strDir = IsOutgoing ? "to" : "from";
                                LogEntry($"Active call {_strDir}: phone={lastChangedLineDetails.m_sPeerNumber}, name={lastChangedLineDetails.m_sPeerName}", LogLevels.Debug);
                                if (ActiveCallHandler != null)
                                    ActiveCallHandler.Invoke(lastChangedLineDetails.m_sPeerNumber, lastChangedLineDetails.m_sPeerName, IsOutgoing);
                            }
                        }
                        break;

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

        public void Disconnect()
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (MyEventSink != null)
                    MyEventSink.Disconnect();
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }
    }

    public class ClientSdkEventArgs : EventArgs
    {
        public CLMgrMessage Msg;
        public int Param;

        public ClientSdkEventArgs(CLMgrMessage msg, int param)
        {
            Msg = msg;
            Param = param;
        }
    }

    public delegate void LineManagerMessageHandler(ClientSdkEventArgs e);

    public class ClientSdkEventSink
    {
        private ClientLineMgrClass ConnectedLineManager;
        private IClientLineMgrEventsPub_PubOnLineMgrNotificationEventHandler EventHandler;
        private LineManagerMessageHandler LineManagerMessageDelegateOfForm;

        public ClientSdkEventSink()
        {
            EventHandler = new IClientLineMgrEventsPub_PubOnLineMgrNotificationEventHandler(clmgr_EventSink);
        }

        public void Connect(ClientLineMgrClass lineManager, LineManagerMessageHandler lineManagerMessageDelegateOfForm)
        {
            ConnectedLineManager = lineManager;
            LineManagerMessageDelegateOfForm = lineManagerMessageDelegateOfForm;

            //add eventhandler for the PubOnlineMgrNotification Events
            ConnectedLineManager.PubOnLineMgrNotification += EventHandler;
        }

        public void Disconnect()
        {
            //remove eventhandler for the PubOnlineMgrNotification Events
            ConnectedLineManager.PubOnLineMgrNotification -= EventHandler;
            ConnectedLineManager = null;
            LineManagerMessageDelegateOfForm = null;
        }

        private void clmgr_EventSink(int msg, int param)
        {
            //this method receives the COM events from the client line manger
            if ((LineManagerMessageDelegateOfForm != null))
            {
                LineManagerMessageDelegateOfForm?.Invoke(new ClientSdkEventArgs((CLMgrMessage)msg, param));
            }
        }
    }

    public enum CLMgrMessage
    {
        CLMgrLineStateChangedMessage = 0,      //state of at least one line has changed
        CLMgrLineSelectionChangedMessage = 1,  //line in focus has changed
        CLMgrLineDetailsChangedMessage = 2,    //details of at least one line have changed
        CLMgrCallDetailsMessage = 4,           //details of last call are available, post mortem for logging purpose
        CLMgrServerDownMessage = 5,            //server goes down, keep line manager, wait for ServerUp message
        CLMgrServerUpMessage = 6,              //server is up again, keep interfaces to line manger
        CLMgrWaveDeviceChanged = 7,            //speaker / micro has been switched on / off
        CLMgrGroupCallNotificationMessage = 8, //notification about group call
        CLMgrNumberOfLinesChangedMessage = 10, //the number of lines has changed
        CLMgrClientShutDownRequest = 11,       //Client Line Manager requests client to shutdown and release all interfaces
        CLMgrLineStateChangedMessageEx = 28,   //state of certain line has changed, lParam: LOWORD: line index of line that changed its state (starting with 0) HIWORD: new state of this line
        CLMgrSIPRegistrationStateChanged = 30, //registration state of SIP account has changed
        //lParam: LOBYTE: Account index
        //        HIBYTE: new state
        CLMgrWaveFilePlayed = 31,              //wave file playback finished
        //lParam: line index;
        //if -1, the message is related to a LineMgr function PlaySoundFile or PlayToRtp
        //if >=0 the message is related to a line function PlaySoundFile of line with this index
        PubCLMgrFirstDataReceived = 32         //first RTP data received on line, might be silence
        //lParam: line index;
    }

    public enum LineState
    {
        Inactive = 0,                        //line is inactive
        HookOffInternal = 1,                 //off hook, internal dialtone
        HookOffExternal = 2,                 //off hook, external dialtone
        Ringing = 3,                         //incoming call, ringing
        Dialing = 4,                         //outgoing call, we are dialing, no sound
        Alerting = 5,                        //outgoing call, alerting = ringing on destination
        Knocking = 6,                        //outgoing call, knocking = second call ringing on destination
        Busy = 7,                            //outgoing call, destination is busy
        Active = 8,                          //incoming / outgoing call, logical and physical connection is established
        OnHold = 9,                          //incoming / outgoing call, logical connection is established, destination gets music on hold
        ConferenceActive = 10,               //incoming / outgoing conference, logical and physical connection is established
        ConferenceOnHold = 11,               //incoming / outgoing conference, logical connection is established, not physcically connected
        Terminated = 12,                     //incoming / outgoing connection / call has been disconnected
        Transferring = 13,                   //special LSOnHold, call is awaiting to be transferred, peer gets special music on hold
        Disabled = 14                        //special LSInactive: wrap up time
    }

    public enum DisconnectReason
    {
        Normal = 0,
        Busy = 1,
        Rejected = 2,
        Cancelled = 3,
        Transferred = 4,
        JoinedConference = 5,
        NoAnswer = 6,
        TooLate = 7,
        DirectCallImpossible = 8,
        WrongNumber = 9,
        Unreachable = 10,
        CallDiverted = 11,
        CallRoutingFailed = 12,
        PermissionDenied = 13,
        NetworkCongestion = 14,
        NoChannelAvailable = 15,
        NumberChanged = 16,
        IncompatibleDestination = 17
    }

}
