using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Collections.Specialized;
using System.Net;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;

using nsAlienRFID2;
using nsAlienDemo;


namespace Ex13
{
	/// <summary>
	/// Summary description for FormEx13.
	/// </summary>
	public partial class FormEx13 : System.Windows.Forms.Form
    {		
		
		public FormEx13()
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();
			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

        //private void trackBar1_Scroll(object sender, System.EventArgs e)
        //{
			
        //    this.speedControl1.Speed = this.trackBar1.Value;
        //    this.label1.Text = this.trackBar1.Value.ToString();
        //}
        
        const string USERNAME = "alien";
        const string PASSWORD = "password";
        const string CUSTOM_FORMAT = "${TAGIDB}, ${TX}, ${DATE2} ${TIME2}, ${PCWORD}, ${SPEED}, ${RSSI}, ${G2DATA1}, ${FREQ}";
        //const int MULTIPLY = 50;
        //const int RSSI_LIMIT = 50000;


        bool mbDisposing = false;
        static CAlienServer mServer = null;
        static string msTagStreamAddress = null;

        private delegate void Doit();
        private static readonly object moTagLock = new object();
        private static Dictionary<string, AlienRFIDTag> CurrentTags = new Dictionary<string, AlienRFIDTag>();

        private static string sLastConnectionString = null;

        private static Dictionary<string, clsReader> ActiveReaders = new Dictionary<string,clsReader>();
        private static Dictionary<string, MyStoredReaderState> StoredReaderStates = new Dictionary<string, MyStoredReaderState>();

        //clsReader mReader = null;
        //private ReaderInfo mReaderInfo = null;
        //private ComInterface meReaderInterface = ComInterface.enumTCPIP;


        private void FormEx13_Load(object sender, EventArgs e)
        {
            string exePath = Application.ExecutablePath;
            int idx = exePath.LastIndexOf("\\");
            if (idx != -1)
                AlienLog.Path = exePath.Substring(0, idx + 1);
            string appName = "Alien-Ex.13: Intelligent Tag Radar";
            AlienLog.WriteLine(true, appName);

            Thread.CurrentThread.Name = appName;
            this.Text = "Alien Library .NET - " + appName;

            mServer = new CAlienServer(7798);
            msTagStreamAddress = mServer.NotificationHost;
            mServer.ServerMessageReceived += new CAlienServer.ServerMessageReceivedEventHandler(mServer_ServerMessageReceived);
            mServer.StartListening();
            cboIPAddress.Select();
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            string connectionString = null;
            if (rdoTCP.Checked)
            {
                if (cboIPAddress.Text.Trim() == "")
                {
                    lblStatus.Text = "Please enter reader's IP Address.";
                    cboIPAddress.Focus();
                    return;
                }
                
                string ip = cboIPAddress.Text.Trim();
                string port = PortUD.Value.ToString();
                connectionString = ip + ":" + port;
            }
            else
            {
                if (cboPorts.Text.Trim() == "")
                {
                    lblStatus.Text = "Please select a com port.";
                    lblStatus.Focus();
                    return;
                }
                string com = cboPorts.Text.Trim();
                string br = cboHostBaudRate.Text.Trim();
                if (!string.IsNullOrEmpty(br))
                    connectionString = com + ":" + br;
                else
                    connectionString = com;
            }

            this.Cursor = Cursors.WaitCursor;
            clsReader reader = new clsReader(true);

            try
            {
                if (!ActiveReaders.ContainsKey(connectionString))
                {
                    if (rdoTCP.Checked)
                    {
                        string ip = cboIPAddress.Text.Trim();
                        reader.InitOnNetwork(ip, Convert.ToInt32(PortUD.Value));
                    }
                    else
                    {
                        string com = cboPorts.Text.Trim();
                        string br = cboHostBaudRate.Text.Trim();
                        int iCom = 0;
                        if (int.TryParse(com.Substring(3, 1), out iCom))
                        {
                            if (!string.IsNullOrEmpty(br))
                                reader.InitOnCom(iCom, int.Parse(br));
                            else
                                reader.InitOnCom(iCom);
                        }
                        else
                        {
                            lblStatus.Text = "Com port required.";
                            this.Cursor = Cursors.Default;
                        }
                    }

                    lblStatus.Text = "Connecting to the reader...";
                    this.Cursor = Cursors.WaitCursor;
                    connectReader(reader);
                    saveReaderState(connectionString, reader);
                }
                else
                    connectReader(ActiveReaders[connectionString]);

                string status = null;
                if (!configureReader(reader, connectionString))
                    status = "Reader on '" + connectionString + "' doesn't support ITR.  Only TagID and Time will be shown.";
                else
                    status = "Reader on '" + connectionString + "' connected and configured for ITR.";

                if (!ActiveReaders.ContainsKey(connectionString))
                    ActiveReaders.Add(connectionString, reader);
                else
                    ActiveReaders[connectionString] = reader;

                if (connectionString.Contains("."))
                {
                    if (!cboIPAddress.Items.Contains(cboIPAddress.Text.Trim()))
                        cboIPAddress.Items.Add(cboIPAddress.Text.Trim());
                }
                else
                {
                    if (!cboPorts.Items.Contains(cboPorts.Text.Trim()))
                        cboPorts.Items.Add(cboPorts.Text.Trim());
                }

                sLastConnectionString = connectionString;
                lblStatus.Text = status;
            }
            catch (Exception ex)
            {
                lblStatus.Text = ex.Message;
                if (reader.IsConnected)
                    reader.Disconnect();
            }
            this.Cursor = Cursors.Default;
        }
        void connectReader(clsReader reader)
        {
            string ipAddress = reader.ReaderSettings.IPAddress;

            string connect = reader.Connect();
            if (!reader.IsConnected)
                throw new Exception("Connection failed for the reader: " + ipAddress);

            if (reader.ReaderSettings.InterfaceType == ComInterface.enumTCPIP)
            {
                if (!reader.Login(USERNAME, PASSWORD))
                    throw new Exception("Login failed for the reader: " + ipAddress);
            }
        }

        void saveReaderState(string connectionString, clsReader reader)
        {
            if (!StoredReaderStates.ContainsKey(connectionString))
            {
                MyStoredReaderState oldReaderState = new MyStoredReaderState();
                oldReaderState.autoMode = reader.AutoMode;
                oldReaderState.ioStreamMode = reader.IOStreamMode;
                oldReaderState.notifyMode = reader.NotifyMode;
                string tagStreamAddress = reader.TagStreamAddress;
                if (tagStreamAddress.ToLower().Trim() != "(not set)")
                    oldReaderState.tagStreamAddress = tagStreamAddress;
                oldReaderState.tagStreamFormat = reader.TagStreamFormat;
                oldReaderState.tagStreamMode = reader.TagStreamMode;
                oldReaderState.acqG2Target = reader.AcqG2Target;
                try 
                { 
                    oldReaderState.acqG2TagData = reader.AcqG2TagData; 
                    oldReaderState.rssiFilter = reader.RSSIFilter;
                    oldReaderState.speedFilter = reader.SpeedFilter;
                }  // not all readers support it
                catch { lblStatus.Text = "Reader doesn't support ITR."; }
                oldReaderState.tagStreamCustomFormat = reader.TagStreamCustomFormat;

                StoredReaderStates.Add(connectionString, oldReaderState);
            }       
        }

        private bool configureReader(clsReader reader, string connectionString)
        {
            bool result = true;
            string tagData = getTagDataRequest();
            reader.TagStreamAddress = msTagStreamAddress;
            reader.TagStreamCustomFormat = CUSTOM_FORMAT;
            reader.TagStreamFormat = "Custom";
            try
            {
                reader.AcqG2TagData = tagData;
                reader.SpeedFilter = getSpeedFilter();
                reader.RSSIFilter = getRSSIFilter();
            }
            catch (Exception ex) { AlienLog.WriteLine(true, ex.Message);  result = false; }

            reader.NotifyMode = "OFF";
            reader.IOStreamMode = "OFF";

            reader.TagStreamMode = "ON";
            reader.AutoMode = "ON";

            return result;
        }

        private string getSpeedFilter()
        {
            string result = string.Empty;
            double value1;
            double value2;
            if (double.TryParse(txtSpeedFilter1.Text, out value1) &&
                double.TryParse(txtSpeedFilter2.Text, out value2) &&
                (value1 != value2))
                result = value1.ToString() + " " + value2.ToString();

            if (string.IsNullOrEmpty(result))
                result = "0";
            return result;
        }
        private string getRSSIFilter()
        {
            string result = string.Empty;
            decimal value1 = nudRSSIFilter1.Value;
            decimal value2 = nudRSSIFilter2.Value;
            if (value1 != value2)
                result = value1.ToString() + " " + value2.ToString();

            if (string.IsNullOrEmpty(result))
                result = "0";
            return result;
        }
        private string getTagDataRequest()
        {
            string tagData = "";
            if (!string.IsNullOrEmpty(cboBank.Text))
            {
                tagData = ((int)(cboBank.SelectedIndex - 1)).ToString();
                tagData += " " + ((int)(nudWordPtr.Value)).ToString();
                tagData += " " + ((int)(nudWordCount.Value)).ToString();
            }
            else
                tagData = "0";

            return tagData;
        }

        private void restoreReader(string connectionString, clsReader reader)
        {
            if (StoredReaderStates.ContainsKey(connectionString))
            {
                MyStoredReaderState oldReaderState = StoredReaderStates[connectionString];

                reader.AutoMode = oldReaderState.autoMode;
                reader.TagStreamMode = oldReaderState.tagStreamMode;
                reader.NotifyMode = oldReaderState.notifyMode;
                reader.IOStreamMode = oldReaderState.ioStreamMode;
                reader.TagStreamCustomFormat = oldReaderState.tagStreamCustomFormat;
                reader.TagStreamFormat = oldReaderState.tagStreamFormat;
                if (!string.IsNullOrEmpty(oldReaderState.tagStreamAddress))
                    reader.TagStreamAddress = oldReaderState.tagStreamAddress;
                if (!string.IsNullOrEmpty(oldReaderState.speedFilter))
                    reader.SpeedFilter = oldReaderState.speedFilter;
                if (!string.IsNullOrEmpty(oldReaderState.rssiFilter))
                    reader.RSSIFilter = oldReaderState.rssiFilter;
                if (!string.IsNullOrEmpty(oldReaderState.acqG2Target))
                    reader.AcqG2Target = oldReaderState.acqG2Target;
                if (!string.IsNullOrEmpty(oldReaderState.acqG2TagData))
                    reader.AcqG2TagData = oldReaderState.acqG2TagData;
            }
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            this.Cursor = Cursors.WaitCursor;
            string connectionString = sLastConnectionString;
            if (ActiveReaders.ContainsKey(connectionString))
            {
                clsReader reader = ActiveReaders[connectionString] as clsReader;
                try
                {
                    if (reader.IsConnected)
                    {
                        restoreReader(connectionString, reader);
                        reader.Disconnect();
                    }
                }
                catch { }
            }
            //btnDisconnect.Enabled = false;
            //btnConnect.Enabled = true;
            lblStatus.Text = "Reader disconnected.";
            this.Cursor = Cursors.Default;
        }

        private void rdoSerial_CheckedChanged(object sender, System.EventArgs e)
        {
            bool state = rdoSerial.Checked;
            grpSerial.Enabled = state;
            grpNetwork.Enabled = !state;
            btnConnect.Text = (state ? "Co&nnect" : "Co&nnect + Login");
        }
        private void rdoTCP_CheckedChanged(object sender, System.EventArgs e)
        {
            bool state = rdoTCP.Checked;
            grpSerial.Enabled = !state;
            grpNetwork.Enabled = state;
            btnConnect.Text = (state ? "Co&nnect + Login" : "Co&nnect");
            if (state)
                cboIPAddress.Select();
            else
                cboPorts.Select();
        }
        private void cboPorts_SelectedIndexChanged(object sender, EventArgs e)
        {
        }
        private void cboHostBaudRate_SelectedIndexChanged(object sender, EventArgs e)
        {
        }
        private void cboIPAddress_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode != Keys.Enter)
                return;
            IPAddress ipAddress = null;
            string ip = cboIPAddress.Text.Trim();
            if (!cboIPAddress.Items.Contains(ip))
            {
                try
                {
                    ipAddress = IPAddress.Parse(ip);
                    cboIPAddress.Items.Add(ip);
                }
                catch
                {
                    cboIPAddress.Text = "";
                    lblStatus.Text = "Illegal IP Address: '" + ip + "'.";
                }
            }
        }
        void mServer_ServerMessageReceived(string msg)
        {

            if (mbDisposing) return;
            if (msg == null) return;

            //Debug.WriteLine("mServer_ServerMessageReceived():\r\n" + msg.Replace("\0", "\\0"));
            lock (this)
            {
                Doit method = delegate
                                  {
                                      lock (moTagLock)
                                      {
                                          try
                                          {
                                              string key = null;        // tags id
                                              string tagOnly = null;    // tagstream message without MAC address

                                            try { tagOnly = msg.Substring(msg.IndexOf(" ") + 1); }
                                            catch { }
                                            if (tagOnly != null)
                                            {
                                                TagInfo tagInfo = AlienUtils.ParseCustomTag(CUSTOM_FORMAT, tagOnly);
                                                if (tagInfo == null)
                                                {
                                                    return;
                                                }
                                                key = tagInfo.TagID;
                                                if (!CurrentTags.ContainsKey(key))
                                                {
                                                    Point p = new Point(CurrentTags.Count * alienTagContol0.Size.Width, alienTagContol0.Location.Y);
                                                    Size size = alienTagContol0.Size;
                                                    AlienRFIDTag myControl = new AlienRFIDTag();
                                                    myControl.TagID = tagInfo.TagID;
                                                    myControl.Location = p;
                                                    myControl.Size = size;
                                                    if ((tagInfo.TagDataArray.Length > 0) &&
                                                        (!string.IsNullOrEmpty(tagInfo.TagDataArray[0])))
                                                        myControl.TagData = tagInfo.TagDataArray[0];

                                                    //myControl.Click += new System.EventHandler(myAlienRFIDTag_Click);

                                                    this.Controls.Add(myControl);
                                                    myControl.Show();
                                                    CurrentTags.Add(key, myControl);
                                                }
                                                CurrentTags[key].SpeedScale = (double)nudVelocityScale.Value;
                                                CurrentTags[key].Speed = tagInfo.Speed;
                                                CurrentTags[key].RSSILimit = (double)nudRSSIScale.Value;
                                                CurrentTags[key].RSSI = tagInfo.RSSI;
                                                CurrentTags[key].Time = tagInfo.LastSeenTime.Substring(12);
                                                CurrentTags[key].TagData = tagInfo.TagDataArray[0];
                                                CurrentTags[key].Frequency = tagInfo.Frequency;
                                            }
                                          }
                                          catch (Exception ex)
                                          {
                                              lblStatus.Text = ex.Message;
                                              Debug.WriteLine("Exception in 'method': " + ex.Message);
                                          }
                                      }
                                  };

                this.BeginInvoke(method);
            }
        }

        private void myAlienRFIDTag_Click(object sender, EventArgs e)
        {
            AlienRFIDTag myControl = sender as AlienRFIDTag;
            lblStatus.Text = "Click event for tag control with id = " + myControl.TagID;
        }

        private void FormEx13_FormClosing(object sender, FormClosingEventArgs e)
        {
            mbDisposing = true;
            foreach (string k in ActiveReaders.Keys)
            {
                try
                {
                    clsReader reader = ActiveReaders[k] as clsReader;
                    if (reader.IsConnected)
                    {
                        restoreReader(k, reader);
                        reader.Dispose();
                    }
                }
                catch { }
            }
            mServer.StopListening();
            mServer.Dispose();
        }


        private void btnClear_Click(object sender, EventArgs e)
        {
            lock (moTagLock)
            {
                CurrentTags.Clear();
                List<Control> controls = new List<Control>();
                foreach (Control c in this.Controls)
                {
                    if ((c is nsAlienDemo.AlienRFIDTag) &&
                        (!c.Name.EndsWith("0")))
                        controls.Add(c);
                }
                for (int i = 0; i < controls.Count; i++)
                    this.Controls.Remove(controls[i]);
                controls.Clear();
            }
        }

        private void txtSpeedFilter_KeyPress(object sender, KeyPressEventArgs e)
        {
            if ((e.KeyChar == 8) ||
                (e.KeyChar == 13))
                return;


            TextBox textBox = sender as TextBox;
            ErrorProvider errorProvider = textBox.Name.EndsWith("1") ? errorProvider1 : errorProvider2;
            if ((e.KeyChar == 46) && (textBox.Text.Contains(".")) && (!textBox.SelectedText.Contains(".")))
            {
                errorProvider.SetError(textBox, "Not a valid value: More than one '.'");
                textBox.SelectAll();
            }
            else if (((e.KeyChar < 48) || (e.KeyChar > 57)) && (e.KeyChar !=  46))
            {
                errorProvider.SetError(textBox, Convert.ToChar(e.KeyChar) + " is not a valid value.  Must be a number or '.'");
                textBox.SelectAll();
            }
            else
                errorProvider.SetError(textBox, "");
        }

        private void btnApply_Click(object sender, EventArgs e)
        {
            string failedReader = "";
            foreach (string key in ActiveReaders.Keys)
            {
                if (!applySettings(key))
                    failedReader += (key + ", ");
            }
            if (!string.IsNullOrEmpty(failedReader))
            {
                string msg = "Some reader don't support ITR: " + failedReader.Substring(0, failedReader.Length - 2);
                AlienLog.WriteLine(true, msg);
                lblStatus.Text = msg;
            }
            else
                lblStatus.Text = "All settings applied OK.";
        }

        private bool applySettings(string key)
        {
            bool result = true;
            try
            {
                if (!ActiveReaders[key].IsConnected)
                    connectReader(ActiveReaders[key]);
                Thread.Sleep(1);
                if (!configureReader(ActiveReaders[key], key))
                   result = false;
            }
            catch (Exception ex)
            {
                string msg = "Failed to apply readers settings: " + ex.Message;
                AlienLog.WriteLine(true, msg);
                lblStatus.Text = msg;
            }
            return result;
        }
	}

    internal class MyStoredReaderState
    {
        internal string notifyMode = null;
        internal string tagStreamMode = null;
        internal string ioStreamMode = null;
        internal string tagStreamAddress = null;
        internal string autoMode = null;
        internal string tagStreamFormat = null;
        internal string acqG2TagData = null;
        internal string tagStreamCustomFormat = null;
        internal string speedFilter = null;
        internal string rssiFilter = null;
        internal string acqG2Target = null;

        internal MyStoredReaderState() { }
    }
}
