using System;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
using Ozeki.VoIP;
using Ozeki.Media;


namespace Callback_Form
{
    public partial class FormCallback : Form
    {
        private const bool OperatorCall = false;
        private const bool CustomerCall = true;
        private ISoftPhone softPhone;
        private IPhoneLine phoneLine;
        private RegState phoneLineInformation;
        private IPhoneCall calloperator;
        private IPhoneCall callcustumer;
        private Timer countDownTimer;

        private PhoneCallAudioSender operatorSender = new PhoneCallAudioSender();
        private PhoneCallAudioReceiver operatorReceiver = new PhoneCallAudioReceiver();
        private PhoneCallAudioSender customerSender = new PhoneCallAudioSender();
        private PhoneCallAudioReceiver customerReceiver = new PhoneCallAudioReceiver();
        private MediaConnector connector = new MediaConnector();

        public FormCallback()
        {
            InitializeComponent();
            statusbarLabelOzeki.Text = string.Format("© Copyright {0}, Ozeki Informatics Ltd.", DateTime.Now.Year);
            dateTimePicker1.MinDate = DateTime.Now;
        }

        private void FormCallback_Load(object sender, EventArgs e)
        {
            InitializeSoftPhone();
        }

        #region Helper Functions
        /// <summary>
        ///It initializes a softphone object  with a SIP BPX, and it  is for requisiting a SIP account that is nedded  for a SIP PBX service. It registers this SIP
        ///account to the SIP PBX through an ’IphoneLine’ object  which represents the telephoneline. 
        ///If the telephone registration is successful we have a call ready softphone. In this example the softphone can be reached by dialing the number 891.
        /// </summary>
        private void InitializeSoftPhone()
        {
            try
            {
                var account = new SIPAccount(true, "oz875", "oz875", "oz875", "oz875", "192.168.115.103", 5060);
                softPhone = SoftPhoneFactory.CreateSoftPhone(SoftPhoneFactory.GetLocalIP(), 5700, 5750);
                softPhone.IncomingCall += new EventHandler<VoIPEventArgs<IPhoneCall>>(softPhone_IncomingCall);
                phoneLine = softPhone.CreatePhoneLine(account);
                phoneLine.RegistrationStateChanged += phoneLine_PhoneLineInformation;

                softPhone.RegisterPhoneLine(phoneLine);

                connector.Connect(operatorReceiver, customerSender);
                connector.Connect(customerReceiver, operatorSender);
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("You didn't give your local IP adress, so the program won't run properly.\n {0}", ex.Message), string.Empty, MessageBoxButtons.OK,
                MessageBoxIcon.Error);
				
                var sb = new StringBuilder();
                sb.AppendLine("Some error happened.");
                sb.AppendLine();
                sb.AppendLine("Exception:");
                sb.AppendLine(ex.Message);
                sb.AppendLine();
                if(ex.InnerException != null)
                {
                    sb.AppendLine("Inner Exception:");
                    sb.AppendLine(ex.InnerException.Message);
                    sb.AppendLine();
                }
                sb.AppendLine("StackTrace:");
                sb.AppendLine(ex.StackTrace);

                MessageBox.Show(sb.ToString());				
            }
        }

        /// <summary>
        ///  It signs up to the necessary events of a call transact.
        /// </summary>
        private void WireUpCallEvents(IPhoneCall call)
        {
            call.CallStateChanged += (call_CallStateChanged);
            call.DtmfReceived += (call_DtmfReceived);
        }

        /// <summary>
        /// It starts a call to an operator or an customer.
        /// </summary>
        /// <param name="IsCustomerCall">direction of the call</param>
        private void BeginCall(bool IsCustomerCall)
        {
            if (IsCustomerCall && !String.IsNullOrEmpty(textBoxCallbackPhone.Text))
            {
                callcustumer = softPhone.CreateCallObject(phoneLine, textBoxCallbackPhone.Text);
                WireUpCallEvents(callcustumer);
                InvokeGUIThread(() =>
                {
                    listBoxEvents.Items.Add(String.Format("{0} call starts.", callcustumer));
                });

                customerReceiver.AttachToCall(callcustumer);
                customerSender.AttachToCall(callcustumer);

                callcustumer.Start();
            }
            else
            {
                if (string.IsNullOrEmpty(textBoxOperator.Text))
                    return;

                if (phoneLineInformation != RegState.RegistrationSucceeded)
                {
                    MessageBox.Show("Phone line state is not valid!");
                    return;
                }

                calloperator = softPhone.CreateCallObject(phoneLine, textBoxOperator.Text);
                WireUpCallEvents(calloperator);
                InvokeGUIThread(() =>
                {
                    listBoxEvents.Items.Add(String.Format("{0} call starts.", calloperator));
                });

                operatorSender.AttachToCall(calloperator);
                operatorReceiver.AttachToCall(calloperator);

                calloperator.Start();
            }
        }

        /// <summary>
        /// It stops a call.
        /// </summary>
        /// <param name="call"></param>
        private void HangUpCall(IPhoneCall call)
        {
            if (call==calloperator)
            {
                if (callcustumer!=null)
                {
                    callcustumer.HangUp();
                }
                operatorSender.Detach();
                operatorReceiver.Detach();
            }
            else
            {
                if (calloperator!=null)
                {
                    calloperator.HangUp();
                }
                customerSender.Detach();
                customerReceiver.Detach();
            }

            InvokeGUIThread(() => { ControlsEnable(); });
        }

        /// <summary>
        /// The controls of the Windows Form applications can only be modified on the GUI thread. This method grants access to the GUI thread.
        /// </summary>
        /// <param name="action"></param>
        private void InvokeGUIThread(Action action)
        {
            Invoke(action);
        }
 
        private void ControlsDisable()
        {
            textBoxOperator.Enabled = false;
            textBoxCallbackPhone.Enabled = false;
            buttonAdd.Enabled = false;
            dateTimePicker1.Enabled = false;
            buttonCancel.Enabled = true;
            panelCountDown.Visible = true;
        }

        private void ControlsEnable()
        {
            textBoxOperator.Enabled = true;
            textBoxCallbackPhone.Enabled = true;
            buttonAdd.Enabled = true;
            dateTimePicker1.Enabled = true;
            panelCountDown.Visible = false;
            buttonCancel.Enabled = false;
        }

        /// <summary>
        /// Reset countDown timer.
        /// </summary>
        private void TimerReset()
        {
            if (countDownTimer != null)
            {
                countDownTimer.Stop();
                countDownTimer.Tick -= countDownTimer_Tick;
                countDownTimer = null;
            }
            ControlsEnable();
        }

        /// <summary>
        /// It occurs on timer ticks.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void countDownTimer_Tick(object sender, EventArgs e)
        {
            labelCountdownValue.Text = (dateTimePicker1.Value - DateTime.Now).ToString();
            if (DateTime.Now >= dateTimePicker1.Value)
            {
                labelCountdownValue.Text = "00:00:00";
                listBoxEvents.Items.Add("Start calling.");
                TimerReset();
                BeginCall(OperatorCall);
            }
        }

        #endregion

        #region Ozeki VoIP-SIP SDK's events

        /// <summary>
        /// Occurs when phone line state has changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void phoneLine_PhoneLineInformation(object sender, RegistrationStateChangedArgs e)
        {
            phoneLineInformation = e.State;
            InvokeGUIThread(() =>
            {
                labelIdentifier.Text = ((IPhoneLine)sender).SIPAccount.RegisterName;
                if (e.State == RegState.RegistrationSucceeded)
                {
                    labelRegStatus.Text = "Online";
                    labelCallStateInfo.Text = "Registration succeeded";
                }
                else
                    labelCallStateInfo.Text = e.State.ToString();
            });
        }

        /// <summary>
        /// Occurs when an incoming call request has received.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void softPhone_IncomingCall(object sender, VoIPEventArgs<IPhoneCall> e)
        {
            e.Item.Reject();
        }

        /// <summary>
        /// Occurs when the phone call state has changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void call_CallStateChanged(object sender, CallStateChangedArgs e)
        {
            InvokeGUIThread(() => 
            {
                labelCallStateInfo.Text = e.State.ToString();
                listBoxEvents.Items.Add(String.Format("{0} call statechanged: {1}",sender,e.State.ToString()));
            });

            switch (e.State)
            {
                case CallState.Answered:                   
                    if ((IPhoneCall)sender==calloperator)
                    {
                        BeginCall(CustomerCall);
                    }
                    break;
                case CallState.Completed:
                case CallState.Cancelled:
                    HangUpCall((IPhoneCall)sender);
                    calloperator = null;
                    callcustumer = null;
                    break;
            }
        }        

        /// <summary>
        /// Displays DTMF signals
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void call_DtmfReceived(object sender, VoIPEventArgs<DtmfInfo> e)
        {
            DtmfInfo dtmfInfo = e.Item;

            InvokeGUIThread(() =>
            {
                labelCallStateInfo.Text = String.Format("DTMF signal received: {0} ", dtmfInfo.Signal.Signal);
                listBoxEvents.Items.Add(String.Format("{0} sended the following DTMF sign: {1}", sender, dtmfInfo.Signal.Signal));
            });
        }

        #endregion

        #region Buttons' events

        private void buttonAdd_Click(object sender, EventArgs e)
        {
            if (countDownTimer == null && !String.IsNullOrEmpty(textBoxOperator.Text) && !String.IsNullOrEmpty(textBoxCallbackPhone.Text))
            {
                if (dateTimePicker1.Value < DateTime.Now)
                {
                    MessageBox.Show("You have given a time, what is past.");
                    return;
                }
                countDownTimer = new Timer();
                countDownTimer.Tick += new EventHandler(countDownTimer_Tick);
                countDownTimer.Interval = 1000;
                countDownTimer.Start();
                labelCountdownValue.Text = (dateTimePicker1.Value - DateTime.Now).ToString();
                ControlsDisable();
            }
            else
            {
                MessageBox.Show("You didn't fill all required field.");
            }
        }

        private void buttonCancel_Click(object sender, EventArgs e)
        {
            TimerReset();
        }

        #endregion

        #region Menu events

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void openOnlineDocumentationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                Process.Start("http://www.voip-sip-sdk.com/p_159-how-to-create-a-callback-form-using-the-ozeki-c-sip-voip-softphone-source-voip.html");
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FormAbout about = new FormAbout();
            about.ShowDialog(this);
        }

        #endregion

    }
}
