// **********************************************************************************************************
//    BitFieldGeneric.cs
//    5/18/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)
// **********************************************************************************************************
using System;

namespace Raytheon.Instruments.MessagingUtilities
{
    // This is a generic class used to apply a bitfield to 
    // a value to get the desired value
    class BitFieldGeneric<T> where T : IConvertible
    {
        protected long m_Value; // This is the value being operated on
        protected ulong m_Mask;

        public char ToChar()
        {
            return (char)m_Value;
        }

        public sbyte ToSByte()
        {
            return (sbyte)m_Value;
        }

        public short ToShort()
        {
            return (short)m_Value;
        }

        public int ToInt()
        {
            return (int)m_Value;
        }

        public long ToLong()
        {
            return (long)m_Value;
        }

        public ushort ToUShort()
        {
            return (ushort)m_Value;
        }

        public uint ToUInt()
        {
            return (uint)m_Value;
        }

        public ulong ToULong()
        {
            return (ulong)m_Value;
        }

        public BitFieldGeneric(T value, ulong bitMask)
        {
            m_Mask = bitMask;
            try
            {
                m_Value = ApplyBitMask((ulong)value.ToInt64(null), bitMask);
            }
            catch
            {
                m_Value = ApplyBitMask(value.ToUInt64(null), bitMask);
            }
        }

        public BitFieldGeneric(string value, ulong bitMask)
        {
            m_Mask = bitMask;

            if(string.IsNullOrEmpty(value))
            {
                value = "0";
            }

            if (Parse.Try(value, out m_Value) == true)
            {
                m_Value = PrepareWithBitMask(m_Value, bitMask);
            }
            else
            {
                throw new Exception("Unable to parse value = " + value);
            }
        }

        // count the number of 1 bits in a ulong
        int BitCount(ulong x)
        {
            int n = 0;
            if (x > 0) do ++n; while ((x &= x - 1) > 1);
            return n;
        }

        // Check to see if the MSB is set after accounting for
        // the mask.  If it is, then the number should be converted
        // to display a negative number
        public void ToSigned()
        {
            if (m_Mask > 0)
            {
                // See if the sign bit is set
                int numbits = BitCount(m_Mask);
                if (m_Value > (Math.Pow(2, numbits - 1)))
                {
                    // If it is, take the two's complement
                    m_Value = (~m_Value) + 1;

                    // Mask off the leading F's from the conversion
                    ulong mask = 1;
                    for (int i = 0; i < numbits - 1; i++)
                    {
                        mask = (mask << 1) + 1;
                    }

                    m_Value = (long)(((ulong)m_Value) & mask);

                    // Add the negative sign
                    m_Value = -m_Value;
                }
            }
        }

        public override string ToString()
        {
            return m_Value.ToString();
        }

        public void BitOR(T value)
        {
            long orValue = value.ToInt64(null);
            m_Value |= orValue;
        }

        private long PrepareWithBitMask(long val, ulong bitMask)
        {
            ulong value = (ulong)val;
            ulong mask = bitMask;

            if (bitMask != 0)
            {
                if ((mask & 1) != 1)
                {
                    while (((mask >> 1) & 1) != 1) //shift mask to LSB
                    {
                        mask >>= 1;
                    }

                    mask >>= 1; // one last shift not done by loop
                }

                value &= mask; // ensure value is contained in the same # of bits as the mask

                // Shift the value back to its proper spot in the memory
                while (mask != bitMask)
                {
                    value <<= 1;
                    mask <<= 1;
                }
            }
            return (long)value;
        }

        private long ApplyBitMask(ulong val, ulong bitMask)
        {
            ulong value = val;

            if (bitMask != 0) // Apply the bit field
            {
                value &= bitMask;

                // Shift until the bitmask resides in the LSB
                if ((bitMask & 1) != 1)
                {
                    while (((bitMask >> 1) & 1) != 1)
                    {
                        value >>= 1;
                        bitMask >>= 1;
                    }

                    // We need one more shift after leaving the while loop
                    value >>= 1;
                }
            }
            return (long)value;
        }
    }

}
