using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace arinc_1553_CSharpGUI
{
    using U64BIT = System.UInt64;
    using U32BIT = System.UInt32;
    using U16BIT = System.UInt16;
    using U8BIT = System.Byte;
    using S64BIT = System.Int64;
    using S32BIT = System.Int32;
    using S16BIT = System.Int16;
    using S8BIT = System.SByte;

    public struct EguParameterValue
    {
        public S16BIT s16Value;
        public U16BIT u16Value;
        public float fValue;
        public string strValue;
    }

    public class EguParameter
    {
        public string name;

        /* Data type for this parameter E.g. UNSIGNED INT */
        public string dataType;

        public string codedType;
        public string isReverseBits;

        /* Location and length (in bits) of this parameter within the message data words */
        public uint length;
        public uint startWord;
        public uint startBit;

        /* Scaling factor and offset for performing engineering unit conversion */
        public double scaleFactor;
        public double offset;

        public string units;

        /* Range to validate against when user inputs value to this engg parameter */
        public string rangeLow = "";
        public string rangeHigh = "";

        /* Reference to the message in which this Egu Parameter resides */
        public Msg1553 pMsg = new Msg1553();

        /* Current value of this parameter */
        public EguParameterValue value = new EguParameterValue();

        /* Value edited by the user from the UI but not yet written to the data block of the message */
        public EguParameterValue modifiedValue = new EguParameterValue();

        public bool bEditInProgress = false;   /* Indicates if this parameter is being edited by the user. This flag is used to
                                        determine if value display for this parameter should be updated when a new instance
                                        of its message is received */

        EguParameterValue m_defaultValue = new EguParameterValue();
        string strDefaultValue;

        public ushort displayRowNo;

        /* Return values:   0 : value is valid
         *                  -1: value is over range
         *                  -2: value is under range
         */
        public int SetModifiedValue(string strNewValue)
        {
            int retVal = -1;
            EguParameterValue eguParaVal = new EguParameterValue();

            retVal = GetEguParameterValueFromStr(strNewValue, ref eguParaVal);
            if (retVal == 0)
            {
                modifiedValue = eguParaVal;
            }

            return retVal;
        }

        /* Return values:   0 : value is valid
         *                  -1: value is over range
         *                  -2: value is under range
         */
        public int GetEguParameterValueFromStr(string strValue, ref EguParameterValue eguParaValue)
        {
            int retVal = -1;

            //if ((dataType == "UNSIGNED_REAL") || (dataType == "SIGNED_REAL"))
            if (dataType.ToUpper() == "REALIEEE754")
            {
                float val;
                try
                {
                    val = float.Parse(strValue);

                    if ((rangeHigh.Length != 0) && (val > float.Parse(rangeHigh)))
                    {
                        retVal = -2;
                        //qDebug() << __FUNCTION__ << "-2 for UNSIGNED_REAL and SIGNED_REAL";
                    }
                    else if ((rangeLow.Length != 0) && (val < float.Parse(rangeLow)))
                    {
                        retVal = -3;
                        //qDebug() << __FUNCTION__ << "-3 for UNSIGNED_REAL and SIGNED_REAL";
                    }
                    else
                    {
                        eguParaValue.fValue = val;
                        retVal = 0;
                    }
                }
                catch (Exception)
                {
                    retVal = -1;
                    //printf("-1 for UNSIGNED_REAL and SIGNED_REAL");
                }
            }
            //else if ((dataType == "UNSIGNED_INTEGER") || (dataType == "BCD"))
            else if (dataType.ToUpper() == "UNSIGNED")
            {
                U16BIT val;
                try
                {
                    val = UInt16.Parse(strValue);

                    if ((rangeHigh.Length != 0) && (val > UInt16.Parse(rangeHigh)))
                    {
                        retVal = -2;
                        //qDebug() << __FUNCTION__ << "-2 for UNSIGNED_INTEGER";
                    }
                    else if ((rangeLow.Length != 0) && (val < UInt16.Parse(rangeLow)))
                    {
                        retVal = -3;
                        //qDebug() << __FUNCTION__ << "-3 for UNSIGNED_INTEGER";
                    }
                    else
                    {
                        eguParaValue.u16Value = val;
                        retVal = 0;
                    }
                }
                catch (Exception)
                {
                    retVal = -1;
                    //printf("-1 for UNSIGNED_INTEGER");
                }
            }
            //else if (dataType == "SIGNED_INTEGER")
            else if (dataType.ToUpper() == "INTEGER")
            {
                S16BIT val;
                try
                {
                    val = Int16.Parse(strValue);

                    if ((rangeHigh.Length != 0) && (val > Int16.Parse(rangeHigh)))
                    {
                        retVal = -2;
                        //qDebug() << __FUNCTION__ << "-2 for SIGNED_INTEGER";
                    }
                    else if ((rangeLow.Length != 0) && (val < Int16.Parse(rangeLow)))
                    {
                        retVal = -3;
                        //qDebug() << __FUNCTION__ << "-3 for SIGNED_INTEGER";
                    }
                    else
                    {
                        eguParaValue.s16Value = val;
                        retVal = 0;
                    }
                }
                catch (Exception)
                {
                    retVal = -1;
                    //printf("-1 for SIGNED_INTEGER");
                }
            }
            else if (dataType == "CODED")
            {
                eguParaValue.strValue = strValue;
                retVal = 0;
            }

            return retVal;
        }

        /* Returns the parameter value as one or more 16-bit unsigned (WORD) values */
        public void GetRawValue(ref U16BIT[] buffer, List<CodedType> m_CodedTypes)
        {
            U32BIT uhwClearBits = 0;
            uhwClearBits = ~((uint)0xFFFF << (int)length);
            uhwClearBits = (uhwClearBits << (int)startBit);

            if (dataType.ToUpper() == "UNSIGNED")
            {
                U32BIT uhwCount = (U32BIT)((value.u16Value - offset) / scaleFactor);
                uhwCount = uhwCount << (int)startBit;

                uhwCount = uhwCount & uhwClearBits;

                uhwClearBits = ~uhwClearBits;
                buffer[0] = (ushort) (buffer[0] & uhwClearBits);
                buffer[0] = (ushort) (buffer[0] | uhwCount);
            }
            else if (dataType.ToUpper() == "INTEGER")
            {
                //U32BIT uhwCount = (U32BIT)((value.s16Value - offset) / scaleFactor);
                U32BIT uhwCount = (U32BIT)((value.fValue - offset) / scaleFactor);
                uhwCount = uhwCount << (int)startBit;

                uhwCount = uhwCount & uhwClearBits;

                uhwClearBits = ~uhwClearBits;
                buffer[0] = (ushort) (buffer[0] & uhwClearBits);
                buffer[0] = (ushort) (buffer[0] | uhwCount);
            }
            //else if ((dataType == "UNSIGNED_REAL") || (dataType == "SIGNED_REAL"))
            else if (dataType.ToUpper() == "REALIEEE754")
            {
                // store the val in buf
                // buffer[0] = 0;
                double dlEngineeringData = (value.fValue - offset) / scaleFactor;

                U32BIT uhwCount = (U32BIT)dlEngineeringData;
                uhwCount = uhwCount << (int)startBit;

                uhwCount = uhwCount & uhwClearBits;

                uhwClearBits = ~uhwClearBits;
                buffer[0] = (ushort) (buffer[0] & uhwClearBits);
                buffer[0] = (ushort) (buffer[0] | uhwCount);
            }
            else if (dataType.ToUpper() == "CODED")
            {
                /* Get raw value corresponding to the coded string */
                buffer[0] = GetCodedStringRawValue(codedType, value.strValue, m_CodedTypes);

                /* position the raw value correctly by left shifting the raw value to the "startBit" */
                buffer[0] = (ushort) (buffer[0] << (int)startBit);
            }
            else if (dataType == "BCD")
            {
                // TODO
            }
        }

        U16BIT GetCodedStringRawValue(string codedTypeToMatch, string codedString, List<CodedType> m_CodedTypes)
        {
            U16BIT index = 0;
            foreach (CodedType ct in m_CodedTypes)
            {
                if (ct.type == codedTypeToMatch)
                {
                    foreach (string str in ct.values)
                    {
                        // We do a case insensitive comparison
                        if (str.ToUpper() == codedString.ToUpper())
                        {
                            return index;
                        }

                        index++;
                    }
                }
            }

            /* if we get here we did not find a match */
            return 0xFFFF;
        }

        public string GetTooltipText()
        {
            string tooltip;

            tooltip = "Type " + dataType + "\nStartBit = " + startBit + " NoofBits = " + length;

            if (dataType != "CODED")
            {
                tooltip += "\nScalingFactor " + scaleFactor + " \nLow Range "
                            + (rangeLow != "" ? rangeLow : "n/a")
                            + " High Range " + (rangeHigh != "" ? rangeHigh : "n/a");
            }

            return tooltip;
        }

        public string GetRangeTipText()
        {
            string rangeTip = "";

            if (dataType != "CODED")
            {
                rangeTip = "Low Range = " + (rangeLow != "" ? rangeLow : "n/a")
                            + "\nHigh Range = " + (rangeHigh != "" ? rangeHigh : "n/a");
            }

            return rangeTip;
        }

        public bool SetDefaultValue(string newStrDefValue, ref string errorString, List<CodedType> m_CodedTypes)
        {
            bool retValue = false;

            //if ((dataType == "UNSIGNED_REAL") || (dataType == "SIGNED_REAL"))
            if (dataType.ToUpper() == "REALIEEE754")
            {
                if (newStrDefValue.Length == 0)
                {
                    if (rangeLow.Length != 0)
                    {
                        m_defaultValue.fValue = float.Parse(rangeLow);
                    }
                    else
                    {
                        m_defaultValue.fValue = (float) 0.0;
                    }
                    retValue = true;
                }
                else
                {
                    float val;
                    try
                    {
                        val = float.Parse(newStrDefValue);

                        if ((rangeHigh.Length != 0) && (val > float.Parse(rangeHigh)))
                        {
                            retValue = false;
                            errorString = "Default value out of range";
                        }
                        else if ((rangeLow.Length != 0) && (val < float.Parse(rangeLow)))
                        {
                            retValue = false;
                            errorString = "Default value out of range";
                        }
                        else
                        {
                            m_defaultValue.fValue = val;
                            retValue = true;
                        }
                    }
                    catch (Exception)
                    {
                        retValue = false;
                        errorString = "Could not convert default value to float";
                    }
                }
            }
            //else if ((dataType == "UNSIGNED_INTEGER") || (dataType == "BCD"))
            else if (dataType.ToUpper() == "UNSIGNED")
            {
                if (newStrDefValue.Length == 0)
                {
                    if (rangeLow.Length != 0)
                    {
                        m_defaultValue.u16Value = UInt16.Parse(rangeLow);
                    }
                    else
                    {
                        m_defaultValue.u16Value = 0;
                    }
                    retValue = true;
                }
                else
                {
                    U16BIT val;
                    try
                    {
                        val = UInt16.Parse(newStrDefValue);

                        if ((rangeHigh.Length != 0) && (val > UInt16.Parse(rangeHigh)))
                        {
                            retValue = false;
                            errorString = "Default value out of range";
                        }
                        else if ((rangeLow.Length != 0) && (val < UInt16.Parse(rangeLow)))
                        {
                            retValue = false;
                            errorString = "Default value out of range";
                        }
                        else
                        {
                            m_defaultValue.u16Value = val;
                            retValue = true;
                        }
                    }
                    catch (Exception)
                    {
                        retValue = false;
                        errorString = "Could not convert default value to unsigned short";
                    }
                }
            }
            else if (dataType.ToUpper() == "INTEGER")
            {
                if (newStrDefValue.Length == 0)
                {
                    if (rangeLow.Length != 0)
                    {
                        m_defaultValue.s16Value = Int16.Parse(rangeLow);
                    }
                    else
                    {
                        //m_defaultValue.s16Value = 0;
                        m_defaultValue.fValue = 0;
                    }
                    retValue = true;
                }
                else
                {
                    //S16BIT val;
                    float val;
                    try
                    {
                        //val = Int16.Parse(newStrDefValue);
                        val = float.Parse(newStrDefValue);

                        if ((rangeHigh.Length != 0) && (val > Int16.Parse(rangeHigh)))
                        {
                            retValue = false;
                            errorString = "Default value out of range";
                        }
                        else if ((rangeLow.Length != 0) && (val < Int16.Parse(rangeLow)))
                        {
                            retValue = false;
                            errorString = "Default value out of range";
                        }
                        else
                        {
                            //m_defaultValue.s16Value = val;
                            m_defaultValue.fValue = val;
                            retValue = true;
                        }
                    }
                    catch (Exception)
                    {
                        retValue = false;
                        errorString = "Could not convert default value to short";
                    }
                }
            }
            else if (dataType.ToUpper() == "CODED")
            {
                // TODO - handle condition where default string passed is empty
                if (newStrDefValue.Length == 0)
                {
                    //qDebug() << "Warning!!! Default value passed is empty";

                    // Since no default value is specified, choose one from the available coded values
                    List<string> codedValues = GetCodedValues(codedType, m_CodedTypes);
                    if (codedValues.Count > 0)
                    {
                        m_defaultValue.strValue = codedValues[0];
                        retValue = true;
                    }
                    else
                    {
                        //qDebug() << "Warning!!! Unknown coded type";
                    }
                }
                else
                {
                    // TODO: Check if the default value string matches at least one of the strings in coded values for this type
                    m_defaultValue.u16Value = GetCodedStringRawValue(codedType, newStrDefValue, m_CodedTypes);
                    if (m_defaultValue.u16Value != 0xFFFF) // GetCodedStringRawValue returns 0xFFFF if it does not find a match
                    {
                        m_defaultValue.strValue = newStrDefValue;
                        retValue = true;
                    }
                    else
                    {
                        retValue = false;
                        //qDebug() << "Default value passed does not match any values of this coded type";
                        errorString = "Default value passed does not match any values of this coded type";
                    }
                }
            }

            if (retValue == true)
            {
                strDefaultValue = newStrDefValue;
            }

            return retValue;
        }

        List<string> GetCodedValues(string codedType, List<CodedType> listCodedTypes)
        {
            List<string> codedValues = new List<string>();

            foreach (CodedType ct in listCodedTypes)
            {
                if (ct.type == codedType)
                {
                    codedValues = ct.values;
                    break;
                }
            }

            return codedValues;
        }

        string getStrDefaultValue()
        {
            return strDefaultValue;
        }

        public void LoadDefaultData()
        {
            value = m_defaultValue;
        }
    }
}
