//                           UNCLASSIFIED
/*-------------------------------------------------------------------------
RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION
PROPRIETARY TO RAYTHEON COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS
AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. DISCLOSURE TO
UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO
RAYTHEON COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS
CONTENTS SHALL BE FURNISHED OR DISCLOSED TO OR COPIED OR USED BY PERSONS
OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF RAYTHEON
COMPANY.

THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S.
GOVERNMENT.

UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
-------------------------------------------------------------------------*/

using Raytheon.Common;
using System;

namespace BitMeasurementManagerLib
{
   /// <summary>
   /// Parses out messages that go between the VRSUI and the Test Controller
   /// This class is meant to be used by both the test controller and the VRSUI, so it parses out messages that target either of those systems
   /// </summary>
   internal class BitMsgParser : IMsgParser
   {
      #region PublicClassMembers

      #endregion

      #region PrivateClassMembers
      private readonly BitMessageIDs _messageIdToSizeMap;
      private BitMsgEndianControl.HeaderDef _headerDef;
      private readonly bool _shallWePerformVerboseParserLogging;
      #endregion

      #region PrivateFuctions

      /// <summary>
      /// 
      /// </summary>
      /// <param name="pData"></param>
      /// <param name="numBytesInPdata"></param>
      /// <param name="bytesToRemove"></param>
      /// <param name="messageId"></param>
      /// <param name="errorCode"></param>
      /// <returns></returns>
      private bool HandleData(IntPtr pData, uint numBytesInPdata, ref uint bytesToRemove, ref uint messageId, ref uint errorCode)
      {
         // check to see if we have enough data for at least a standard header
         uint payloadHeaderSize = BitConfigurableMessageHeader.GetHeaderSize();

         if (numBytesInPdata < payloadHeaderSize)
         {
            ErrorLogger.Instance().Write("BitMsgParser::HandleData() - not enough data in the buffer to form a header. Buffer contained " + Convert.ToString(numBytesInPdata) + " bytes, needed " + payloadHeaderSize.ToString() + " for a header", ErrorLogger.LogLevel.INFO);
            bytesToRemove = 0;
            return false;
         }

         uint id = BitConfigurableMessageHeader.GetMessageId(pData, numBytesInPdata);
         bool isSecondaryIdUsed = false;
         uint secondaryId = BitConfigurableMessageHeader.GetSecondaryMessageId(pData, numBytesInPdata, out isSecondaryIdUsed);

         // check that this is an expected message
         if (_messageIdToSizeMap.ContainsId(id) == false)
         {
            uint numBytesToRemove = Resync(pData, numBytesInPdata);
            string msg = "BitMsgParser::HandleData() - unknown id received: " + id.ToString("X8") + " When resyncing threw away " + numBytesToRemove.ToString() + " Bytes";
            ErrorLogger.Instance().Write(msg, ErrorLogger.LogLevel.ERROR);
            bytesToRemove = numBytesToRemove;
            return false;
         }

         // look at secondary ID if applicable
         if (isSecondaryIdUsed == true)
         {
            if (secondaryId != _headerDef.secondaryMsgIdExpectedValue)
            {
               uint numBytesToRemove = Resync(pData, numBytesInPdata);
               string msg = "BitMsgParser::HandleData() - detected seondary ID: " + secondaryId.ToString("X8") + " was not as expected: " + _headerDef.secondaryMsgIdExpectedValue.ToString("X8") + " When resyncing threw away " + numBytesToRemove.ToString() + " Bytes";
               ErrorLogger.Instance().Write(msg, ErrorLogger.LogLevel.ERROR);
               bytesToRemove = numBytesToRemove;
               return false;
            }
         }

         uint msgSize = _messageIdToSizeMap.GetSize(id);

         // do we have enough data to make the complete message
         if (numBytesInPdata < msgSize)
         {
            ErrorLogger.Instance().Write("BitMsgParser::HandleData() - not enough data in the buffer to form a entire message. Buffer contained " + Convert.ToString(numBytesInPdata) + " bytes, expected msg size is: " + msgSize.ToString(), ErrorLogger.LogLevel.INFO);
            // need to wait for more data
            bytesToRemove = 0;
            return false;
         }

         // everything has checked out, set the return params
         bytesToRemove = msgSize;

         if (_shallWePerformVerboseParserLogging == true)
         {
            ErrorLogger.Instance().Write("BitMsgParser::HandleData() - found msg: " + id.ToString() + " which has " + msgSize.ToString() + " bytes", ErrorLogger.LogLevel.INFO);
         }

         messageId = id;

         return true;
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="pData"></param>
      /// <param name="numBytesInPdata"></param>
      /// <returns>The number of bytes to remove from the buffer</returns>
      private uint Resync(IntPtr pData, uint numBytesInPdata)
      {
         ErrorLogger.Instance().Write("BitMsgParser::Resync() - Begin", ErrorLogger.LogLevel.INFO);

         // increment pData by 1
         pData = IntPtr.Add(pData, 1);

         // keep track of how many bytes to remove
         uint bytesToRemove = 1;

         // update bytes remaining
         numBytesInPdata = numBytesInPdata - 1;

         bool didWeFindMsg = false;

         while (didWeFindMsg == false)
         {
            // check to see if we have enough data for at least a standard header
            uint payloadHeaderSize = BitConfigurableMessageHeader.GetHeaderSize();

            if (numBytesInPdata < payloadHeaderSize)
            {
               ErrorLogger.Instance().Write("BitMsgParser::Resync() - not enough data in the buffer to form a header. Buffer contained " + Convert.ToString(numBytesInPdata) + " bytes, needed " + payloadHeaderSize.ToString() + " for a header. Removing " + bytesToRemove.ToString() + " bytes", ErrorLogger.LogLevel.INFO);
               break;
            }

            uint id = BitConfigurableMessageHeader.GetMessageId(pData, numBytesInPdata);
            bool isSecondaryIdUsed = false;
            uint secondaryId = BitConfigurableMessageHeader.GetSecondaryMessageId(pData, numBytesInPdata, out isSecondaryIdUsed);

            // check that this is an expected message
            if (_messageIdToSizeMap.ContainsId(id) == false)
            {
               // need to keep looking
               // increment pData by 1
               pData = IntPtr.Add(pData, 1);

               // keep track of how many bytes to remove
               bytesToRemove++;

               // update bytes remaining
               numBytesInPdata--;
            }
            else
            {
               // look at secondary ID if applicable
               if (isSecondaryIdUsed == true)
               {
                  if (secondaryId != _headerDef.secondaryMsgIdExpectedValue)
                  {
                     // need to keep looking
                     // increment pData by 1
                     pData = IntPtr.Add(pData, 1);

                     // keep track of how many bytes to remove
                     bytesToRemove++;

                     // update bytes remaining
                     numBytesInPdata--;
                  }
                  else
                  {
                     didWeFindMsg = true;
                     ErrorLogger.Instance().Write("BitMsgParser::Resync() - Detected message ID: " + id.ToString("X8") + ". Detected Secondary ID: " + secondaryId.ToString("X8") + " Resync complete. Removing " + bytesToRemove.ToString() + " Bytes", ErrorLogger.LogLevel.INFO);
                     break;
                  }
               }
               else
               {
                  didWeFindMsg = true;
                  ErrorLogger.Instance().Write("BitMsgParser::Resync() - Detected message ID: " + id.ToString("X8") + ". Resync complete. Removing " + bytesToRemove.ToString() + " Bytes", ErrorLogger.LogLevel.INFO);
                  break;
               }
            }
         }

         ErrorLogger.Instance().Write("BitMsgParser::Resync() - returning " + bytesToRemove.ToString(), ErrorLogger.LogLevel.INFO);

         return bytesToRemove;

      }
      #endregion

      #region PublicFuctions

      /// <summary>
      /// 
      /// </summary>
      /// <param name="messageIds"></param>
      /// <param name="shallWePerformVerboseParserLogging"></param>
      public BitMsgParser(BitMessageIDs messageIds, bool shallWePerformVerboseParserLogging)
      {
         // Using it to hold onto valid message ids and the size
         _messageIdToSizeMap = messageIds;
         _shallWePerformVerboseParserLogging = shallWePerformVerboseParserLogging;
         _headerDef = BitMsgEndianControl.Instance().GetHeaderDef();
      }

      /// <summary>
      /// Search the data buffer for the next valid message
      /// </summary>
      /// <param name="pData">The data buffer to search</param>
      /// <param name="numBytesInPdata">The number of bytes in pData</param>
      /// <param name="bytesToRemove">The number of bytes to remove from the buffer</param>
      /// <param name="messageId">The message id of the message that was found</param>
      /// <returns>true if a message was found, false otherwise</returns>
      public bool Run(IntPtr pData, uint numBytesInPdata, ref uint bytesToRemove, ref uint messageId, ref uint errorCode)
      {
         // default error code to 0;
         errorCode = 0;

         return HandleData(pData, numBytesInPdata, ref bytesToRemove, ref messageId, ref errorCode);
      }
      #endregion
   }
}
