/*
 *****************************************************************************
 *                                                                           *
 *              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.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using Impinj.STP;


namespace Impinj
{
    /// <summary>
    /// The ItemEncodeSerialPoert class is used to capture the Tag Operation
    /// Reports from an ItemEncode device GPIO port serial interface on an
    /// itemEncode device that is configured to produce Tag Operation
    /// Reports via the serial port and output them as an event.
    /// </summary>
    class ItemEncodeSerialPort
    {
        // TagOperationReportReceived event that is used to publish the
        // Tag Operation Report contents
        public event EventHandler<TagOperationReportReceivedEventArgs> TagOperationReportReceived;

        // The seral port object
        private SerialPort serialPort;

        // Default Constructor
        public ItemEncodeSerialPort()
        {
            serialPort = new SerialPort();

            // Bind handlers to the Data and Error received events
            serialPort.DataReceived += DataReceivedHandler;
            serialPort.ErrorReceived += ErrorReceivedHandler;
        }

        // Open the serial port with the default ItemEncode device settings.
        public void Open(string portName)
        {
            try
            {
                serialPort.PortName = portName;
                serialPort.BaudRate = 115200;
                serialPort.DataBits = 8;
                serialPort.Parity = Parity.None;
                serialPort.StopBits = StopBits.One;
                serialPort.Handshake = Handshake.None;

                serialPort.ReceivedBytesThreshold = 10;
                serialPort.NewLine = "\r\n";
                serialPort.ReadTimeout = 35;

                serialPort.Open();
                serialPort.DiscardInBuffer();
            }
            catch
            {
                throw;
            }
        }

        // Close the serial port
        public void Close()
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
            }
        }

        // Handler that is bound to the serial port DataReceived event
        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                // Read the data in the serial port buffer
                string serialData = ((SerialPort)sender).ReadLine();

                // Break up the comma delimited serial data into fields
                string[] fields = serialData.Trim().Split(',');

                
                // Create the object to populate with data
                TagOperationReportReceivedEventArgs tagOperationReport = 
                    new TagOperationReportReceivedEventArgs();

                // Extract the signature field
                tagOperationReport.Signature = fields[0].Trim();

                // Determine whether the Tag Operation Report type
                switch (fields[1].Trim())
                {
                    case "E":
                        tagOperationReport.TagOperationReportType =
                            TagOperationReportType.TAG_OPERATION_REPORT_TYPE_ENCODE;
                        break;
                    case "V":
                        tagOperationReport.TagOperationReportType =
                            TagOperationReportType.TAG_OPERATION_REPORT_TYPE_INVENTORY;
                        break;
                    default:
                        break;
                }

                // Determine the Tag Operation Report result
                switch (fields[2].Trim())
                {
                    case "P":
                        tagOperationReport.TagOperationResult = 
                            TagOperationResult.TAG_OPERATION_RESULT_PASS;
                        break;
                    case "F":
                        tagOperationReport.TagOperationResult = 
                            TagOperationResult.TAG_OPERATION_RESULT_FAIL;
                        break;
                    default:
                        break;
                }

                // Copy the TID and EPC values, if they are non-null
                tagOperationReport.Tid = fields[3].Trim() ?? string.Empty;
                tagOperationReport.Epc = fields[4].Trim() ?? string.Empty;

                // Issue the TagOperationReportReceived event if there is a
                // handler bound to it
                if (null != TagOperationReportReceived)
                {
                    TagOperationReportReceived(this, tagOperationReport);
                }

            }
            // Exception handlers
            catch (TimeoutException tex)
            {
                Console.WriteLine(
                    "Timeout Exception in DataReceivedHandler: {0}",
                    tex.Message.ToString()
                    );
            }
            catch (Exception ex)
            {
                string ex_msg = string.Format("Exception in DataReceivedHandler: {0}{1}",
                    ex.Message,
                    (null == ex.InnerException) ? string.Empty : Environment.NewLine + ex.InnerException.Message
                    );
                Console.WriteLine("Exception: {0}", ex_msg);
            }

        }

        // Handler that is bound to the serial port ErrorReceived event
        private void ErrorReceivedHandler(object sender, SerialErrorReceivedEventArgs e)
        {
            Console.WriteLine(
                "Error Received Handler: {0}",
                e.EventType.ToString()
                );
        }
    }
}
