using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TAPI3Lib;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace WK5.Core.Services
{
    public class TAPIService
    {
        public List<ITAddress> TapiLines { get; set; } = new List<ITAddress>();
        public TAPIClass Tapi { get; set; } = new TAPIClass();


        public TAPIService()
        {
            InitializeTapi();
        }

        private void InitializeTapi()
        {
            Tapi.Initialize();
            TapiLines = GetLines().ToList();
        }

        private IEnumerable<ITCallInfo> GetCalls(ITAddress address)
        {
            IEnumCall ec = address.EnumerateCalls();
            uint arg = 0;
            ITCallInfo ici;

            bool stop = false;

            while (!stop)
            {
                try
                {
                    ec.Next(1, out ici, ref arg);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                    stop = true;
                    break;
                }
                if(ici != null)
                {
                    yield return ici;
                }
                else
                {
                    stop = true;
                    break;
                }
            }
        }

        public string? Call(ITAddress address, string destinationPhoneNumber)
        {
            if (String.IsNullOrWhiteSpace(destinationPhoneNumber))
            {
                return "Es wurde keine Telefonnummer angegeben.";
            }

            foreach (ITCallInfo info in GetCalls(address))
            {
                if (info.CallState is not CALL_STATE.CS_DISCONNECTED and not CALL_STATE.CS_IDLE)
                {
                    return "Über die aktuelle Line wird bereits ein Anruf ausgeführt!";
                }
            }

            try
            {
                ITBasicCallControl callControl = address.CreateCall(destinationPhoneNumber, TapiConstants.LINEADDRESSTYPE_PHONENUMBER, TapiConstants.TAPIMEDIATYPE_AUDIO);
                //callControl.SetQOS(TapiConstants.TAPIMEDIATYPE_AUDIO, QOS_SERVICE_LEVEL.QSL_BEST_EFFORT);
                callControl.Connect(false);
                //callControl.Dial(destinationPhoneNumber);
            }
            catch (COMException ex)
            {
                return $"Fehler beim starten des Anruf. Vermutlich ist KarleyPhone mit deiner Line noch offen. \r\nFehler ({ex.ErrorCode}) {ex.Message}";
            }

            return null;
        }

        public string? Call(int line, string destinationPhoneNumber)
        {
            destinationPhoneNumber = SanitizePhoneNumber(destinationPhoneNumber);
            ITAddress? address = GetLine(line);

            if (address is null)
            {
                return "Es konnte keine passende Line gefunden werden über die angerufen werden soll.";
            }

            return Call(address, destinationPhoneNumber);
        }

        public string? Call(string callerNumber, string destinationPhoneNumber)
        {
            if (String.IsNullOrWhiteSpace(callerNumber))
            {
                return "Die Telefonnummer von der der Anruf ausgehen soll wurde nicht gefunden.";
            }

            if (String.IsNullOrWhiteSpace(destinationPhoneNumber))
            {
                return "Es wurde keine zu wählende Telefonnummer angegeben.";
            }

            int line = GetLineFromTelefonnummer(callerNumber);
            if (LineExists(line))
            {
                return Call(line, destinationPhoneNumber);
            }

            return "Zu deiner Telefonnummer konnte keine passende Line gefunden werden.";
        }

        public string? Call(AppMitarbeiter mitarbeiter, string destinationPhoneNumber)
        {
            if (String.IsNullOrWhiteSpace(mitarbeiter.Telefonnummer))
            {
                return "Die Telefonnummer von der der Anruf ausgehen soll wurde nicht gefunden.";
            }

            if (String.IsNullOrWhiteSpace(destinationPhoneNumber))
            {
                return "Es wurde keine zu wählende Telefonnummer angegeben.";
            }

            return Call(mitarbeiter.Telefonnummer, destinationPhoneNumber);

        }

        public bool IsInCall(int line)
        {
            ITAddress? address = GetLine(line);

            if (address is null)
            {
                return false;
            }

            return IsInCall(address);
        }

        public bool IsInCall(ITAddress address)
        {
            foreach (ITCallInfo info in GetCalls(address))
            {
                if (info.CallState is not CALL_STATE.CS_DISCONNECTED and not CALL_STATE.CS_IDLE)
                {
                    return true;
                }

            }
            return false;
        }

        public ITBasicCallControl? GetActiveCallControl(int line) => (ITBasicCallControl?)GetActiveCallInfo(line);

        public ITCallInfo? GetActiveCallInfo(int line)
        {
            ITAddress? address = GetLine(line);

            if (address is null)
            {
                return null;
            }

            foreach (ITCallInfo info in GetCalls(address))
            {
                if (info.CallState is not CALL_STATE.CS_DISCONNECTED and not CALL_STATE.CS_IDLE)
                {
                    return info;
                }
                else
                {
                    return null;
                }

            }
            return null;
        }

        public record ActiveCall
        {
            public string? CallerIdName { get; set; }
            public string? CallerIdNumber { get; set; }
            public string? CalledIdName { get; set; }
            public string? CalledIdNumber { get; set; }
            public string? ConnectedName { get; set; }
            public string? ConnectedNumber { get; set; }
            public string? RedirectionName { get; set; }
            public string? RedirectionNumber { get; set; }
            public string? RedirectingName { get; set; }
            public string? RedirectingNumber { get; set; }
            public string? CalledPartyFriendlyName { get; set; }
            public string? Comment { get; set; }
            public string? DisplayableAddress { get; set; }
            public string? CallingPartyId { get; set; }

            public static ActiveCall FromCallInfo(ITCallInfo info)
            {
                return new ActiveCall()
                {
                    CallerIdName = info.get_CallInfoString(CALLINFO_STRING.CIS_CALLERIDNAME),
                    CallerIdNumber = info.get_CallInfoString(CALLINFO_STRING.CIS_CALLERIDNUMBER),
                    CalledIdName = info.get_CallInfoString(CALLINFO_STRING.CIS_CALLEDIDNAME),
                    CalledIdNumber = info.get_CallInfoString(CALLINFO_STRING.CIS_CALLEDIDNUMBER),
                    ConnectedName = info.get_CallInfoString(CALLINFO_STRING.CIS_CONNECTEDIDNAME),
                    ConnectedNumber = info.get_CallInfoString(CALLINFO_STRING.CIS_CONNECTEDIDNUMBER),
                    RedirectionName = info.get_CallInfoString(CALLINFO_STRING.CIS_REDIRECTIONIDNAME),
                    RedirectionNumber = info.get_CallInfoString(CALLINFO_STRING.CIS_REDIRECTIONIDNUMBER),
                    RedirectingName = info.get_CallInfoString(CALLINFO_STRING.CIS_REDIRECTINGIDNAME),
                    RedirectingNumber = info.get_CallInfoString(CALLINFO_STRING.CIS_REDIRECTINGIDNUMBER),
                    CalledPartyFriendlyName = info.get_CallInfoString(CALLINFO_STRING.CIS_CALLEDPARTYFRIENDLYNAME),
                    Comment = info.get_CallInfoString(CALLINFO_STRING.CIS_COMMENT),
                    DisplayableAddress = info.get_CallInfoString(CALLINFO_STRING.CIS_DISPLAYABLEADDRESS),
                    CallingPartyId = info.get_CallInfoString(CALLINFO_STRING.CIS_CALLINGPARTYID),
                };
            }
        }

        public void EndCall(int line)
        {
            ITAddress? address = GetLine(line);

            if (address is null)
            {
                return;
            }

            EndAllCalls(address);

        }

        public void EndAllCalls(ITAddress address)
        {
            foreach (ITCallInfo info in GetCalls(address))
            {
                try
                {
                    ITBasicCallControl callControl = (ITBasicCallControl)info;
                    callControl.Disconnect(DISCONNECT_CODE.DC_NORMAL);
                    //info.ReleaseUserUserInfo();
                    info.Address.TAPIObject.Shutdown();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                }

            }
        }

        public int GetLineFromTelefonnummer(string nummer)
        {
            string[] checkSplitters = new string[] { "-", " " };
            foreach (string splitter in checkSplitters)
            {
                string[] numberParts = nummer.Split(splitter);
                if (Int32.TryParse(numberParts.Last(), out int line))
                {
                    return line;
                }
            }

            return -1;
        }

        public int GetMitarbeiterLine(AppMitarbeiter mitarbeiter)
        {
            if (!String.IsNullOrWhiteSpace(mitarbeiter.Telefonnummer))
            {
                return GetLineFromTelefonnummer(mitarbeiter.Telefonnummer);
            }
            else
            {
                return -1;
            }
        }

        public ITAddress? GetLine(int line)
        {
            foreach (ITAddress address in TapiLines)
            {
                if (Int32.TryParse(address.DialableAddress, out int dialableAddress))
                {
                    if (line == dialableAddress)
                    {
                        return address;
                    }
                }
            }
            return null;
        }
        public bool LineExists(int line)
        {
            return GetLine(line) != null;
        }

        public IEnumerable<ITAddress> GetLines()
        {
            if (Tapi.Addresses is not null)
            {
                ITCollection? addresses = Tapi.Addresses as ITCollection;
                if (addresses is not null)
                {
                    foreach (ITAddress address in addresses)
                    {
                        yield return address;
                    }
                }
            }
        }
        public static string SanitizePhoneNumber(string phoneNumber)
        {
            if (phoneNumber.StartsWith("+"))  //Wenn mit + startet, ist es vermutlich ausland - also + in 00 ändern!
            {
                phoneNumber = "00" + phoneNumber;
            }

            if (phoneNumber.StartsWith("000"))
            {
                phoneNumber = "00" + phoneNumber[3..];
            }

            if (!phoneNumber.StartsWith("#")) //Wenn mit # starten, dann intern und keine 0 hinzu
            {
                phoneNumber = "0" + phoneNumber;
            }

            Regex rgx = new Regex("\\D");
            phoneNumber = rgx.Replace(phoneNumber, "");

            return phoneNumber;
        }

        public static string SanitizePhoneNumberNew(string phoneNumber)
        {
            if (phoneNumber.StartsWith("0"))
            {
                phoneNumber = "0" + phoneNumber;
            }

            if (phoneNumber.StartsWith("+"))
            {
                phoneNumber.TrimStart('+');
                phoneNumber = "00" + phoneNumber;
            }

            return new Regex("\\D").Replace(phoneNumber, "");
        }

    }
}