// Copyright © 2015 Genesys. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Genesyslab.Platform.Commons.Collections;
using Tomers.WPF.Localization;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using Genesyslab.Desktop.Infrastructure.DependencyInjection;
using Genesyslab.Platform.Commons.Logging;
using Genesyslab.Desktop.Modules.Gms.CallbackInvitation.Generic;
using System.Windows.Threading;
using System.Runtime.Serialization;
using System.Web.Script.Serialization;
using System.Collections;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace Genesyslab.Desktop.Modules.Gms.CallbackInvitation.CallbackDisposition
{
    /// <summary>
    /// Interaction logic for DispositionDialog.xaml
    /// </summary>
    public partial class DispositionDialog : Window
    {
        private readonly KeyValueCollection displayData = null;
        private readonly KeyValueCollection responseOptions = null;
        private readonly KeyValueCollection configOptions = null;
        private readonly int dispositionTimeout;
        private readonly IObjectContainer container = null;
        private readonly ILogger log = null;

        private const int displayDataRowHeight = 24;
        private const String displayDataTranslationPrefix = "DisplayData_";
        private const String responseButtonTranslationPrefix = "ResponseButton_";
        private const String responseButtonPrefix = "responseButton_";

        private const String completedDispositionsKey = "completed_dispositions";
        private const String customerNumberKey = "customer_number";
        private const String businessHoursURLKey = "business_hours_url";

        private const String responseOptionsDoneKey = "done";
        private const String responseOptionsDoneButtonKey = "button";
        private const String responseOptionsDoneURLKey = "url";
        private const String responseOptionsDoneOptionsKey = "options";

        DispatcherTimer _timer;
        TimeSpan _time;

        // disable close window button
        private const int GWL_STYLE = -16;
        private const int WS_SYSMENU = 0x00080000;

        [DllImport("user32.dll")]
        private extern static int SetWindowLong(IntPtr hwnd, int index, int value);
        [DllImport("user32.dll")]
        static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        const UInt32 SWP_NOSIZE = 0x0001;
        const UInt32 SWP_NOMOVE = 0x0002;
        const UInt32 SWP_SHOWWINDOW = 0x0040;

        [DllImport("user32.dll")]
        private extern static int GetWindowLong(IntPtr hwnd, int index);

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            var hwnd = new WindowInteropHelper(this).Handle;
            SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
            SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
        }

        private IDictionary<string, string> dictLocalDtToUTC = new Dictionary<string, string>();

        public DispositionDialog(IObjectContainer container, KeyValueCollection displayData, KeyValueCollection responseOptions, KeyValueCollection configOptions, int dispositionTimeout)
        {
            this.container = container;
            this.log = this.container.Resolve<ILogger>().CreateChildLogger("DispositionDialog");
            this.displayData = displayData;
            this.responseOptions = responseOptions;
            this.configOptions = configOptions;
            this.dispositionTimeout = dispositionTimeout;

            InitializeComponent();

            this.Title = String.Format("{0} ({1} {2})",
                    LanguageDictionary.Current.Translate<String>("DispositionDialog.FormCaption", "Title"),
                    this.dispositionTimeout.ToString(), LanguageDictionary.Current.Translate<String>("DispositionDialog.RemainingTime", "Title"));

            // Set form height depending on number of display data items
            if (this.displayData != null)
            {
                this.Height = 246 + (this.displayData.Count * displayDataRowHeight);
            }

            SetupTimer();
            PlaceDisplayData();
            PlaceResponseButtons();
        }

        private void SetupTimer()
        {
            _time = TimeSpan.FromSeconds(this.dispositionTimeout);
            _timer = new DispatcherTimer(new TimeSpan(0, 0, 1), DispatcherPriority.Normal, delegate
            {
                // Set the form title with the remaining time
                this.Title = String.Format("{0} ({1} {2})",
                        LanguageDictionary.Current.Translate<String>("DispositionDialog.FormCaption", "Title"),
                        _time.Seconds.ToString(), LanguageDictionary.Current.Translate<String>("DispositionDialog.RemainingTime", "Title"));

                if (_time == TimeSpan.Zero)
                {
                    _timer.Stop();
                    this.Close();
                }
                _time = _time.Add(TimeSpan.FromSeconds(-1));
            }, Application.Current.Dispatcher);

            _timer.Start();
        }

        private void PlaceDisplayData()
        {
            if (this.displayData != null)
            {
                // place display

                // Completed, reason, disposition codes
                IDictionaryEnumerator enumDictData = displayData.GetEnumerator();
                while (enumDictData.MoveNext())
                {
                    if (enumDictData.Key.Equals(completedDispositionsKey))
                    {
                        string [] split = ((string)enumDictData.Value).Split(new Char[] {','});
                        foreach (string item in split) {
                            comboBoxDispositions.Items.Add(item);
                        }
                    }
                }
                comboBoxDispositions.SelectedIndex = 0;

                // customer number
                string stringCustomerNumber = displayData.GetAsString(customerNumberKey);
                textBoxCustomerNumber.Text = stringCustomerNumber;

                dictLocalDtToUTC.Clear();

                // retry later, calender with business hours
                // get URL
                string stringBusinessHoursURL = displayData.GetAsString(businessHoursURLKey);
                // add desired time in config option to be replaced with the value in URL
                string desiredTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
                //string desiredTime = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ");
                configOptions.Add("desired_time", desiredTime);
                string url = replaceMatchVariable(stringBusinessHoursURL);
                // get values, http request
                Dictionary<string, Object> officeHours = getOfficeHoursValues(url); // request could be sent in another thread to be able to display the window
                enumDictData = officeHours.GetEnumerator();
                while (enumDictData.MoveNext())
                {
                    // convert UTC time to Local time
                    DateTime convertedDate = DateTime.Parse((string)enumDictData.Key);
                    DateTime localDate = convertedDate.ToLocalTime();
                    string localDtStr = localDate.ToString("yyyy-MM-ddTHH:mm:ssZ");
                    dictLocalDtToUTC.Add(localDtStr, (string)enumDictData.Key);
                    //comboBoxDateTimeCalendar.Items.Add(enumDictData.Key);
                    comboBoxDateTimeCalendar.Items.Add(localDtStr);
                }
                if (comboBoxDateTimeCalendar.Items.Count > 0)
                {
                    comboBoxDateTimeCalendar.SelectedIndex = 0;
                }
 
            }
        }

        private void PlaceResponseButtons()
        {
            if (this.responseOptions != null)
            {
                String[] allKeys = this.responseOptions.AllKeys;
                for (int idx = 0; idx < this.responseOptions.Count; idx++)
                {
                    ColumnDefinition newColumn = new ColumnDefinition();
                    ResponseButtonGrid.ColumnDefinitions.Add(newColumn);

                    String systemCaption = this.responseOptions.GetAsKeyValueCollection(allKeys[idx]).GetAsString("button");

                    // Insert a response button
                    Button newResponseButton = new Button();
                    newResponseButton.Name = responseButtonPrefix + idx.ToString();
                    newResponseButton.Margin = new Thickness(4, 4, 4, 4);
                    newResponseButton.FontWeight = FontWeights.Bold;
                    newResponseButton.Content = LanguageDictionary.Current.Translate<String>(responseButtonTranslationPrefix + systemCaption, "Translation");
                    newResponseButton.Click += new RoutedEventHandler(ResponseButtonClick);
                    ResponseButtonGrid.Children.Add(newResponseButton);

                    // Position the button in the correct grid row and column
                    Grid.SetRow(newResponseButton, 0);
                    Grid.SetColumn(newResponseButton, idx);
                }
            }
        }

        private Dictionary<string, Object> getOfficeHoursValues(string url)
        {
            // method GET, returns JSON value
            this.log.Debug(String.Format("{0} Executing RESTful URL to Genesys Mobile Server: {1}", GConst.LOG_PREFIX, url));

            Dictionary<string, Object> result = new Dictionary<string, Object>();
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                //request.Method = "GET"; // default is GET

                WebResponse webResponse = request.GetResponse();
                Stream webStream = webResponse.GetResponseStream();
                StreamReader responseReader = new StreamReader(webStream);
                string response = responseReader.ReadToEnd();
                responseReader.Close();
                this.log.Debug(String.Format("{0} Response from Genesys Mobile Server: {1}", GConst.LOG_PREFIX, response));

                // JSON deserialize
                var serializer = new JavaScriptSerializer();
                var deserializedResult = serializer.Deserialize<Dictionary<string, Object>>(response);
                result = deserializedResult;
            }
            catch (Exception restexception)
            {
                MessageBox.Show(restexception.Message);
                this.log.Debug(String.Format("{0} Exception occurred while contacting Orchestration Server: {1}", GConst.LOG_PREFIX, restexception.Message));
            }
            return result;
        }

        private void radioButtonCompleted_Checked(object sender, RoutedEventArgs e)
        {
            // enabled
            labelReason.IsEnabled = true;
            comboBoxDispositions.IsEnabled = true;

            // disabled
            labelCustomerNumber.IsEnabled = false;
            textBoxCustomerNumber.IsEnabled = false;
            labelDateTime.IsEnabled = false;
            comboBoxDateTimeCalendar.IsEnabled = false;
        }

        private void radioButtonRetryNow_Checked(object sender, RoutedEventArgs e)
        {
            // enabled
            labelCustomerNumber.IsEnabled = true;
            textBoxCustomerNumber.IsEnabled = true;

            // disabled
            labelReason.IsEnabled = false;
            comboBoxDispositions.IsEnabled = false;
            labelDateTime.IsEnabled = false;
            comboBoxDateTimeCalendar.IsEnabled = false;
        }

        private void radioButtonRetryLater_Checked(object sender, RoutedEventArgs e)
        {
            // enabled
            labelDateTime.IsEnabled = true;
            comboBoxDateTimeCalendar.IsEnabled = true;

            // disabled
            labelReason.IsEnabled = false;
            comboBoxDispositions.IsEnabled = false;
            labelCustomerNumber.IsEnabled = false;
            textBoxCustomerNumber.IsEnabled = false;
        }

        private string replaceMatchVariable(string inputText)
        {
            // replace all $variable$ keys using config options
            Match matchVariableIndex = Regex.Match(inputText, @"\$(\w+)\$");

            while (matchVariableIndex.Success)
            {
                // one group, get group[1]
                Group g = matchVariableIndex.Groups[1];
                string variableToReplace = g.Value;
                string replacementValue = configOptions.GetAsString(variableToReplace);
                if (replacementValue != null)
                {
                    inputText = inputText.Replace("$" + variableToReplace + "$", replacementValue);
                }
                else
                {
                    // c'est fâcheux
                    this.log.Debug(String.Format("{0} No value replacement for variable: {1}", GConst.LOG_PREFIX, variableToReplace));
                }
                matchVariableIndex = matchVariableIndex.NextMatch();
            }
            this.log.Debug(String.Format("{0} Formatted URL for Done POST request is : {1}", GConst.LOG_PREFIX, inputText));
            return inputText;
        }

        private void ResponseButtonClick(object sender, RoutedEventArgs e)
        {
            Button clicked = (Button)sender;
            Match matchButtonIndex = Regex.Match(clicked.Name, "^" + responseButtonPrefix + @"(?<ButtonIndex>\d+)$");

            if (matchButtonIndex.Success)
            {
                this.log.Debug(String.Format("{0} Button pressed: {1}", GConst.LOG_PREFIX, clicked.Name));
                string[] allKeys = this.responseOptions.AllKeys;
                String URL = this.responseOptions.GetAsKeyValueCollection(allKeys[Int16.Parse(matchButtonIndex.Groups["ButtonIndex"].ToString())]).GetAsString("url");
                this.log.Debug(String.Format("{0} Executing RESTful URL to Orchestration Server: {1}", GConst.LOG_PREFIX, URL));

                string json = "";
                // get info
                if (radioButtonCompleted.IsChecked.Value)
                {
                    this.log.Debug(String.Format("{0} Radio Button used is : {1}", GConst.LOG_PREFIX, radioButtonCompleted.Content));
                    // create request with dialog parameters
                    //string returnedOption = "completed";
                    string returnedValue = (string)comboBoxDispositions.SelectedValue;  // disposition text
                    json = "{\"disposition\":\"completed\",\"reason\":\"" + returnedValue + "\"}";
                }
                if (radioButtonRetryNow.IsChecked.Value)
                {
                    this.log.Debug(String.Format("{0} Radio Button used is : {1}", GConst.LOG_PREFIX, radioButtonRetryNow.Content));
                    //string returnedOption = "retry_now";
                    string returnedValue = (string)textBoxCustomerNumber.Text;  // customer number
                    json = "{\"disposition\":\"retry_now\",\"customer_number\":\"" + returnedValue + "\"}";
                }
                if (radioButtonRetryLater.IsChecked.Value)
                {
                    this.log.Debug(String.Format("{0} Radio Button used is : {1}", GConst.LOG_PREFIX, radioButtonRetryLater.Content));
                    //string returnedOption = "retry_later";
                    string returnedValue = (string)comboBoxDateTimeCalendar.SelectedValue; // date/time local time
                    string utcDesiredTimeStr = dictLocalDtToUTC[returnedValue];
                    string customerNumber = (string)textBoxCustomerNumber.Text; // customer number
                    json = "{\"disposition\":\"retry_later\",\"desired_time\":\"" + utcDesiredTimeStr + "\",\"customer_number\":\"" + customerNumber + "\"}";
                }
                // if json is not empty then post
                if (json.Length > 0)
                {
                    // call ORS via GMS url
                    try
                    {
                        this.log.Debug(String.Format("{0} Executing RESTful URL to Orchestration Server: payload {1}", GConst.LOG_PREFIX, json));

                        // replace all $variable$ keys using config options
                        string URLText = replaceMatchVariable(URL);

                        byte[] messageJson = Encoding.UTF8.GetBytes(json);

                        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URLText);
                        request.Method = "POST";
                        request.ContentType = "application/json";
                        request.ContentLength = messageJson.Length; //Encoding.UTF8.GetByteCount(messageJson);
                        using (Stream requestStream = request.GetRequestStream())
                        {
                            requestStream.Write(messageJson, 0, messageJson.Length);
                        }

                        WebResponse webResponse = request.GetResponse();
                        Stream webStream = webResponse.GetResponseStream();
                        StreamReader responseReader = new StreamReader(webStream);
                        string response = responseReader.ReadToEnd();
                        responseReader.Close();

                        this.log.Debug(String.Format("{0} Response from Orchestration Server: {1}", GConst.LOG_PREFIX, response));
                        this.DialogResult = true;
                        this.Close();
                    }
                    catch (Exception restexception)
                    {
                        MessageBox.Show(restexception.Message);
                        this.log.Debug(String.Format("{0} Exception occurred while contacting Orchestration Server: {1}", GConst.LOG_PREFIX, restexception.Message));
                        this.DialogResult = false;
                        this.Close();
                    }
                }
                else
                {
                    // select a radio button
                    MessageBox.Show(" Choice disposition feedback.");
                    this.log.Debug(String.Format("{0} Choice disposition feedback.", GConst.LOG_PREFIX));
                }
            }
        }
    }
}
