/*
 *****************************************************************************
 *                                                                           *
 *              IMPINJ CONFIDENTIAL AND PROPRIETARY                          *
 *                                                                           *
 * This source code is the sole property of Impinj, Inc. Reproduction or     *
 * utilization of this source code in whole or in part is forbidden without  *
 * the prior written consent of Impinj, Inc.                                 *
 *                                                                           *
 * (c) Copyright Impinj, Inc. 2015. All rights reserved.                     *
 *                                                                           *
 *****************************************************************************/
using System;
using System.Net;
using System.Text;
using System.Windows;
using System.Windows.Input;
using Impinj.STP;

namespace Impinj
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        // Constants
        const string ReaderHostnameDefault = @"SpeedwayR-10-XX-XX";
        const string VolumeLicenseFieldDefault = @"Paste volume license key here";

        // Data members
        private string _SpeedwayRHostname = ReaderHostnameDefault;
        private bool ReaderHostnameChanged = false;
        private string _VolumeLicenseField = VolumeLicenseFieldDefault;
        private bool VolumeLicenseFieldChanged = false;
        private Device ItemEncodeVerifyDevice;

        // Define a mutable hostname type
        public string SpeedwayRHostname
        {
            get { return _SpeedwayRHostname; }
            set
            {
                if (value != ReaderHostnameDefault)
                {
                    _SpeedwayRHostname = value;
                    ReaderHostnameChanged = true;
                }
            }
        }

        // Define a mutable volume license type
        public string VolumeLicenseField
        {
            get { return _VolumeLicenseField; }
            set
            {
                if (value != VolumeLicenseFieldDefault)
                {
                    _VolumeLicenseField = value;
                    VolumeLicenseFieldChanged = true;
                }
            }
        }

        // Main routine for the application
        public MainWindow()
        {
            InitializeComponent();
            ReaderHostnameTextBox.Text = ReaderHostnameDefault;
            licenseTextBox.Text = VolumeLicenseField;
        }

        // Procedure that is called when the getOperationData button is clicked
        private void getOperationDataButton_Click(object sender, RoutedEventArgs e)
        {
            // Call the doGetOperationData() procedure to do the work
            doGetOperationData();
        }

        // Main routine for getting the Operation Data from the
        // ItemEncodeVerify device
        private void doGetOperationData()
        {
            // First, extract the reader hostname from the GUI
            SpeedwayRHostname = ReaderHostnameTextBox.Text;

            // If the SpeedwayR hostname has not been changed, let
            // the user know that it has to be updated.
            if (false == ReaderHostnameChanged)
            {
                MessageBox.Show(
                    "You must enter a valid hostname",
                    "Invalid hostname",
                    MessageBoxButton.OK,
                    MessageBoxImage.Exclamation
                    );

                return;
            }
            else
            {
                try
                {
                    // Declare an array of IPAddresses that is used for the
                    // hostname lookup
                    IPAddress[] ips;

                    // The device.NetConnect() call below takes a significant
                    // amount of time to return if the hostname is not correct,
                    // so check to see whether it can be resolved via DNS
                    ips = Dns.GetHostAddresses(SpeedwayRHostname);
                }
                catch
                {
                    // If an exception has occurred, it means that the hostname
                    // is probably incorrect, so flag this to the user
                    MessageBox.Show(
                        "Please enter a valid Speedway Revolution hostname",
                        "Invalid hostname",
                        MessageBoxButton.OK,
                        MessageBoxImage.Exclamation
                        );
                }

                // If we have gotten here, then the hostname is valid
                try
                {
                    // Create a new Device object to talk with the
                    // ItemEncodeVerify device
                    ItemEncodeVerifyDevice = new Device();
                    // Establish a network connection with the device
                    ItemEncodeVerifyDevice.NetConnect(SpeedwayRHostname);

                    // Get the OperationData from the ItemEncodeVerify device
                    OperationData operationData = ItemEncodeVerifyDevice.GetOperationData();

                    // Present the results in the GUI Console output (top TextBox)
                    OutputToGuiConsole(GenerateOperationDataReport(operationData, SpeedwayRHostname));

                    // Disconnect from the ItemEncodeVerify device
                    ItemEncodeVerifyDevice.NetDisconnect();
                }
                catch (Exception ex)
                {
                    // An exception has occurred, so inform the user
                    string ex_msg = string.Format(
                        "Exception getting OperationData:{0}{1}{2}",
                        Environment.NewLine,
                        ex.Message,
                        (null == ex.InnerException) ? string.Empty : Environment.NewLine + ex.InnerException.Message
                        );

                    MessageBox.Show(
                        ex_msg,
                        "Error getting OperationData",
                        MessageBoxButton.OK,
                        MessageBoxImage.Exclamation
                        );

                    // Disconnect from the ItemEncodeVerify device
                    ItemEncodeVerifyDevice.NetDisconnect();
                }
            }
        }

        // Present the data to the GUI Console
        private string GenerateOperationDataReport(OperationData operationData, string reader)
        {
            // Declare the object that will be populated with the OperationData
            // details
            StringBuilder odr = new StringBuilder();

            // First, add the basic information that is always returned
            // The device readername
            odr.AppendFormat("Device:\t\t\t{0}{1}",
                reader,
                Environment.NewLine);
            // The ItemEncodeVerify firmware version
            odr.AppendFormat("Version:\t\tItemEncodeVerify {0}{1}",
                operationData.OperationDetails.FirmwareVersion,
                Environment.NewLine);
            // The Speedway Revolution serial number
            odr.AppendFormat("Serial Number:\t\t{0}{1}",
                operationData.OperationDetails.SerialNumber.Replace("-", ""),
                Environment.NewLine);
            // The Speedway Revolution regulatory region
            odr.AppendFormat("Regulatory Region:\t{0}{1}",
                operationData.OperationDetails.Region,
                Environment.NewLine);

            // Append a list of the license keys that have been activated
            odr.AppendFormat("{0}Activated License Keys:{0}",
                Environment.NewLine);

            foreach (string licenseKey in operationData.OperationDetails.InstalledFeatures)
            {
                odr.AppendFormat("\t{0}{1}", licenseKey, Environment.NewLine);
            }

            // Add the metrics specific to each tag chip type, as
            // identified by the first 32-bits of the TID
            odr.AppendFormat("{0}Operation Metrics, Per Tag Chip Type:{0}",
                Environment.NewLine);
            foreach (TagMetrics tagMetrics in operationData.OperationDetails.OperationMetrics.TagMetrics)
            {
                // Output the tag chip type for the current set of metrics 
                odr.AppendLine(string.Format("{1}TID prefix:\t{0}",
                    tagMetrics.TagChip.ToHexString(),
                    Environment.NewLine
                    ));

                // Output a header to the data
                odr.AppendLine(string.Format(
                    "\t\t\t\tTotal\t\tPass\t\tFail\t\tVoid"
                    ));
                odr.AppendLine(string.Format("{0}",
                    GenerateUnderline(78)
                    ));

                // If present, add the metrics attributable to running
                // in Inventory device mode
                if (tagMetrics.InventoryMetrics != null)
                {
                    odr.AppendFormat("Inventory:\t\t\t{0}\t\t{1}\t\t{2}{3}",
                        tagMetrics.InventoryMetrics.TotalCount,
                        tagMetrics.InventoryMetrics.PassCount,
                        tagMetrics.InventoryMetrics.FailCount,
                        Environment.NewLine);
                }

                // If present, add the metrics attributable to running
                // in Verify Tag Chip Basic device mode
                if (tagMetrics.VerifyBasicMetrics != null)
                {
                    ulong verifyBasicVoidCount = (tagMetrics.VerifyBasicMetrics.VoidCount != null) ? tagMetrics.VerifyBasicMetrics.VoidCount.Value : 0;

                    odr.AppendFormat("Verify Tag Chip Basic:\t{0}\t\t{1}\t\t{2}\t\t{3}{4}",
                        tagMetrics.VerifyBasicMetrics.TotalCount,
                        tagMetrics.VerifyBasicMetrics.PassCount,
                        tagMetrics.VerifyBasicMetrics.FailCount,
                        verifyBasicVoidCount,
                        Environment.NewLine);
                }

                // If present, add out the metrics attributable to running
                // in Encode device mode
                if (tagMetrics.EncodeMetrics != null)
                {
                    ulong encodeVoidCount = (tagMetrics.EncodeMetrics.VoidCount != null) ? tagMetrics.EncodeMetrics.VoidCount.Value : 0;

                    odr.AppendFormat("Encode:\t\t\t\t{0}\t\t{1}\t\t{2}\t\t{3}{4}",
                        tagMetrics.EncodeMetrics.TotalCount,
                        tagMetrics.EncodeMetrics.PassCount,
                        tagMetrics.EncodeMetrics.FailCount,
                        encodeVoidCount,
                        Environment.NewLine);
                }

                // If present, add the metrics attributable to running
                // in Verify Tag Chip Data device mode
                if (tagMetrics.VerifyDataMetrics != null)
                {
                    ulong verifyDataVoidCount = (tagMetrics.VerifyDataMetrics.VoidCount != null) ? tagMetrics.VerifyDataMetrics.VoidCount.Value : 0;

                    odr.AppendFormat("Verify Tag Chip Data:\t{0}\t\t{1}\t\t{2}\t\t{3}{4}",
                        tagMetrics.VerifyDataMetrics.TotalCount,
                        tagMetrics.VerifyDataMetrics.PassCount,
                        tagMetrics.VerifyDataMetrics.FailCount,
                        verifyDataVoidCount,
                        Environment.NewLine);
                }

                // If present, add the metrics attributable to running
                // in ItemVerify device mode
                if (tagMetrics.CartonContentsMetrics != null)
                {
                    odr.AppendFormat("Carton Contents:\t\t{0}\t\t{1}\t\t{2}{3}",
                        tagMetrics.CartonContentsMetrics.TotalCount,
                        tagMetrics.CartonContentsMetrics.PassCount,
                        tagMetrics.CartonContentsMetrics.FailCount,
                        Environment.NewLine);
                }

                // Add a table footer
                odr.AppendLine(string.Format("{0}",
                    GenerateUnderline(78)
                    ));
            }

            // Add the Monza Volume licensing details
            odr.AppendFormat("{0}Monza Encoding:{0}",
                Environment.NewLine);
            odr.AppendFormat("\tCurrent Monza Volume Key ID:\t\t{0}{1}",
                operationData.OperationDetails.MonzaVolumeKeyId.ToString(),
                Environment.NewLine);
            odr.AppendFormat("\tRemaining Monza Encodes:\t\t{0}{1}",
                operationData.OperationDetails.OperationMetrics.RemainingMonzaEncodes.ToString(),
                Environment.NewLine);

            // Add the Non-Monza Volume licensing details
            odr.AppendFormat("{0}Non-Monza Encoding:{0}",
                Environment.NewLine);
            odr.AppendFormat("\tCurrent Non-Monza Volume Key ID:\t{0}{1}",
                operationData.OperationDetails.NonMonzaVolumeKeyId.ToString(),
                Environment.NewLine);
            odr.AppendFormat("\tRemaining Non-Monza Encodes:\t\t{0}{1}",
                operationData.OperationDetails.OperationMetrics.RemainingNonMonzaEncodes.ToString(),
                Environment.NewLine);

            // If present, add any Inter Device Communication metrics
            odr.AppendFormat("{0}Inter Device Communication Metrics:{0}",
                Environment.NewLine);

            if (operationData.OperationDetails.OperationMetrics.InterDeviceCommunicationMetrics.Count > 0)
            {
                foreach (InterDeviceCommunicationMetrics idcm in operationData.OperationDetails.OperationMetrics.InterDeviceCommunicationMetrics)
                {
                    odr.AppendFormat("\t{0}:", idcm.ToString());
                }
            }
            else
            {
                odr.AppendLine("\t<no inter device communication metrics>");
            }

            // Add the contents of the OperationData digest
            odr.AppendFormat("{0}Operation Data Digest:{0}",
                Environment.NewLine);
            foreach (String data in operationData.Data)
            {
                odr.AppendFormat("\tData:\t\"{0}\"{1}",
                    data,
                    Environment.NewLine);
            }

            // Indicate that Get OperationData operation is complete
            odr.AppendFormat(
                "{1}Get OperationData Complete. ItemVerify device {0} disconnected.{1}",
                SpeedwayRHostname,
                Environment.NewLine
            );

            // Return the constructed StringBuilder object as a string
            return odr.ToString();
        }

        // Utility function to generate a string of '-' characters of the
        // specified length
        private string GenerateUnderline(int length)
        {
            return new string('-', length);
        }

        // Utility function for presenting data in the GUI Console TextBox
        private void OutputToGuiConsole(string message)
        {
            consoleTextBox.AppendText(message.ToString() + Environment.NewLine);
            consoleTextBox.ScrollToEnd();
        }

        // Procedure that is run when the Load Volume button is pressed
        private void loadVolumeLicenseButton_Click(object sender, RoutedEventArgs e)
        {
            // First, extract the reader hostname from the GUI
            SpeedwayRHostname = ReaderHostnameTextBox.Text;

            // Next, extract the volume license key from the GUI
            VolumeLicenseField = licenseTextBox.Text.Trim();

            // The default hostname is not a valid SpeedwayR hostname, so let
            // the user know that it has to be updated.
            if (false == ReaderHostnameChanged)
            {
                MessageBox.Show(
                    "You must enter a valid hostname",
                    "Invalid hostname",
                    MessageBoxButton.OK,
                    MessageBoxImage.Exclamation
                    );

                return;
            }
            else
            {
                try
                {
                    // Declare an array of IPAddresses that is used for the
                    // hostname lookup
                    IPAddress[] ips;

                    // The device.NetConnect() call below takes a significant
                    // amount of time to return if the hostname is not correct,
                    // so check to see whether it can be resolved via DNS
                    ips = Dns.GetHostAddresses(SpeedwayRHostname);
                }
                catch
                {
                    // If an exception has occurred, it means that the hostname
                    // is probably incorrect, so flag this to the user
                    MessageBox.Show(
                        "Please enter a valid Speedway Revolution hostname",
                        "Invalid hostname",
                        MessageBoxButton.OK,
                        MessageBoxImage.Exclamation
                        );
                    return;
                }
            }

            try
            {
                if (false == VolumeLicenseFieldChanged)
                {
                    MessageBox.Show(
                        "You must enter a Volume License key",
                        "Volume License key required",
                        MessageBoxButton.OK,
                        MessageBoxImage.Exclamation
                        );

                    return;
                }
                else
                {
                    // Create a new Device object to talk with the
                    // ItemEncodeVerify device
                    ItemEncodeVerifyDevice = new Device();
                    // Establish a network connection with the device
                    ItemEncodeVerifyDevice.NetConnect(SpeedwayRHostname);

                    // Create a LicenseKey object
                    LicenseKey volumeLicenseKey = new LicenseKey();

                    // Report status to GUI Console field
                    consoleTextBox.Clear();
                    consoleTextBox.AppendText(string.Format(
                        "Loading Volume License to reader {0} ... ",
                        ReaderHostnameTextBox.Text
                        ));

                    // Create the license key object payload
                    volumeLicenseKey.LicenseKeyPayload = 
                        new ByteArray(Encoding.ASCII.GetBytes(VolumeLicenseField));

                    // Add the volume license key to the ItemEncodeVerify device
                    ItemEncodeVerifyDevice.AddLicenseKey(volumeLicenseKey);

                    // Report to the GUI that the operation is complete
                    consoleTextBox.AppendText("Done.");

                    // Disconnect the ItemEncodeVerify device
                    ItemEncodeVerifyDevice.NetDisconnect();
                }

            }
            catch (Exception ex)
            {
                // An exception has occurred, so inform the user
                string ex_msg = string.Format(
                    "Exception setting Volume License:{0}{1}{2}",
                    Environment.NewLine,
                    ex.Message,
                    (null == ex.InnerException) ? string.Empty : Environment.NewLine + ex.InnerException.Message
                    );

                MessageBox.Show(
                    ex_msg,
                    "Error setting Volume License",
                    MessageBoxButton.OK,
                    MessageBoxImage.Exclamation
                    );

                // Disconnect the ItemEncodeVerify device
                ItemEncodeVerifyDevice.NetDisconnect();
            }
        }

        // Utility function used to get the Operation Data when the Enter key
        // is pressed on entering the Speedway Revolution hostname
        private void OnKeyDownHandler(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Return)
            {
                doGetOperationData();
            }
        }
    }
}
