/*
 *****************************************************************************
 *                                                                           *
 *              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.Sockets;
using System.Text;
using Impinj.STP;

namespace Impinj
{
    class Program
    {
        // Private data member declarations
        private static Device ItemEncodeDevice = new Device();
        private static string SpeedwayRHostname = string.Empty;

        // Main routine
        static void Main(string[] args)
        {
            // Local variables
            string userInput;

            // Initialize console window
            InitializeConsoleWindow();

            // Determine whether Speedway Revolution hostname has been provided
            // on the command line, or not
            switch (args.Length)
            {
                case 0:
                    // Use the Speedway Revolution hostname defined in the
                    // SolutionsConstants class of the SolutionItems project
                    SpeedwayRHostname = SolutionConstants.SpeedwayRHostname;
                    break;
                case 1:
                    // If the input parameter is a valid string, use it
                    // as the hostname
                    if (false == string.IsNullOrEmpty(args[0]))
                    {
                        SpeedwayRHostname = args[0];
                    }
                    // Otherwise print a usage message and exit
                    else
                    {
                        PrintUsage();
                    }
                    break;
                default:
                    // For all other cases, print a usage message and exit
                    PrintUsage();
                    break;
            }

            // Print brief usage statement
            Console.WriteLine(
                "Send one or more test results (press Ctrl+Z to exit):" +
                Environment.NewLine
                );
            Console.WriteLine("[TID],[true = send pass result/false = send fail result]");
            Console.WriteLine("E2801160200041E796B8FFFF,true" +
                Environment.NewLine);

            // Main user interaction loop
            do
            {
                // Present a command line indicator
                Console.Write("> ");
                // Read user input
                userInput = Console.ReadLine();

                // Continue ony if user input is not null (i.e. Ctrl+Z)
                if (null != userInput)
                {
                    string[] testResultData;

                    // Parse input as either comma or space delimited commands
                    if (userInput.Contains(", "))
                    {
                        testResultData = userInput.Replace(", ", ",").Split(new Char[] { ',', ' ' });
                    }
                    else
                    {
                        testResultData = userInput.Split(new Char[] { ',', ' ' });
                    }

                    // Do a rudimentary check to ensure the user command is
                    // well formed
                    if (true == isValidCommand(testResultData))
                    {
                        // Summarize user commands
                        Console.WriteLine(
                            "  Sending {0}, {1}\r\n",
                            testResultData[0].ToString(),
                            testResultData[1].ToString()
                            );

                        // Send the packet to the specified device
                        SendPacket(
                            SpeedwayRHostname,
                            testResultData[0].ToString(),
                            Convert.ToBoolean(testResultData[1].ToString())
                            );
                    }
                    else
                    {
                        Console.WriteLine("  Invalid command.");
                    }
                }
            // Exit when Ctrl+Z is pressed
            } while (userInput != null);
        }

        // Returnes a true is the provided command consists of 2 entries, and 
        // that both are well-formed. Otherwise it returns a false.
        private static bool isValidCommand(string[] commands)
        {
            bool valid = false;

            // Are there 2 commands
            if (2 == commands.Length)
            {
                // Is the user provided a 24 character TID for the first
                // parameter?
                if (24 == commands[0].Length)
                {
                    // Is the TID only Hexadecimal characters?
                    if (true == OnlyHexInString(commands[0]))
                    {
                        // Has the user provided a true or false for the
                        // second parameter?
                        if ("true" == commands[1] || "false" == commands[1])
                        {
                            // Everything checks out, so flag a valid command
                            valid = true;
                        }
                    }
                }
            }

            return valid;
        }

        // Utility function for checking that a string contains only
        // Hexadecimal characters
        public static bool OnlyHexInString(string InputString)
        {
            bool returnResult = false;
            StringBuilder SourceString = new StringBuilder();

            if (false == String.IsNullOrEmpty(InputString))
            {
                // Append the input string to the SourceString work variable
                SourceString.Append(InputString);

                // Remove any prefix characters
                SourceString.Replace("0x", string.Empty);

                // Next, remove any space or dash delimiters
                SourceString.Replace(" ", string.Empty);
                SourceString.Replace("-", string.Empty);

                returnResult =
                    System.Text.RegularExpressions.
                    Regex.IsMatch(SourceString.ToString(), @"\A\b[0-9a-fA-F]+\b\Z");
            }
            else
            {
                throw new ArgumentNullException();
            }

            return returnResult;
        }

        // Build a UDP data packet in the correct format for a Voyantic
        // Tagsurance ItemEncode test result and sent it to
        // the the specified ItemEncode device
        private static void SendPacket(String readerAddress, String tid, bool sendTagPassResult)
        {
            UdpClient client = new UdpClient();
            const int kNumBytesInMessage = 18;
            ByteArray tidByteArray = new ByteArray(tid);

            // Limit the TID reported to the ItemEncode device to only 96-bits
            byte[] tidBytes = tidByteArray.ToBytes();
            if (tidBytes.Length > 12)
            {
                Array.Resize(ref tidBytes, 12);
            }

            // Create the buffer to insert the data into
            int i = 0;
            byte[] sendBuffer = new byte[kNumBytesInMessage];

            // Insert the bytes that identify this as a Voyantic Tagsurance
            // to ItemEncode test data packet ("vtie")
            sendBuffer[i++] = Convert.ToByte('v');
            sendBuffer[i++] = Convert.ToByte('t');
            sendBuffer[i++] = Convert.ToByte('i');
            sendBuffer[i++] = Convert.ToByte('e');

            // Insert the test result; 0x0 = fail, 0x1 = pass
            sendBuffer[i++] = (sendTagPassResult) ? (byte)0x1 : (byte)0x0;

            // Insert the TID data 2 bytes at a time, reversing the order of
            // the words
            sendBuffer[i++] = (byte)(tidBytes.Length / 2);

            for (int tidBytesIndex = 0; tidBytesIndex < tidBytes.Length; tidBytesIndex += 2)
            {
                sendBuffer[i++] = tidBytes[tidBytesIndex + 1];
                sendBuffer[i++] = tidBytes[tidBytesIndex];
            }

            // Send the data packet to the ItemEncode device
            client.Send(sendBuffer, sendBuffer.Length, readerAddress, 36364);
        }

        // Print a brief usage statement and then exit the application
        private static void PrintUsage()
        {
            Console.WriteLine("Usage: VoyanticTagsuranceTestResultSender <deviceName>");
            Console.WriteLine("\r\nPress any key to exit . . .");
            Console.ReadKey(true);
            Environment.Exit(-1);
        }

        // Utility procedure for changing console window configuration,
        // including the window title
        private static void InitializeConsoleWindow()
        {
            Console.Clear();
            Console.Title = "Impinj ItemEncode - Voyantic Tagsurance Test Result Sender";
            Console.SetWindowSize(150, 50);
            Console.BufferHeight = 1000;
        }

    }
}

