using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Ports;
using System.Diagnostics;

using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.Agg.OpenGlGui;
using MatterHackers.PolygonMesh;
using MatterHackers.RenderOpenGl;
using MatterHackers.VectorMath;
using MatterHackers.MatterControl.DataStorage;
using MatterHackers.Localizations;

namespace MatterHackers.MatterControl.PrinterControls.PrinterConnections
{
    public class EditConnectionWidget : ConnectionWidgetBase
    {
        List<SerialPortIndexRadioButton> SerialPortButtonsList = new List<SerialPortIndexRadioButton>();
        List<BaudRateRadioButton> BaudRateButtonsList = new List<BaudRateRadioButton>();
        FlowLayoutWidget ConnectionControlContainer;
        Printer ActivePrinter;
        MHTextEditWidget printerNameInput;
		MHTextEditWidget otherBaudRateInput;
        MHTextEditWidget printerModelInput;
        MHTextEditWidget printerMakeInput;
        LinkButtonFactory linkButtonFactory = new LinkButtonFactory();
        Button saveButton;
        Button cancelButton;
        GuiWidget comPortLabelWidget;
        GuiWidget baudRateWidget;
		RadioButton otherBaudRateRadioButton;
        CheckBox enableAutoconnect;
        bool printerComPortIsAvailable = false;
        TextImageButtonFactory textImageButtonFactory = new TextImageButtonFactory();

        bool addNewPrinterFlag = false;

        public EditConnectionWidget(ConnectionWindow windowController, GuiWidget containerWindowToClose, Printer activePrinter = null)
            : base(windowController, containerWindowToClose)
        {
            textImageButtonFactory.normalTextColor = RGBA_Bytes.White;
            textImageButtonFactory.hoverTextColor = RGBA_Bytes.White;
            textImageButtonFactory.disabledTextColor = RGBA_Bytes.White;
            textImageButtonFactory.pressedTextColor = RGBA_Bytes.White;
            textImageButtonFactory.borderWidth = 0;

            linkButtonFactory.textColor = RGBA_Bytes.White;
            linkButtonFactory.fontSize = 8;

            this.BackgroundColor = ActiveTheme.Instance.SecondaryBackgroundColor;
            this.AnchorAll();
            this.Padding = new BorderDouble(0); //To be re-enabled once native borders are turned off

            GuiWidget mainContainer = new FlowLayoutWidget(FlowDirection.TopToBottom);
            mainContainer.AnchorAll();
            mainContainer.Padding = new BorderDouble(3, 0, 3, 5);
            mainContainer.BackgroundColor = ActiveTheme.Instance.PrimaryBackgroundColor;

            string headerTitle;

            if (activePrinter == null)
            {
                headerTitle = string.Format("Add a Printer");
                this.addNewPrinterFlag = true;
                this.ActivePrinter = new Printer();
                this.ActivePrinter.Name = "Default Printer";
                this.ActivePrinter.BaudRate = "250000";
                try
                {
                    this.ActivePrinter.ComPort = SerialPort.GetPortNames()[0];
                }
                catch
                {
                    //No active COM ports
                }
            }
            else
            {
                this.ActivePrinter = activePrinter;
				string editHeaderTitleTxt = new LocalizedString ("Edit").Translated; 
				headerTitle = string.Format("{1} - {0}", this.ActivePrinter.Name, editHeaderTitleTxt);
                if (this.ActivePrinter.BaudRate == null)
                {
                    this.ActivePrinter.BaudRate = "250000";
                }
                if (this.ActivePrinter.ComPort == null)
                {
                    try
                    {
                        this.ActivePrinter.ComPort = SerialPort.GetPortNames()[0];
                    }
                    catch
                    {
                        //No active COM ports
                    }
                }
            }

            FlowLayoutWidget headerRow = new FlowLayoutWidget(FlowDirection.LeftToRight);
            headerRow.Margin = new BorderDouble(0, 3, 0, 0);
            headerRow.Padding = new BorderDouble(0, 3, 0, 3);
            headerRow.HAnchor = HAnchor.ParentLeftRight;
            {

                TextWidget headerLabel = new TextWidget(headerTitle, pointSize:14);
                headerLabel.TextColor = this.defaultTextColor;

                headerRow.AddChild(headerLabel);
            }

            ConnectionControlContainer = new FlowLayoutWidget(FlowDirection.TopToBottom); 
            ConnectionControlContainer.Padding = new BorderDouble(5);
            ConnectionControlContainer.BackgroundColor = ActiveTheme.Instance.SecondaryBackgroundColor;
            ConnectionControlContainer.HAnchor = HAnchor.ParentLeftRight;            
            {
				TextWidget printerNameLabel = new TextWidget(new LocalizedString("Printer Name").Translated, 0, 0, 10);
                printerNameLabel.TextColor = this.defaultTextColor;
                printerNameLabel.HAnchor = HAnchor.ParentLeftRight;
                printerNameLabel.Margin = new BorderDouble(0, 0, 0, 1);

                printerNameInput = new MHTextEditWidget(this.ActivePrinter.Name);
                
                printerNameInput.HAnchor |= HAnchor.ParentLeftRight;

                comPortLabelWidget = new FlowLayoutWidget();

				Button refreshComPorts = linkButtonFactory.Generate(new LocalizedString("(refresh)").Translated);
                refreshComPorts.Margin = new BorderDouble(left: 5);
                refreshComPorts.VAnchor = VAnchor.ParentBottom;
                refreshComPorts.Click += new ButtonBase.ButtonEventHandler(RefreshComPorts);                

				TextWidget comPortLabel = new TextWidget(new LocalizedString("Serial Port").Translated, 0, 0, 10);
                comPortLabel.TextColor = this.defaultTextColor;

                comPortLabelWidget.AddChild(comPortLabel);
                comPortLabelWidget.AddChild(refreshComPorts);
                comPortLabelWidget.Margin = new BorderDouble(0, 0, 0, 10);
                comPortLabelWidget.HAnchor = HAnchor.ParentLeftRight;

                FlowLayoutWidget comPortContainer = new FlowLayoutWidget(FlowDirection.TopToBottom);
                comPortContainer.Margin = new BorderDouble(0);
                comPortContainer.HAnchor = HAnchor.ParentLeftRight;
                

                int portIndex = 0;
                foreach (string serialPort in SerialPort.GetPortNames())
                {
                    //Filter com port list based on usb type (applies to Mac mostly)
                    bool looks_like_mac = serialPort.StartsWith("/dev/tty.");
                    bool looks_like_pc = serialPort.StartsWith("COM");
                    if (looks_like_mac || looks_like_pc)
                    {
                        SerialPortIndexRadioButton comPortOption = createComPortOption(serialPort);
                        comPortContainer.AddChild(comPortOption);
                        portIndex++;
                    }
                }

                //If there are no com ports in the filtered list assume we are missing something and show the unfiltered list
                if (portIndex == 0)
                {

                    foreach (string serialPort in SerialPort.GetPortNames())
                    {
                        SerialPortIndexRadioButton comPortOption = createComPortOption(serialPort);
                        comPortContainer.AddChild(comPortOption);
                        portIndex++;
                    }
                }

                if (!printerComPortIsAvailable && this.ActivePrinter.ComPort != null)
                {
                    SerialPortIndexRadioButton comPortOption = createComPortOption(this.ActivePrinter.ComPort);
                    comPortOption.Enabled = false;
                    comPortContainer.AddChild(comPortOption);
                    portIndex++;
                }

                //If there are still no com ports show a message to that effect
                if (portIndex == 0)
                {
					TextWidget comPortOption = new TextWidget(new LocalizedString("No COM ports available").Translated);
                    comPortOption.Margin = new BorderDouble(3, 6, 5, 6);
                    comPortOption.TextColor = this.subContainerTextColor;
                    comPortContainer.AddChild(comPortOption);
                }


				TextWidget baudRateLabel = new TextWidget(new LocalizedString("Baud Rate").Translated, 0, 0, 10);
                baudRateLabel.TextColor = this.defaultTextColor;
                baudRateLabel.Margin = new BorderDouble(0, 0, 0, 10);
                baudRateLabel.HAnchor = HAnchor.ParentLeftRight;

                baudRateWidget = GetBaudRateWidget();
                baudRateWidget.HAnchor = HAnchor.ParentLeftRight;

                FlowLayoutWidget printerMakeContainer = createPrinterMakeContainer();
                FlowLayoutWidget printerModelContainer = createPrinterModelContainer();

				enableAutoconnect = new CheckBox(new LocalizedString("Auto Connect").Translated);
                enableAutoconnect.TextColor = RGBA_Bytes.White;
                enableAutoconnect.Margin = new BorderDouble(top: 10);
                enableAutoconnect.HAnchor = HAnchor.ParentLeft;
                if (this.ActivePrinter.AutoConnectFlag)
                {
                    enableAutoconnect.Checked = true;
                }

                ConnectionControlContainer.VAnchor = VAnchor.ParentBottomTop;
                ConnectionControlContainer.AddChild(printerNameLabel);
                ConnectionControlContainer.AddChild(printerNameInput);
                ConnectionControlContainer.AddChild(printerMakeContainer);
                ConnectionControlContainer.AddChild(printerModelContainer);
                ConnectionControlContainer.AddChild(comPortLabelWidget);
                ConnectionControlContainer.AddChild(comPortContainer);
                ConnectionControlContainer.AddChild(baudRateLabel);
                ConnectionControlContainer.AddChild(baudRateWidget);
                ConnectionControlContainer.AddChild(enableAutoconnect);
            }

            FlowLayoutWidget buttonContainer = new FlowLayoutWidget(FlowDirection.LeftToRight);
            buttonContainer.HAnchor = HAnchor.ParentLeft | HAnchor.ParentRight;
            //buttonContainer.VAnchor = VAnchor.BottomTop;
            buttonContainer.Margin = new BorderDouble(0, 3);
            {
                //Construct buttons
				saveButton = textImageButtonFactory.Generate(new LocalizedString("Save").Translated);
                //saveButton.VAnchor = VAnchor.Bottom;

				cancelButton = textImageButtonFactory.Generate(new LocalizedString("Cancel").Translated);
                //cancelButton.VAnchor = VAnchor.Bottom;
                cancelButton.Click += new ButtonBase.ButtonEventHandler(CancelButton_Click);

                //Add buttons to buttonContainer
                buttonContainer.AddChild(saveButton);
                buttonContainer.AddChild(cancelButton);
            }

            //mainContainer.AddChild(new PrinterChooser());

            mainContainer.AddChild(headerRow);
            mainContainer.AddChild(ConnectionControlContainer);
            mainContainer.AddChild(buttonContainer);

            this.AddChild(mainContainer);

            BindSaveButtonHandlers();
			BindBaudRateHandlers();
        }

        private FlowLayoutWidget createPrinterMakeContainer()
        {
            FlowLayoutWidget container = new FlowLayoutWidget(FlowDirection.TopToBottom);
            container.Margin = new BorderDouble(0, 5);
            BorderDouble elementMargin = new BorderDouble(top: 3);

			TextWidget printerManufacturerLabel = new TextWidget(new LocalizedString("Printer Make").Translated, 0, 0, 10);
            printerManufacturerLabel.TextColor = this.defaultTextColor;
            printerManufacturerLabel.HAnchor = HAnchor.ParentLeftRight;
            printerManufacturerLabel.Margin = elementMargin;

            string printerMake = "";
            if (this.ActivePrinter.Make != null)
            {
                printerMake = this.ActivePrinter.Make;
            }

            printerMakeInput = new MHTextEditWidget(printerMake);
            printerMakeInput.HAnchor |= HAnchor.ParentLeftRight;
            printerMakeInput.Margin = elementMargin;

            container.AddChild(printerManufacturerLabel);
            container.AddChild(printerMakeInput);

            container.HAnchor = HAnchor.ParentLeftRight;
            return container;
        }

        private FlowLayoutWidget createPrinterModelContainer()
        {
            FlowLayoutWidget container = new FlowLayoutWidget(FlowDirection.TopToBottom);
            container.Margin = new BorderDouble(0, 5);
            BorderDouble elementMargin = new BorderDouble(top: 3);

			TextWidget printerModelLabel = new TextWidget(new LocalizedString("Printer Model").Translated, 0, 0, 10);
            printerModelLabel.TextColor = this.defaultTextColor;
            printerModelLabel.HAnchor = HAnchor.ParentLeftRight;
            printerModelLabel.Margin = elementMargin;

            string printerModel = "";
            if (this.ActivePrinter.Model != null)
            {
                printerModel = this.ActivePrinter.Model;
            }

            printerModelInput = new MHTextEditWidget(printerModel);
            printerModelInput.HAnchor |= HAnchor.ParentLeftRight;
            printerModelInput.Margin = elementMargin;

            container.AddChild(printerModelLabel);
            container.AddChild(printerModelInput);

            container.HAnchor = HAnchor.ParentLeftRight;
            return container;
        }

        public GuiWidget GetBaudRateWidget()
        {
            FlowLayoutWidget baudRateContainer = new FlowLayoutWidget(FlowDirection.TopToBottom);
            baudRateContainer.Margin = new BorderDouble(0);
            
            List<string> baudRates = new List<string> { "115200", "250000"};
			BorderDouble baudRateMargin = new BorderDouble(3,3,5,3);

            foreach (string baudRate in baudRates)
            {
                BaudRateRadioButton baudOption = new BaudRateRadioButton(baudRate);
                BaudRateButtonsList.Add(baudOption);
				baudOption.Margin = baudRateMargin;
                baudOption.HAnchor = HAnchor.ParentLeft;
                baudOption.TextColor = this.subContainerTextColor;                
                if (this.ActivePrinter.BaudRate == baudRate)
                {
                    baudOption.Checked = true;
                }
                baudRateContainer.AddChild(baudOption);
            }

			otherBaudRateRadioButton = new RadioButton(new LocalizedString("Other").Translated);
			otherBaudRateRadioButton.Margin = baudRateMargin;
			otherBaudRateRadioButton.TextColor = this.subContainerTextColor;

			baudRateContainer.AddChild(otherBaudRateRadioButton);

			//See if the baud rate of the current print is in the list of displayed rates,
			//flag the 'other' option if it is not and prefill the rate.
            otherBaudRateInput = new MHTextEditWidget("");
            otherBaudRateInput.Visible = false;
            otherBaudRateInput.HAnchor |= HAnchor.ParentLeftRight;

            if (this.ActivePrinter.BaudRate != null)
            {
                if (!baudRates.Contains(this.ActivePrinter.BaudRate.ToString()))
                {
                    otherBaudRateRadioButton.Checked = true;
                    otherBaudRateInput.Text = this.ActivePrinter.BaudRate.ToString();
                    otherBaudRateInput.Visible = true;
                }
            }

			baudRateContainer.AddChild(otherBaudRateInput);
            return baudRateContainer;
        }

		private void BindBaudRateHandlers()
		{
			otherBaudRateRadioButton.CheckedStateChanged += new RadioButton.CheckedStateChangedEventHandler(BindBaudRate_Select);
			foreach(BaudRateRadioButton button in BaudRateButtonsList)
			{
				button.CheckedStateChanged += new RadioButton.CheckedStateChangedEventHandler(BindBaudRate_Select);
			}
			BindBaudRate_Select(null, null);
		}
		
		private void BindBaudRate_Select(object sender, EventArgs e)
		{
			if (otherBaudRateRadioButton.Checked == true)
			{
				otherBaudRateInput.Visible = true;
			}
			else
			{
				otherBaudRateInput.Visible = false;
			}
		}

        public SerialPortIndexRadioButton createComPortOption(string serialPort)
        {
            //Add formatting here to make port names prettier
            string portName = serialPort;

            SerialPortIndexRadioButton comPortOption = new SerialPortIndexRadioButton(portName, serialPort);
            SerialPortButtonsList.Add(comPortOption);

            comPortOption.HAnchor = HAnchor.ParentLeft;
            comPortOption.Margin = new BorderDouble(3, 3, 5, 3);
            comPortOption.TextColor = this.subContainerTextColor;
                        
            if (this.ActivePrinter.ComPort == serialPort)
            {
                comPortOption.Checked = true;
                printerComPortIsAvailable = true;
            }
            return comPortOption;
        }

        void RefreshComPorts(object sender, MouseEventArgs mouseEvent)
        {
            try
            {
                this.ActivePrinter.Name = printerNameInput.Text;
                this.ActivePrinter.BaudRate = GetSelectedBaudRate();
                this.ActivePrinter.ComPort = GetSelectedSerialPort();
            }
            catch
            {

            }
            this.windowController.ChangedToEditPrinter(this.ActivePrinter);
        }

        void ReloadCurrentWidget(object sender, MouseEventArgs mouseEvent)
        {
            if (this.addNewPrinterFlag == true)
            {
                this.windowController.ChangeToAddPrinter();
            }
            else
            {
                this.windowController.ChangedToEditPrinter(this.ActivePrinter);
            }            
        }

        void BindSaveButtonHandlers()
        {
            saveButton.UnbindClickEvents();
            saveButton.Text = "Save";
            saveButton.Enabled = true;
            saveButton.Click += new ButtonBase.ButtonEventHandler(SaveButton_Click);
        }

        void CloseWindow(object o, MouseEventArgs e)
        {
            PrinterCommunication.Instance.HaltConnectionThread();
            this.containerWindowToClose.Close();
        }

        void CancelButton_Click(object sender, MouseEventArgs mouseEvent)
        {            
            if (GetPrinterRecordCount() > 0)
            {
                this.windowController.ChangeToChoosePrinter();
            }
            else
            {
                this.containerWindowToClose.Close();
            }
        }

        void SaveButton_Click(object sender, MouseEventArgs mouseEvent)
        {
            this.ActivePrinter.Name = printerNameInput.Text;
            try
            {
                this.ActivePrinter.BaudRate = GetSelectedBaudRate();
                this.ActivePrinter.ComPort = GetSelectedSerialPort();
                this.ActivePrinter.Make = printerMakeInput.Text;
                this.ActivePrinter.Model = printerModelInput.Text;
                this.ActivePrinter.AutoConnectFlag = enableAutoconnect.Checked;
            }
            catch
            {
                //Unable to retrieve Baud or Port, possibly because they weren't shown as options - needs better handling
            }
            this.ActivePrinter.Commit();

            this.windowController.ChangeToChoosePrinter();
        }
        
        private string GetSelectedBaudRate()
        {
            foreach (BaudRateRadioButton button in BaudRateButtonsList)
            {
                if (button.Checked)
                {
                    return button.BaudRate.ToString();
                }
            }
			if (otherBaudRateRadioButton.Checked)
			{
				return otherBaudRateInput.Text;
			}

			throw new Exception(new LocalizedString("Could not find a selected button.").Translated);
        }

        void InstallDriver()
        {
            string infPathAndFileToInstall = "";
            switch (MatterHackers.Agg.UI.WindowsFormsAbstract.GetOSType())
            {
                case Agg.UI.WindowsFormsAbstract.OSType.Windows:
                    if (!File.Exists(infPathAndFileToInstall))
                    {
                        var driverInstallerProcess = new Process();
                        // Prepare the process to run
                        // Enter in the command line arguments, everything you would enter after the executable name itself
                        driverInstallerProcess.StartInfo.Arguments = "--installInf \"" + infPathAndFileToInstall;
                        // Enter the executable to run, including the complete path
                        string printerDriverInstallerExePathAndFileName = Path.Combine(".", "PrinterDriverInstaller.exe");

                        driverInstallerProcess.StartInfo.CreateNoWindow = true;
                        driverInstallerProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

                        driverInstallerProcess.StartInfo.FileName = System.IO.Path.GetFullPath(printerDriverInstallerExePathAndFileName);
                        driverInstallerProcess.StartInfo.Verb = "runas";
                        driverInstallerProcess.StartInfo.UseShellExecute = true;

                        driverInstallerProcess.Start();

                        driverInstallerProcess.WaitForExit();

                        // Retrieve the app's exit code
                        var exitCode = driverInstallerProcess.ExitCode;
                    }
                    break;

                case Agg.UI.WindowsFormsAbstract.OSType.Mac:
                    break;
            }
        }

        private string GetSelectedSerialPort()
        {
            foreach (SerialPortIndexRadioButton button in SerialPortButtonsList)
            {
                if (button.Checked)
                {
                    return button.PortValue;
                }
            }

			throw new Exception(new LocalizedString("Could not find a selected button.").Translated);
        }
    }
}
