// 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.InteropServices;
using System.Windows.Interop;

namespace Genesyslab.Desktop.Modules.Gms.CallbackInvitation.CallbackInvitation {
    /// <summary>
    /// Interaction logic for InvitationDialog.xaml
    /// </summary>
    public partial class InvitationDialog : Window {
        private readonly KeyValueCollection displayData = null;
        private readonly KeyValueCollection responseOptions = null;
        private readonly int invitationTimeout;
        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_";

        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);
        }

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

            InitializeComponent();

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

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

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

        private void SetupTimer() {
            _time = TimeSpan.FromSeconds(this.invitationTimeout);
            _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>("InvitationDialog.FormCaption", "Title"),
                            _time.Seconds.ToString(), LanguageDictionary.Current.Translate<String>("InvitationDialog.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) {
                DisplayDataGrid.Height = (this.displayData.Count * displayDataRowHeight);

                String[] allKeys = this.displayData.AllKeys;
                for (int idx = 0; idx < this.displayData.Count; idx++) {
                    RowDefinition newRow = new RowDefinition();
                    newRow.Height = new GridLength(displayDataRowHeight);
                    DisplayDataGrid.RowDefinitions.Add(newRow);

                    // Insert a caption label
                    Label newDisplayKey = new Label();
                    newDisplayKey.Name = "displayKey_" + idx.ToString();
                    newDisplayKey.Margin = new Thickness(0, 0, 0, 0);
                    newDisplayKey.Content = LanguageDictionary.Current.Translate<String>(displayDataTranslationPrefix + allKeys[idx], "Translation");
                    newDisplayKey.FontWeight = FontWeights.Bold;
                    DisplayDataGrid.Children.Add(newDisplayKey);

                    // Insert a data label
                    Label newDisplayValue = new Label();
                    newDisplayValue.Name = "displayValue_" + idx.ToString();
                    newDisplayValue.Margin = new Thickness(0, 0, 0, 0);
                    newDisplayValue.Content = this.displayData[allKeys[idx]];
                    DisplayDataGrid.Children.Add(newDisplayValue);

                    // Position the labels in the correct grid row and column
                    Grid.SetRow(newDisplayKey, idx);
                    Grid.SetColumn(newDisplayKey, 0);
                    Grid.SetRow(newDisplayValue, idx);
                    Grid.SetColumn(newDisplayValue, 1);
                }
            }
        }

        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 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));

                try {
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
                    request.Method = "POST";
                    request.ContentType = "application/json";
                    request.ContentLength = 0;
                    StreamWriter requestWriter = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII);
                    requestWriter.Write("");
                    requestWriter.Close();

                    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();
                }
            }
        }
    }
}
