using GlobalPayments.Api.Terminals;
using GlobalPayments.Api.Terminals.Ingenico;
using GlobalPayments.Api.Terminals.Ingenico.Requests;
using GlobalPaymentsTestApp.Entities.Response;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;

namespace GlobalPaymentsTestApp.Control {
    public partial class PATModeControl : UserControl {

        private XMLResponseControl _xmlResponseControl;
        private ConfirmationResponseControl _confirmationResponseControl;
        private List<PATRequest> _patRequestList;
        private PATResponse _response;
        private decimal? _amount;
        private bool _isTableUnlockShown;

        private bool _isRequestTypeXML;
        private int? _prevDeviceCount;
        private string _waiterId;
        private string _tableId;
        private string _terminalId;
        private string _terminalCurrency;

        string raw = string.Empty;
        IDeviceInterface _device;

        public bool _isControlOpen;
        private bool _isWaitingForResponse;

        public PATModeControl(IDeviceInterface device) {
            InitializeComponent();
            CloseControl();

            _patRequestList = new List<PATRequest>();
            _response = new PATResponse();

            _isRequestTypeXML = false;
            _isControlOpen = false;
            _isWaitingForResponse = false;
            _prevDeviceCount = 0;

            _device = device;
            _isTableUnlockShown = false;

            AnalyzeConnectedDevices();
        }

        #region Threads
        private void AnalyzeConnectedDevices() {
            new Thread(() => {
                while(_device != null) {
                    try {
                        Thread.Sleep(100);

                        if (_prevDeviceCount != _device.ConnectedDevicesCount()) {
                            _prevDeviceCount = _device.ConnectedDevicesCount();

                            for (int i = 0; i < _patRequestList.ToList().Count(); i++) {
                                PATRequest pat = _patRequestList[i];
                                if (!pat.Client.Connected) {
                                    CloseControl();
                                    Invoker.RemoveListViewItemAt(listViewPATRequest, i);
                                    _patRequestList.RemoveAt(i);
                                    if (i == 0)
                                        _isWaitingForResponse = false;
                                    UpdateControl();
                                }
                            }
                        }
                    } catch(Exception) {
                        //eat exception
                    }
                }
            }).Start();
        }
        #endregion

        #region Event Listeners
        public void DeviceOnPayAtTableRequest(PATRequest request) {
            raw += "Received: \r\n" + request.ToString() + "\r\n\r\n";
            Invoker.SetRichTextBox(txtRawMessage, raw);

            AddPATRequest(request);
        }

        public void DeviceOnMessageSent(string message) {
            raw += "Sent: \r\n" + message + "\r\n\r\n";
            Invoker.SetRichTextBox(txtRawMessage, raw);
        }

        public void DeviceOnBroadcastMessage(string code, string message) {
            raw += "Broadcast: \r\n" + code + " | " + message + "\r\n\r\n";
            Invoker.SetRichTextBox(txtRawMessage, raw);
        }

        private void AddPATRequest(PATRequest request) {
            try {
                TcpClient client = request.Client;
                string ip = string.Empty;
                var item = new ListViewItem();

                if (client.Connected)
                    ip = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
                else
                    ip = "Disconnected client.";

                for (int i = 0; i < _patRequestList.ToList().Count; i++) {
                    TcpClient c = _patRequestList[i].Client;
                    if (((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString() == ((IPEndPoint)c.Client.RemoteEndPoint).Address.ToString()) {
                        if (Invoker.IsListViewItemSelectedAt(listViewPATRequest, i)) {
                            CloseControl();
                            _isWaitingForResponse = false;
                        }

                        item = new ListViewItem(ip);
                        item.SubItems.Add(request.RequestType.ToString());

                        if(i == 0) {
                            request.WaiterId = _waiterId;
                            request.TableId = _tableId;
                            request.TerminalId = _terminalId;
                            request.TerminalCurrency = _terminalCurrency;
                        }

                        _patRequestList[i] = request;
                        Invoker.ReplaceListViewItemAt(listViewPATRequest, item, i);

                        Invoker.SelectListViewItem(listViewPATRequest, 0);

                        UpdateControl();
                        return;
                    }
                }

                item = new ListViewItem(ip);
                item.SubItems.Add(request.RequestType.ToString());

                _patRequestList.Add(request);
                Invoker.SetListViewItem(listViewPATRequest, item);

                Invoker.SelectListViewItem(listViewPATRequest, 0);

                UpdateControl();

            } catch (Exception e) {
                MessageBox.Show(e.Message);
            }
        }
        #endregion

        #region Controls
        private void OpenControl(UserControl control) {
            Invoker.SetGroupBoxVisibility(grpControl, true);
            Invoker.SetGroupBoxControl(grpControl, null);
            Invoker.SetGroupBoxControl(grpControl, control);
        }

        public void CloseControl() {
            Invoker.SetGroupBoxVisibility(grpControl, false);
            Invoker.SetGroupBoxControl(grpControl, null);
        }

        private void UpdateControl() {
            if (!_isControlOpen) {
                if(!_isWaitingForResponse) {
                    if (_patRequestList.Count > 0) {
                        Invoker.SelectListViewItem(listViewPATRequest, 0);
                        PATRequestType requestType = _patRequestList.ElementAt(0).RequestType;
                        _waiterId = _patRequestList.ElementAt(0).WaiterId;
                        _tableId = _patRequestList.ElementAt(0).TableId;
                        _terminalId = _patRequestList.ElementAt(0).TerminalId; 
                        _terminalCurrency = _patRequestList.ElementAt(0).TerminalCurrency;

                        if (requestType.Equals(PATRequestType.TableUnlock)) {
                            if (!_isTableUnlockShown) {
                                _isTableUnlockShown = true;
                                new Thread(() => {
                                    while (true) {
                                        try {
                                            if (!_patRequestList.ElementAt(0).Client.Connected) {
                                                DialogResult dialogResult = MessageBox.Show("Table unlock request was sent by the terminal \r\nTerminal is now on idle mode.", "Table Unlock");
                                                if (dialogResult == DialogResult.OK) {
                                                    _isTableUnlockShown = false;
                                                    _amount = null;
                                                    break;
                                                }
                                            }
                                        } catch (Exception) {
                                            break;
                                        }
                                    }
                                }).Start();
                            }
                            return;
                        }

                        switch (requestType) {
                            case PATRequestType.TableReceipt:
                            case PATRequestType.TableList:
                            case PATRequestType.AdditionalMessage:
                                _xmlResponseControl = new XMLResponseControl(_patRequestList.ElementAt(0));
                                Invoker.SetButton(btnPositive, true);
                                Invoker.SetButton(btnNegative, false);
                                OpenControl(_xmlResponseControl);

                                _isRequestTypeXML = true;
                                break;

                            case PATRequestType.TableLock:
                            case PATRequestType.TransactionOutcome:
                            case PATRequestType.TransferData:
                            case PATRequestType.SplitSaleReport:
                            case PATRequestType.Ticket:
                            case PATRequestType.EndOfDayReport:
                                _confirmationResponseControl = new ConfirmationResponseControl(_patRequestList.ElementAt(0), _amount);
                                Invoker.SetButton(btnPositive, true);
                                Invoker.SetButton(btnNegative, true);

                                OpenControl(_confirmationResponseControl);

                                _isRequestTypeXML = false;
                                break;

                            default:
                                MessageBox.Show("Request type sent by the terminal can't recognized");
                                break;
                        }
                    }
                }
            }
        }

        private void grpControl_ControlRemoved(object sender, ControlEventArgs e) {
            _isControlOpen = false;
        }

        private void grpControl_ControlAdded(object sender, ControlEventArgs e) {
            _isControlOpen = true;
        }
        #endregion

        #region Buttons
        private void btnClear_Click(object sender, EventArgs e) {
            raw = string.Empty;
            Invoker.SetRichTextBox(txtRawMessage, raw);
        }

        private void btnPositive_Click(object sender, EventArgs e) {
            if (_isControlOpen) {
                try {
                    TcpClient tcpClient = _patRequestList[0].Client;

                    if (!tcpClient.Connected)
                        throw new Exception("The client is disconnected.");

                    if (_response != null) {
                        if (_isRequestTypeXML) {
                            _response = _xmlResponseControl.GetPATResponse();

                            _device.PayAtTableResponse(tcpClient)
                                .WithXMLPath(_response.XMLPath)
                                .Execute();
                        } else {
                            _response = _confirmationResponseControl.GetPATResponse();
                            _amount = _response.Amount;

                            _response.PATResponseType = PATResponseType.CONF_OK;

                            _device.PayAtTableResponse(tcpClient)
                                .WithPayAtTableResponseType(_response.PATResponseType)
                                .WithAmount(_response.Amount)
                                .WithPaymentMode(_response.PATPaymentMode)
                                .WithCurrencyCode(_response.CurrencyCode)
                                .Execute();
                        }

                        _isWaitingForResponse = true;
                        CloseControl();
                    }
                } catch (Exception ex) {
                    MessageBox.Show(ex.Message, "Error");
                }
            }
        }

        private void btnNegative_Click(object sender, EventArgs e) {
            if (_isControlOpen) {
                try {
                    TcpClient tcpClient = _patRequestList[0].Client;

                    if (!tcpClient.Connected)
                        throw new Exception("The client is disconnected.");

                    if (_response != null) {
                        if (_isRequestTypeXML) {
                            _response = _xmlResponseControl.GetPATResponse();

                            _device.PayAtTableResponse(tcpClient)
                                .WithXMLPath(_response.XMLPath)
                                .Execute();
                        } else {
                            _response = _confirmationResponseControl.GetPATResponse();
                            _response.PATResponseType = PATResponseType.CONF_NOK;

                            _device.PayAtTableResponse(tcpClient)
                                .WithPayAtTableResponseType(_response.PATResponseType)
                                .WithAmount(_response.Amount)
                                .WithPaymentMode(_response.PATPaymentMode)
                                .WithCurrencyCode(_response.CurrencyCode)
                                .Execute();
                        }

                        CloseControl();
                    }
                } catch (Exception ex) {
                    MessageBox.Show(ex.Message, "Error");
                }
            }
        }
        #endregion
    }
}
