// **********************************************************************************************************
//    coeMessage.cs
//    6/1/2022
//    NGI - Next Generation Interceptor
//    
//    Contract No. HQ0856-21-C-0003/1022000209
//    
//    THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S. 
//    INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//    
//    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.
//    
//    UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//    
//    DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M, 
//    NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006, 
//    INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3, 
//    DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3, 
//    SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4, 
//    INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//    
//    CONTROLLED BY: MISSILE DEFENSE AGENCY
//    CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
//    CUI CATEGORY: CTI 
//    DISTRIBUTION/DISSEMINATION CONTROL: F 
//    POC: Alex Kravchenko (1118268)
// **********************************************************************************************************

//\\<Unclassified>
//----------------------------------------------------------------------------//
//                                 UNCLASSIFIED                               //
//----------------------------------------------------------------------------//
//\\<\Unclassified>

//\\<UnlimitedRights>
//----------------------------------------------------------------------------//
// Copyright %(copyright)s Raytheon Company.                                  //
// This software was developed pursuant to Contract Number %(contractNumber)s //
// with the U.S. government. The U.S. government's rights in and to this      //
// copyrighted software are as specified in DFARS 252.227-7014 which was made //
// part of the above contract.                                                //
//----------------------------------------------------------------------------//
//\\<\UnlimitedRights>

//\\<EximUndetermined>
//----------------------------------------------------------------------------//
// WARNING - This document contains technical data and / or technology whose  //
// export or disclosure to Non-U.S. Persons, wherever located, is restricted  //
// by the International Traffic in Arms Regulations (ITAR) (22 C.F.R.         //
// Section 120-130) or the Export Administration Regulations (EAR) (15 C.F.R. //
// Section 730-774). This document CANNOT be exported (e.g., provided to a    //
// supplier outside of the United States) or disclosed to a Non-U.S. Person,  //
// wherever located, until a final jurisdiction and classification            //
// determination has been completed and approved by Raytheon, and any         //
// required U.S. Government approvals have been obtained. Violations are      //
// subject to severe criminal penalties.                                      //
//----------------------------------------------------------------------------//
//\\<\EximUndetermined>


using System;
using System.Runtime.InteropServices;

namespace Raytheon.Common.Coe
{
   /// <summary>
   /// Message Attributes
   /// </summary>
   [AttributeUsage(AttributeTargets.All)]
   public class MessageOffset : Attribute
   {
      readonly uint _bufferOffset;
      public MessageOffset(uint bufferOffset)
      {
         _bufferOffset = bufferOffset;
      }
      public uint Offset
      {
         get { return _bufferOffset; }
      }
   }
   [AttributeUsage(AttributeTargets.All)]
   public class MessageSize : Attribute
   {
      readonly int _bufferSize;
      public MessageSize(int bufferSize)
      {
         _bufferSize = bufferSize;
      }
      public int Size
      {
         get { return _bufferSize; }
      }
   }

   /// <summary>
   /// Message: base class
   /// </summary>
   public abstract class coeMessage : IDisposable
   {
      #region DLLImports
      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_Create")]
      private static extern IntPtr OE_Message_Create(IntPtr Buffer_Address,
                                                     uint Size,
                                                     // Not used in new verion of dll.
                                                     //int Create_Shared,
                                                     //IntPtr ApplicationContext,
                                                     out coe.Status Status);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_Delete")]
      private static extern coe.Status OE_Message_Delete(IntPtr Handle);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_set_Priority")]
      private static extern coe.Status OE_Message_set_Priority(IntPtr Handle,
                                                               int Priority);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_get_Priority")]
      private static extern int OE_Message_get_Priority(IntPtr Handle,
                                                        out coe.Status Status);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_set_Domain")]
      private static extern coe.Status OE_Message_set_Domain(IntPtr Handle,
                                                             uint Domain);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_get_Domain")]
      private static extern uint OE_Message_get_Domain(IntPtr Handle,
                                                       out coe.Status Status);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_set_Label")]
      private static extern coe.Status OE_Message_set_Label(IntPtr Handle,
                                                            uint Label);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_get_Label")]
      private static extern uint OE_Message_get_Label(IntPtr Handle,
                                                      out coe.Status Status);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_set_Data_Size")]
      private static extern coe.Status OE_Message_set_Data_Size(IntPtr Handle,
                                                                uint Size);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_get_Data_Size")]
      private static extern uint OE_Message_get_Data_Size(IntPtr Handle,
                                                          out coe.Status Status);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_get_Transmit_Timestamp")]
      private static extern coe.Status OE_Message_get_Transmit_Timestamp(IntPtr Handle,
                                                                         out ulong Seconds,
                                                                         out uint Fraction_Of_Second);

      [DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Message_get_Receive_Timestamp")]
      private static extern coe.Status OE_Message_get_Receive_Timestamp(IntPtr Handle,
                                                                        out ulong Seconds,
                                                                        out uint Fraction_Of_Second);
      #endregion

      private bool disposed = false;
      protected IntPtr m_Handle = IntPtr.Zero;
      protected IntPtr m_UnmanagedBuffer = IntPtr.Zero;
      protected int m_UnmanagedBufferSize = 0;
      protected int m_Size;
      protected byte[] m_Buffer;

      //
      // The following routines are provided for testing and access purposes
      //
      public IntPtr GetUnmanagedBuffer() { return m_UnmanagedBuffer; }
      public byte[] GetManagedBuffer() { return m_Buffer; }

      protected coeMessage(int size) : this(size, 0) { }
      protected coeMessage(int size, uint label) : this(size, label, 0) { }
      protected coeMessage(int size, uint label, int priority)
      {
         if (size == 0)
         {
            size = getPayloadSize();
         }
         if (size > 0)
         {
            m_Buffer = new byte[size];
            m_UnmanagedBuffer = Marshal.AllocHGlobal((int)size);
            m_UnmanagedBufferSize = (int)size;
         }
         // Changed create_shared and app_context are not used in new version of dll.
         //m_Handle = OE_Message_Create(m_UnmanagedBuffer, (uint)size, coe.OE_FALSE, IntPtr.Zero, out coe.Status oe_status);
         m_Handle = OE_Message_Create(m_UnmanagedBuffer, (uint)size, out coe.Status oe_status);
         if (oe_status != coe.Status.SUCCESS)
         {
            m_Handle = IntPtr.Zero;
            throw new Exception("Unable to create OE_Message.  Error = " + oe_status);
         }
         else
         {
            m_Size = size;
            // The message was created successfully
            oe_status = OE_Message_set_Priority(m_Handle, priority);
            if (oe_status != coe.Status.SUCCESS)
            {
               throw new Exception("Unable to set message priority to " + priority + ".  Error = " + oe_status);
            }

            oe_status = OE_Message_set_Label(m_Handle, label);
            if (oe_status != coe.Status.SUCCESS)
            {
               throw new Exception("Unable to set message priority to " + label + ".  Error = " + oe_status);
            }
         }
      }

      ~coeMessage()
      {
         Dispose(false);
      }

      public void Dispose()
      {
         Dispose(true);
         GC.SuppressFinalize(this);
      }

      protected void Dispose(bool disposing)
      {
         if (disposed) return;

         if (disposing)
         {
         }
         if (m_UnmanagedBuffer != IntPtr.Zero)
         {
            Marshal.FreeHGlobal(m_UnmanagedBuffer);
            m_UnmanagedBuffer = IntPtr.Zero;
         }
         if (m_Handle != IntPtr.Zero)
         {
            OE_Message_Delete(m_Handle);
            m_Handle = IntPtr.Zero;
         }
         disposed = true;
      }

      internal IntPtr Handle
      {
         get { return m_Handle; }
      }

      public uint Domain
      {
         get
         {
            return OE_Message_get_Domain(m_Handle, out coe.Status status);
         }
         set
         {
            coe.Status status = OE_Message_set_Domain(m_Handle, value);
         }
      }

      public uint Label
      {
         get
         {
            return OE_Message_get_Label(m_Handle, out coe.Status status);
         }
         set
         {
            coe.Status status = OE_Message_set_Label(m_Handle, value);
         }
      }

      public int Priority
      {
         get
         {
            return OE_Message_get_Priority(m_Handle, out coe.Status status);
         }
         set
         {
            coe.Status status = OE_Message_set_Priority(m_Handle, value);
         }
      }

      public uint Size
      {
         get
         {
            return OE_Message_get_Data_Size(m_Handle, out coe.Status status);
         }
         set
         {
            coe.Status status = OE_Message_set_Data_Size(m_Handle, value);
         }
      }

      public int BufferSize
      {
         get
         {
            return m_UnmanagedBufferSize;
         }
         set
         {
            if (value > m_UnmanagedBufferSize)
            {
               uint savedDomain = Domain;
               uint savedLabel = Label;
               int savedPriority = Priority;
               uint savedSize = Size;

               if (m_UnmanagedBuffer != IntPtr.Zero)
               {
                  Marshal.FreeHGlobal(m_UnmanagedBuffer);
                  m_UnmanagedBuffer = IntPtr.Zero;
               }
               if (m_Handle != IntPtr.Zero)
               {
                  OE_Message_Delete(m_Handle);
                  m_Handle = IntPtr.Zero;
               }
               m_Buffer = new byte[value];
               m_UnmanagedBuffer = Marshal.AllocHGlobal((int)value);
               m_UnmanagedBufferSize = (int)value;
               // Changed create_shared and app_context are not used in new version of dll.
               //m_Handle = OE_Message_Create(m_UnmanagedBuffer, (uint)value, coe.OE_FALSE, IntPtr.Zero, out coe.Status oe_status);
               m_Handle = OE_Message_Create(m_UnmanagedBuffer, (uint)value, out coe.Status oe_status);
               m_Size = value;

               Domain = savedDomain;
               Label = savedLabel;
               Priority = savedPriority;
               Size = savedSize;
            }
         }
      }

      public void GetSendTime(out ulong Seconds, out uint FractionOfSecond)
      {
         var status = OE_Message_get_Transmit_Timestamp(m_Handle, out ulong seconds, out uint fractionOfSecond);
         Seconds = seconds;
         FractionOfSecond = fractionOfSecond;
      }

      public void GetReceiveTime(out ulong Seconds, out uint FractionOfSecond)
      {
         var status = OE_Message_get_Receive_Timestamp(m_Handle, out ulong seconds, out uint fractionOfSecond);
         Seconds = seconds;
         FractionOfSecond = fractionOfSecond;
      }

      abstract public void Serialize();
      abstract public void Deserialize();

      // Serialization/Deserialization support
      private void alignIndex(ref int dataBufferIndex,
                              int alignment)
      {
         int indexMisalignment = dataBufferIndex % alignment;
         if (indexMisalignment > 0)
         {
            dataBufferIndex += alignment - indexMisalignment;
         }
      }

      public int getPayloadSize()
      {
         int size = 0;
         return serializationSupport.getPayloadSize(this, GetType(), ref size);
      }


      protected void copyToMessageBuffer(byte[] data)
      {
         Marshal.Copy(data, 0, m_UnmanagedBuffer, data.Length);
      }

      protected byte[] copyFromMessageBuffer()
      {
         byte[] data = null;
         uint dataSize = OE_Message_get_Data_Size(m_Handle, out coe.Status status);
         if (dataSize > 0)
         {
            Marshal.Copy(m_UnmanagedBuffer, m_Buffer, 0, (int)dataSize);
            data = m_Buffer;
         }
         return data;
      }
   }
}

//\\<Unclassified>
//----------------------------------------------------------------------------//
//                                 UNCLASSIFIED                               //
//----------------------------------------------------------------------------//
//\\<\Unclassified>