using System;
using System.Collections.Generic;
using OPSSDK;
using Ozeki.Media.MediaHandlers;
using OPSSDKCommon.Model.Message;
using System.Reflection;

namespace OPS_SimpleExample
{
    class Program
    {
        static OpsClient client;
        static IAPIExtension apiExtension;
        static ICall call;
        static string prompt = string.Empty;


        static void Main(string[] args)
        {
            ExtractSDK();

            ShowGreetingMessage();
            ReadLoginInfos();
            ReadCommands();
        }

        private static void ShowGreetingMessage()
        {
            Console.WriteLine("This is a simple Ozeki Phone System XE demo written in C#.");
            Console.WriteLine("It can be used to setup calls, send sms's and receive");
            Console.WriteLine("corresponding delivery reports.");
            Console.WriteLine("The source code of this demo can be downloaded from: ");
            Console.WriteLine("http://www.ozekiphone.com/c-net-voip-developers-740.html");
            Console.WriteLine("-------------------------------------------------------------------------------");
            Console.WriteLine();
        }

        private static void ReadLoginInfos()
        {
            Console.WriteLine("Please enter the IP address of Ozeki Phone System XE.");

            var serverAddress = Read("Server address (default: 127.0.0.1)", false);

            if (string.IsNullOrEmpty(serverAddress))
                serverAddress = "127.0.0.1";

            Console.WriteLine("Please enter the username and password of the user created in Ozeki Phone System XE");
            var username = Read("Username (default: admin)", false);

            if (string.IsNullOrEmpty(username))
                username = "admin";

            var password = Read("Password", true);
            Login(serverAddress, username, password);
        }

        private static void Login(string serverAddress, string username, string password)
        {
            client = new OpsClient();
            Console.WriteLine("Connecting...");

            var result = client.Login(serverAddress, username, password);

            if (result)
            {
            
                Console.WriteLine("Successfully connected to {0} with username: {1}.", serverAddress, username);
                apiExtension = GetAPIExtension();


                prompt = string.Format("[{0}@{1}]> ", apiExtension.ExtensionId, serverAddress);
                apiExtension.IncomingCall += apiExtension_IncomingCall;
                apiExtension.MessageDelivered += apiExtension_MessageDelivered;
                apiExtension.MessageSubmitted += apiExtension_MessageSubmitted;
                apiExtension.MessageReceived += apiExtension_MessageReceived;
                client.ErrorOccurred += client_ErrorOccurred;
            }
            else
            {
                Console.WriteLine("Connection fail try again.");
                Console.WriteLine("Please check whether the IP address is correct and the given user has right to use OPS SDK. This can be checked under PBX features/Preferences/User access profile.");
                ReadLoginInfos();
            }

        }

        static void client_ErrorOccurred(object sender, ErrorInfo e)
        {
            Console.WriteLine(e.Message);
        }

        static void apiExtension_MessageDelivered(object sender, MessageResultEventArgs e)
        {
            Console.WriteLine("Message delivery report received. Message id: {0}, result: {1}", e.CallbackId, e.Result);
        }

        static void apiExtension_MessageSubmitted(object sender, MessageResultEventArgs e)
        {
            Console.WriteLine("Message submit report received. Message id: {0}, result: {1}", e.CallbackId, e.Result);
        }

        private static void ReadCommands()
        {
            Console.WriteLine("This demo features the following commands.");
            Console.WriteLine("The first word specifies the name of the command followed by the mandatory command parameters.");

            ShowHelp();

            while (true)
            {
                Console.Write(prompt);
                var command = Console.ReadLine();

                if(string.IsNullOrEmpty(command))
                    continue;

                var parameters = GetStringParts(command);

                var commandName = parameters[0].ToLower();

                parameters.RemoveAt(0);
                switch (commandName)
                {
                    case "sendsms":
                        SendSMSMessage(parameters);
                        break;
                    case "call":
                        Call(parameters);
                        break;
                    case "callandsay":
                        CallAndSay(parameters);
                        break;
                    case "hangup":
                        HangupCall();
                        break;
                    case "exit":
                        Exit();
                        break;
                    case "help":
                        ShowHelp();
                        break;
                    default:
                        Console.WriteLine("Invalid command.");
                        break;
                }
                
            }
        }

        private static void ShowHelp()
        {
            Console.WriteLine();
            Console.WriteLine("Available commands:");
            Console.WriteLine();

            Console.WriteLine("sendsms <recipient> <content>");
            Console.WriteLine("call <target>");
            Console.WriteLine("callandsay <target> <message>");
            Console.WriteLine("hangup");
            Console.WriteLine("help");
            Console.WriteLine("exit");
            Console.WriteLine();
        }

        private static void Exit()
        {
            Environment.Exit(0);
        }

        private static void CallAndSay(List<string> parameters)
        {
            if (call != null)
            {
                Console.WriteLine("Call already in progress. Please hangup the current call.");
                return;
            }

            if (parameters.Count != 2)
            {
                Console.WriteLine("Invalid callandsay parameters.");
                return;
            }

            call = apiExtension.CreateCall(parameters[0]);
            SubscribeCallEvents();

            var textToSpeech = new TextToSpeech();
            textToSpeech.Stopped += (sender, e) => HangupCall();

            call.CallStateChanged += (sender, e)=>
                                         {
                                             if(e.Item.IsInCall())
                                             {
                                                 call.ConnectAudioSender(textToSpeech);
                                                 textToSpeech.AddAndStartText(parameters[1]);
                                             }
                                                
                                             
                                         };
            call.Start();
        }

        private static void HangupCall()
        {
            if(call != null)
            {
                call.HangUp();
                call = null;
            }
        }

        private static void Call(List<string> parameters)
        {
            if (call != null)
            {
                Console.WriteLine("Call already in progress. Please hangup the current call.");
                return;
            }
            if(parameters.Count != 1)
            {
                Console.WriteLine("Invalid call parameters.");
                return;
            }

            call = apiExtension.CreateCall(parameters[0]);
            InitCall();
            call.Start();
       
        }

        private static void InitCall()
        {
            AttachCallHandlers();
            SubscribeCallEvents();
        }

        private static void SubscribeCallEvents()
        {
            call.CallStateChanged += call_CallStateChanged;
            call.CallErrorOccurred += call_CallErrorOccurred;
        }

        static void call_CallErrorOccurred(object sender, Ozeki.VoIP.VoIPEventArgs<OPSSDKCommon.Model.Call.CallError> e)
        {
            call = null;
            Console.WriteLine("Call error occurred: {0}", e.Item);
        }

        static void call_CallStateChanged(object sender, Ozeki.VoIP.VoIPEventArgs<OPSSDKCommon.Model.Call.CallState> e)
        {
            if (e.Item.IsCallEnded())
                call = null;

            Console.WriteLine("Call state changed: {0}", e.Item);
        }

        private static void SendSMSMessage(List<string> parameters)
        {
            if(parameters.Count != 2)
            {
                Console.WriteLine("Invalid sendsms parameters.");
                return;
            }

            var smsMessage = new SMSMessage(parameters[0], parameters[1]);

            var result = apiExtension.SendMessage(smsMessage);
      
            Console.WriteLine("SMS message sent. Message id: {0}, result: {1}", smsMessage.MessageId, result != null ? result.RoutingState.ToString() : string.Empty);
        }

        private static string Read(string inputName, bool readWhileEmpty)
        {
            Console.Write(prompt + "" + inputName + ": ");
            while (true)
            {
                var input = Console.ReadLine();

                if (!readWhileEmpty)
                    return input;

                if (!string.IsNullOrEmpty(input))
                    return input;

                Console.WriteLine(inputName + " cannot be empty.");
                Console.Write(inputName + ": ");
            }
        }

       

        private static IAPIExtension GetAPIExtension()
        {
            Console.WriteLine("Select API extension.");

            var id = Read("API Extension phone number", true);

            var extension = client.GetAPIExtension(id);

            if(extension == null)
            {
                Console.WriteLine("Invalid API Extension phone number.");
                return GetAPIExtension();
            }

            return extension;

        }

        static void apiExtension_MessageReceived(object sender, Message e)
        {
            Console.WriteLine("Message received from: {0}, content: {1}", e.Sender, e.Content);
        }

        static void apiExtension_IncomingCall(object sender, Ozeki.VoIP.VoIPEventArgs<ICall> e)
        {
            Console.WriteLine("Incoming call received from: " + e.Item.OtherParty);

            if(call != null)
            {
                Console.WriteLine("Another call in progress. Incoming call rejected automatically.");
                e.Item.Reject();
                return;
            }

            call = e.Item;

            InitCall();
            call.Accept();
           
        }

        private static void AttachCallHandlers()
        {
            var speaker = Speaker.GetDefaultDevice();

            if(speaker != null)
            {
                call.ConnectAudioReceiver(speaker);
                speaker.Start();
            }

            var microphone = Microphone.GetDefaultDevice();

            if(microphone != null)
            {
                call.ConnectAudioSender(microphone);
                microphone.Start();
            }
        }

        private static void ExtractSDK()
        {
            AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
            {
                if (args.Name.Contains("OPSSDK"))
                {
                    using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("OPS_SimpleExample.OPSSDK.dll"))
                    {
                        var data = new byte[stream.Length];

                        stream.Read(data, 0, data.Length);

                        return Assembly.Load(data);

                    }
                }

                return null;
            };
        }

        static List<string> GetStringParts(string inputString)
        {
            var retVal = new List<string>();
            string currentPart = string.Empty;
            int lexerState = 0;

            for (int i = 0; i < inputString.Length; i++)
            {
                switch (lexerState)
                {
                    case 0:
                        if (inputString[i] == ' ')
                        {
                            var res = currentPart.Trim();

                            if(res != string.Empty)
                                retVal.Add(currentPart.Trim());

                            currentPart = string.Empty;
                        }
                        else if (inputString[i] == '"')
                            lexerState = 1;
                        else
                            currentPart += inputString[i];
                        break;

                    case 1:
                        if (inputString[i] == '"')
                            lexerState = 0;
                        else
                            currentPart += inputString[i];
                        break;
                }

     
            }

            if (currentPart != string.Empty)
                retVal.Add(currentPart);

            return retVal;
        }


        
    }
}
