//                           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 System;

namespace FpgaMeasurementInstrumentsLib
{
   /// <summary>
   /// An abstract base class for FPGACmdMessages that go between the client and server
   /// </summary>
   internal abstract class FPGACmdMessage : ICloneable
   {
      #region PublicClassMembers
      public enum Page
      {
         PAGE0,
         PAGE1,
         PAGE2,
         PAGE3
      }
      #endregion

      #region PrivateClassMembers
      private readonly string m_description;

      protected FPGACmdMessageHeader m_header;

      protected static void SwapWord(ref byte[] input)
      {
         //Word Size
         int WORD_SIZE = 4;

         for (int i = 0; i < input.Length; i = i + WORD_SIZE)
         {
            //Temp array for swap
            byte[] temp = new byte[WORD_SIZE];

            //Copy a word into temp
            Buffer.BlockCopy(input, i, temp, 0, WORD_SIZE);

            //Swap bytes
            Array.Reverse(temp);

            //Replace word
            Buffer.BlockCopy(temp, 0, input, i, WORD_SIZE);
         }
      }
      #endregion

      #region PrivateFuctions
      /// <summary>
      /// A command message constructor for all children to invoke
      /// </summary>
      /// <param name="commandType">The command message id</param>
      /// <param name="page">The page of the command message</param>
      /// <param name="description">The description of the command message</param>
      protected FPGACmdMessage(FPGACmdMsgIds commandType, Page page, string description)
      {
         try
         {
            m_description = description;
            m_header = new FPGACmdMessageHeader(commandType, page);
         }
         catch (Exception)
         {
            throw;
         }
      }

      /// <summary>
      /// Copy Constructor
      /// </summary>
      /// <param name="FPGACmdMessage">The FPGACmdMessage to copy from</param>
      protected FPGACmdMessage(FPGACmdMessage FPGACmdMessage)
      {
         try
         {
            m_header = new FPGACmdMessageHeader(FPGACmdMessage.m_header);
            m_description = FPGACmdMessage.m_description;
         }
         catch (Exception)
         {
            throw;
         }
      }

      /// <summary>
      /// A clone function for the children to implement
      /// </summary>
      /// <returns>a clone of the child object</returns>
      protected abstract object CloneSelf();

      /// <summary>
      /// A function to encode outgoing data
      /// </summary>
      /// <param name="pData">a pointer to the encoded data</param>
      protected abstract void FormatData(IntPtr pData);

      /// <summary>
      /// 
      /// </summary>
      /// <returns></returns>
      protected abstract int GetPayloadSize();

      /// <summary>
      /// a function to decode incoming data
      /// </summary>
      /// <param name="pData">A pointer to data to populate this object with</param>
      protected abstract void ParseData(IntPtr pData);
      #endregion

      #region PublicFuctions

      /// <summary>
      /// Get a copy of the FPGACmdMessage object
      /// </summary>
      /// <returns>A clone of this object</returns>
      public object Clone()
      {
         // tell the child to clone itself
         return this.CloneSelf();
      }

      /// <summary>
      /// Encode the FPGACmdMessage into a byte array for sending
      /// </summary>
      /// <param name="pData">The buffer to put the FPGACmdMessage items</param>
      public void Format(IntPtr pData)
      {
         try
         {
            m_header.Format(pData);

            IntPtr pPayload = IntPtr.Add(pData, (int)GetHeaderLength());

            // ask child class to format its data
            FormatData(pPayload);
         }
         catch (Exception)
         {
            throw;
         }
      }

      /// <summary>
      /// Getter for the description
      /// </summary>
      /// <returns>The description</returns>
      public string GetDescription()
      {
         return m_description;
      }

      /// <summary>
      /// getter for the message id
      /// </summary>
      /// <returns>The id</returns>
      public FPGACmdMsgIds GetMessageId()
      {
         return m_header.GetMessageId();
      }

      /// <summary>
      /// getter for the header length
      /// </summary>
      /// <returns>The number of bytes in the header</returns>
      public uint GetHeaderLength()
      {
         return m_header.GetHeaderLength();
      }

      /// <summary>
      /// 
      /// </summary>
      /// <returns></returns>
      public uint GetEntireMsgLength()
      {
         return m_header.GetHeaderLength() + (uint)GetPayloadSize();
      }

      /// <summary>
      /// Takes an array of bytes and populates the FPGACmdMessage object
      /// </summary>
      /// <param name="pData">The FPGACmdMessage in byte form</param>
      public void Parse(IntPtr pData)
      {
         try
         {
            m_header.Parse(pData);

            IntPtr pPayLoad = IntPtr.Add(pData, (int)GetHeaderLength());

            ParseData(pPayLoad);
         }
         catch (Exception)
         {
            throw;
         }
      }

      /// <summary>
      /// Convert this FPGACmdMessage into string form
      /// </summary>
      /// <returns>The FPGACmdMessage in string form</returns>
      public override string ToString()
      {
         string desc = "Description: " + GetDescription() + "\n" + m_header.ToString();

         return desc;
      }
      #endregion
   }
}
