//      __  ____                 _   __                
//     /  |/  (_)_____________  / | / /___ _   ______ _
//    / /|_/ / / ___/ ___/ __ \/  |/ / __ \ | / / __ `/
//   / /  / / / /__/ /  / /_/ / /|  / /_/ / |/ / /_/ / 
//  /_/  /_/_/\___/_/   \____/_/ |_/\____/|___/\__,_/  
// 
// Copyright (c) 2013. All Rights Reserved.
// MicroNova, LLC
// www.micro-nova.com
//  
// Permission to use, copy, modify and distribute this software and its documentation, 
// is granted for educational, research, and not-for-profit purposes, provided 
// that this notice is included in all copies, modifications, and distributions.
// This software is provided "as-is" without any warranties or guarantees.
//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

using System.Diagnostics;

namespace JM_Programmer
{

    public partial class Form1 : Form
    {

        MercuryUSB myDevice = new MercuryUSB();
        string BitstreamPath = "";

        public Form1()
        {
            InitializeComponent();

            // Propery changed handler
            myDevice.PropertyChanged += new PropertyChangedEventHandler(MercuryPropertyChanged);
           
            // Initialize status
            myDevice.init();

        }

        public void MercuryPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // How to detect variable changes
            // http://www.daniweb.com/software-development/csharp/threads/215395

            // Invoke required because property could be changed on another thread
            // http://stackoverflow.com/questions/2601166/c-how-to-change-window-state-of-form-on-a-different-thread

            if (this.InvokeRequired) // if change initiated some other thread
                try
                {
                    Invoke(new Action(() => { UpdateForm(e); }));
                }
                catch
                {
                    // without try-catch, we get a ObjectDisposedException upon exiting because the form disappears
                }
            else
                UpdateForm(e); // if change initiated by GUI thread

            
        }

        public void UpdateForm(PropertyChangedEventArgs e)
        {

                    switch (e.PropertyName)
                    {
                        case "connected":
                            if (myDevice.connected == false)
                            {
                                ConnectionLabel.Text = "Disconnected";
                                ConnectionLabel.BackColor = Color.LightSalmon;
                                btnConnect.Text = "Connect";
                                btnConnect.Image = btnConnect.Image = Mercury_Programmer.Properties.Resources.connect;
                            }
                            else
                            {
                                ConnectionLabel.Text = "Connected";
                                ConnectionLabel.BackColor = Color.LightGreen;
                                btnConnect.Text = "Disconnect";
                                btnConnect.Image = btnConnect.Image = Mercury_Programmer.Properties.Resources.disconnect;
                            }
                            break;

                        case "busy":

                            if (myDevice.busy == 2)
                            {
                                // communications in progress
                                StatusLabel.Text = "Busy";
                                StatusLabel.BackColor = Color.Yellow;
                                btnConnect.Enabled = false;
                                btnEraseFlash.Enabled = false;
                                btnBurnFlash.Enabled = false;
                                btnOpen.Enabled = false;
                                btnRegRead.Enabled = false;
                                btnRegWrite.Enabled = false;
                                buttonBurnEEPROM.Enabled = false;
                            }
                            else
                            {
                                // not doing anything
                                StatusLabel.Text = "Idle";
                                StatusLabel.BackColor = Color.Transparent;
                                btnConnect.Enabled = true;
                                btnOpen.Enabled = true;
                                btnEraseFlash.Enabled = myDevice.connected;
                                btnBurnFlash.Enabled = myDevice.connected;
                                btnRegRead.Enabled = myDevice.connected;
                                btnRegWrite.Enabled = myDevice.connected;
                                buttonBurnEEPROM.Enabled = myDevice.connected;

                            }
                            break;

                        case "console":
                            // set text
                            DebugBox.Text = myDevice.console;
                            // automatically scroll to bottom
                            DebugBox.SelectionStart = DebugBox.Text.Length;
                            DebugBox.ScrollToCaret();
                            break;

                        case "progress":
                            if (myDevice.progress == 1)
                            {
                                progressBar.Style = ProgressBarStyle.Marquee;
                                progressBar.MarqueeAnimationSpeed = 2;
                            }
                            else
                                progressBar.Style = ProgressBarStyle.Continuous;

                            progressBar.Value = myDevice.progress;
                            break;
    
                        case "dataread":
                            txtData.Text = myDevice.dataread.ToString("X").PadLeft(8, '0');
                            break;

                        default: break;
                    }
                }

        public void Form1_Load(object sender, EventArgs e)
        {
            // hide testing pages
            tabControl1.TabPages.Remove(tabPage2);
            tabControl1.TabPages.Remove(tabPage3);
            tabControl1.TabPages.Remove(tabPage4);
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            myDevice.Disconnect();
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
           if (myDevice.connected)
               myDevice.Disconnect();
           else
               myDevice.Connect(checkBoxConnectByIndex.Checked, (uint)numericUpDownIndex.Value); 
           
        }

        private void btnEraseFlash_Click(object sender, EventArgs e)
        {
            myDevice.FlashChipErase();
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
                OpenFileDialog dlg = new OpenFileDialog();
                dlg.DefaultExt = ".bit";             // Ensures .bit is the file extension, if someone types in a filename.
                dlg.Filter = "FPGA Bitstream|*.bit"; // Makes only *.bit files show up in the file dialog.
 
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    textboxFileName.Text = Path.GetFileName(dlg.FileName);
                    BitstreamPath = dlg.FileName;
                    myDevice.ConsoleWriteLine("Bitstream path: " + BitstreamPath);
                }


            }

        private void btnListDevices_Click(object sender, EventArgs e)
        {
            myDevice.ListDevices();
        }

        private void btnBurnFlash_Click(object sender, EventArgs e)
        {

            if (BitstreamPath != "")
                myDevice.FlashWriteBitstream(BitstreamPath);
            else
                myDevice.ConsoleWriteLine("Please select a bitfile!");




        }

        private void txtData_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (!"0123456789ABCDEF".Contains(Char.ToUpper(e.KeyChar).ToString()) && e.KeyChar != (char)8  )
                e.Handled = true;    
        }

        private void btnRegWrite_Click(object sender, EventArgs e)
        {
            int data = int.Parse(txtData.Text, System.Globalization.NumberStyles.HexNumber);
            myDevice.RegisterWrite((byte)numAddress.Value, data);
        }

        private void btnRegRead_Click(object sender, EventArgs e)
        {
            txtData.Text = "";
            myDevice.RegisterRead((byte)numAddress.Value);
        }

        private void btnAllLow_Click(object sender, EventArgs e)
        {
            myDevice.SetPins(0x00);
            myDevice.ConsoleWriteLine("All outputs set low.");
        }

        private void btnAllHigh_Click(object sender, EventArgs e)
        {
            myDevice.SetPins(0xFF);
            myDevice.ConsoleWriteLine("All outputs set high.");
        }

        private void labelVersion_Click(object sender, EventArgs e)
        {
            btnAllHigh.Visible = true;
            btnAllLow.Visible = true;
        }

        private void linkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start("http://www.micro-nova.com"); 
        }

        private void labelVersion_Click_1(object sender, EventArgs e)
        {

        }

        private void numAddress_ValueChanged(object sender, EventArgs e)
        {
            txtData.Text = "";
        }

        private void btnLoopback_Click(object sender, EventArgs e)
        {
            myDevice.TestLoopback();
        }

        public void logoBox_Click(object sender, EventArgs e)
        {
            // show testing pages
            if (!tabControl1.Contains(tabPage2))
            {
                tabControl1.TabPages.Add(tabPage2);
                tabControl1.TabPages.Add(tabPage3);
                tabControl1.TabPages.Add(tabPage4);
            }
            // increase window height
            this.Height = 800;
        }

        private void buttonBurnEEPROM_Click(object sender, EventArgs e)
        {
            // try to burn EEPROM
            bool success = myDevice.WriteEEPROM((int)numericUpDownBoardSerial.Value);
            
            // if successful, increment board serial if requested
            if(success && checkBoxAutoIncBoardSerial.Checked)
                numericUpDownBoardSerial.Value++;    
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // disconnect FTDI device before closing!
            myDevice.Disconnect();
        }

        private void buttonTestSRAM_Click(object sender, EventArgs e)
        {
            // set mode 3 (memory active)
            myDevice.RegisterWrite(1, 3);
        }

    }
}
