using GlobalPayments.Api.Entities;
using GlobalPayments.Api.Services;
using GlobalPayments.Api.Terminals;
using GlobalPayments.Api.Terminals.Ingenico.Requests;
using GlobalPayments.Api.Utils;
using GlobalPaymentsTestApp.Control;
using System;
using System.Drawing;
using System.IO.Ports;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;

namespace GlobalPaymentsTestApp {
    public partial class Dashboard : Form {

        private StandardModeControl _standardModeControl;
        private PATModeControl _patModeControl;

        private int _timeout;
        private bool _isListening;

        private bool _isSerial;
        private bool _isTcpIp;

        private IDeviceInterface _device;

        public Dashboard() {
            InitializeComponent();
            SetDefaultValues();
            CloseControl();
            UpdateConnectionStatus();
        }

        #region Form
        private void Dashboard_FormClosed(object sender, FormClosedEventArgs e) {
            if (_device != null)
                _device.Dispose();

            _device = null;

            Application.ExitThread();
            System.Environment.Exit(System.Environment.ExitCode);
        }

        private void txtTcpTimeout_KeyPress(object sender, KeyPressEventArgs e) {
            if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar)) {
                e.Handled = true;
            }
        }

        private void txtIPPort_KeyPress(object sender, KeyPressEventArgs e) {
            if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar)) {
                e.Handled = true;
            }
        }

        private void tabControlCommMode_SelectedIndexChanged(object sender, EventArgs e) {
            if(_device == null) {
                if (tabControlCommMode.SelectedIndex == 0) {
                    SetSerialFields(true);
                    SetTcpIpFields(false);
                    Invoker.SetCheckBoxChecked(chkBroadcastMessage, false);
                    Invoker.SetCheckBoxChecked(chkPATT, false);
                } else {
                    SetSerialFields(false);
                    SetTcpIpFields(true);
                    Invoker.SetCheckBoxChecked(chkBroadcastMessage, false);
                    Invoker.SetCheckBoxChecked(chkPATT, false);
                }
            }
        }

        private void chkPATT_CheckedChanged(object sender, EventArgs e) {
            if (chkPATT.Checked) {
                txtTcpTimeout.Text = "";
                txtTcpTimeout.Enabled = false;
                lblTcpTimeout.Enabled = false;
            } else {
                txtTcpTimeout.Text = "";
                txtTcpTimeout.Enabled = true;
                lblTcpTimeout.Enabled = true;
            }
        }
        #endregion

        #region Serial
        private void btnOpen_Click(object sender, EventArgs e) {
            try {
                if (string.IsNullOrWhiteSpace(txtComPort.Text.Trim()))
                    throw new Exception("COM Port field cannot be empty.");

                if (string.IsNullOrWhiteSpace(txtBaudRate.Text.Trim()))
                    throw new Exception("Baudrate field cannot be empty.");

                if (_device != null)
                    throw new Exception(" is already running.");

                _isListening = true;

                ConnectSerial();

                _isSerial = true;
                _isTcpIp = false;
                _isListening = false;

                _standardModeControl = new StandardModeControl(_device);
                _standardModeControl.IsSerial = true;
                _standardModeControl.IsTcpIp = false;
                OpenControl(_standardModeControl);
                SetSerialFields(true);
                SetTcpIpFields(false);
            } catch(Exception ex) {
                MessageBox.Show(ex.Message, "Error");
                _isListening = false;
            }
        }

        private void btnClose_Click(object sender, EventArgs e) {
            try {
                if (_device != null)
                    _device.Dispose();

                _isListening = false;
                _isSerial = false;
                _isTcpIp = false;
                _device = null;

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

        #region TCP/IP
        private void btnStartListening_Click(object sender, EventArgs e) {
            try {
                if (string.IsNullOrWhiteSpace(txtIPPort.Text.Trim()))
                    throw new Exception("IP Port field cannot be empty.");

                if (_device != null)
                    throw new Exception("Port is already running.");

                _isListening = true;

                ConnectTcp();

                _isSerial = false;
                _isTcpIp = true;
                _isListening = false;

                if (chkPATT.Checked) {
                    _patModeControl = new PATModeControl(_device);
                    OpenControl(_patModeControl);
                } else {
                    _standardModeControl = new StandardModeControl(_device);
                    _standardModeControl.IsSerial = false;
                    _standardModeControl.IsTcpIp = true;
                    OpenControl(_standardModeControl);
                }

                SetTcpIpFields(true);
                SetSerialFields(false);
                Invoker.SetCheckBox(chkPATT, false);

            } catch (Exception ex) {
                MessageBox.Show(ex.Message, "Error");
                _isListening = false;
            }
        }

        private void btnStopListening_Click(object sender, EventArgs e) {
            try {
                if (_device != null)
                    _device.Dispose();

                _isListening = false;
                _isSerial = false;
                _isTcpIp = false;
                _device = null;
                
                CloseControl();
            } catch (Exception ex) {
                MessageBox.Show(ex.Message, "Error");
                _isListening = false;
            }
        }
        #endregion

        #region Application Layer
        private void OpenControl(UserControl control) {
            grpControl.Visible = true;
            grpControl.Controls.Clear();
            grpControl.Controls.Add(control);
        }

        private void CloseControl() {
            grpControl.Visible = false;
            grpControl.Controls.Clear();

            SetTcpIpFields(true);
            SetSerialFields(true);
            Invoker.SetCheckBoxChecked(chkBroadcastMessage, false);
            Invoker.SetCheckBoxChecked(chkPATT, false);
        }

        private void SetTcpIpFields(bool isEnabled) {
            Invoker.SetTextBox(txtIPPort, isEnabled);
            if (!chkPATT.Checked)
                Invoker.SetTextBox(txtTcpTimeout, isEnabled);
            else
                Invoker.SetTextBox(txtTcpTimeout, false);

            Invoker.SetLabel(lblIPAddress, isEnabled);
            Invoker.SetLabel(lblIPPort, isEnabled);
            if (!chkPATT.Checked)
                Invoker.SetLabel(lblTcpTimeout, isEnabled);
            else
                Invoker.SetLabel(lblTcpTimeout, false);

            Invoker.SetCheckBox(chkBroadcastMessage, isEnabled);
            Invoker.SetCheckBox(chkPATT, isEnabled);

            Invoker.SetButton(btnStartListening, isEnabled);
            Invoker.SetButton(btnStopListening, isEnabled);
        }

        private void SetSerialFields(bool isEnabled) {
            Invoker.SetButton(btnOpen, isEnabled);
            Invoker.SetButton(btnClose, isEnabled);

            Invoker.SetGroupBox(grpSerialSettings, isEnabled);
            Invoker.SetGroupBox(grpComPort, isEnabled);
        }

        private void SetDefaultValues() {
            Invoker.SetTextBox(txtIPPort, "18101");
            Invoker.SetTextBox(txtIpAddress, GetAppIPAddress());

            Invoker.SetNumericUpDown(txtTimeOut, "30");
            Invoker.SetNumericUpDown(txtTimeDelay, "10");

            Invoker.SetTextBox(txtComPort, "9");
            Invoker.SetTextBox(txtBaudRate, "9600");

            Invoker.SetLabel(txtCommStatus, "DISCONNECTED");
            Invoker.SetLabel(txtCommStatus, Color.DarkRed);
        }

        private string GetAppIPAddress() {
            var host = Dns.GetHostEntry(Dns.GetHostName());
            string ipAddress = string.Empty;

            foreach (var ip in host.AddressList) {
                if (ip.AddressFamily == AddressFamily.InterNetwork) {
                    ipAddress = ip.ToString();
                    break;
                }
            }

            return ipAddress;
        }

        private void UpdateConnectionStatus() {
            new Thread(() => {
                while(true) {
                    Thread.Sleep(10);

                    if (!chkPATT.Checked) {
                        Invoker.SetLabel(lblCommStatus, "Communication Status:");
                        if (_isListening) {
                            Invoker.SetLabel(txtCommStatus, "CONNECTING...");
                            Invoker.SetLabel(txtCommStatus, Color.Blue);
                        } else {
                            if (_device != null) {
                                if(_isSerial && !_isTcpIp) {
                                    Invoker.SetLabel(txtCommStatus, "CONNECTED SERIAL");
                                    Invoker.SetLabel(txtCommStatus, Color.Green);
                                }
                                if(!_isSerial && _isTcpIp) {                                                                
                                    if(_device.ConnectedDevicesCount() != 0) {
                                        Invoker.SetLabel(txtCommStatus, "CONNECTED TCP/IP");
                                        Invoker.SetLabel(txtCommStatus, Color.Green);
                                    } else {
                                        Invoker.SetLabel(txtCommStatus, "CONNECTING...");
                                        Invoker.SetLabel(txtCommStatus, Color.Blue);
                                    }
                                }
                            } else {
                                Invoker.SetLabel(txtCommStatus, "DISCONNECTED");
                                Invoker.SetLabel(txtCommStatus, Color.DarkRed);
                            }
                        }
                    } else {
                        Invoker.SetLabel(lblCommStatus, "Connected Devices:");
                      
                        if (_device != null) {
                            Invoker.SetLabel(txtCommStatus, _device.ConnectedDevicesCount());
                            Invoker.SetLabel(txtCommStatus, Color.Black);
                        } else {
                            Invoker.SetLabel(txtCommStatus, "0");
                            Invoker.SetLabel(txtCommStatus, Color.Black);
                        }
                    }
                }
            }).Start();
        }
        #endregion

        #region Communication Layer
        private void ConnectSerial() {
            if (_device != null)
                throw new Exception("Port is already running.");

            _device = DeviceService.Create(new ConnectionConfig {
                DeviceType = DeviceType.Ingenico_EPOS_Desk5000,
                ConnectionMode = ConnectionModes.SERIAL,
                Port = txtComPort.Text,
                Timeout = Convert.ToInt32(txtTimeOut.Value * 1000),
                TimeDelay = Convert.ToInt32(txtTimeDelay.Value),
                BaudRate = (BaudRate)Convert.ToInt32(txtBaudRate.Text),
                DataBits = DataBits.Eight,
                Parity = Parity.Even,
                StopBits = StopBits.One,
                Path = string.Empty
            });

            _device.OnMessageSent += DeviceOnMessageSent;
            _device.OnBroadcastMessage += DeviceOnBroadcastMessage;
        }

        private void ConnectTcp() {
            if (_device != null)
                throw new Exception("Port is already running.");

            _device = DeviceService.Create(new ConnectionConfig() {
                DeviceType = DeviceType.Ingenico_EPOS_Move5000,
                ConnectionMode = (chkPATT.Checked ? ConnectionModes.PAY_AT_TABLE : ConnectionModes.TCP_IP_SERVER),
                Port = txtIPPort.Text.Trim(),
                Timeout = !string.IsNullOrEmpty(txtTcpTimeout.Text) ? Convert.ToInt32(int.Parse(txtTcpTimeout.Text) * 1000) : 0,
            });

            _device.OnMessageSent += DeviceOnMessageSent;
            _device.OnPayAtTableRequest += DeviceOnPayAtTableRequest;
            _device.OnBroadcastMessage += DeviceOnBroadcastMessage;
        }

        private void DeviceOnMessageSent(string message) {
            if(_isTcpIp && !_isSerial) {
                if (chkPATT.Checked)
                {
                    _patModeControl.DeviceOnMessageSent(message);
                }
                else
                {
                    _standardModeControl.DeviceOnMessageSent(message);
                }              
            }


            if (!_isTcpIp && _isSerial) {
                if (chkPATT.Checked) {
                    _patModeControl.DeviceOnMessageSent(message);
                } else {
                    _standardModeControl.DeviceOnMessageSent(message);
                }
            }
        }

        private void DeviceOnBroadcastMessage(string code, string message) {
            if (chkBroadcastMessage.Checked || chkBroadcastMessageSerial.Checked) {
                if (_isTcpIp && !_isSerial)
                    if (chkPATT.Checked) {
                        _patModeControl.DeviceOnBroadcastMessage(code, message);
                    } else {
                        _standardModeControl.DeviceOnBroadcastMessage(code, message);
                    }

                if (!_isTcpIp && _isSerial)
                    if (chkPATT.Checked) {
                        _patModeControl.DeviceOnBroadcastMessage(code, message);
                    } else {
                        _standardModeControl.DeviceOnBroadcastMessage(code, message);
                    }
            }
        }

        private void DeviceOnPayAtTableRequest(PATRequest request) {
            if (_isTcpIp && !_isSerial)
                _patModeControl.DeviceOnPayAtTableRequest(request);
        }
        #endregion
    }
}