using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using FxpConvert.Common;

namespace NIFPGA
{
    public sealed class FPGAProperty<T> : FPGABaseProperty
        where T : unmanaged
    {
        internal FPGAProperty(FPGASession session, uint indicator,bool isIndicator = true) : base(session, indicator,isIndicator)
        {
        }
        private void SetValue(T value)
        {
            switch(value)
            {
                case Boolean boolvalue:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteBool>()(_Session.Session, Indicator, Convert.ToByte(boolvalue)));
                    break;
                case Byte:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteU8>()(_Session.Session, Indicator, Unsafe.As<T,byte>(ref value)));
                    break;
                case SByte:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteI8>()(_Session.Session, Indicator, Unsafe.As<T, sbyte>(ref value)));
                    break;
                case Int16:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteI16>()(_Session.Session, Indicator, Unsafe.As<T, short>(ref value)));
                    break;
                case UInt16:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteU16>()(_Session.Session, Indicator, Unsafe.As<T, ushort>(ref value)));
                    break;
                case Int32:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteI32>()(_Session.Session, Indicator, Unsafe.As<T, int>(ref value)));
                    break;
                case UInt32:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteU32>()(_Session.Session, Indicator, Unsafe.As<T, uint>(ref value)));
                    break;
                case Int64:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteI64>()(_Session.Session, Indicator, Unsafe.As<T, long>(ref value)));
                    break;
                case UInt64:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteU64>()(_Session.Session, Indicator, Unsafe.As<T, ulong>(ref value)));
                    break;
                case Single:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteSgl>()(_Session.Session, Indicator, Unsafe.As<T, float>(ref value)));
                    break;
                case double:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteDbl>()(_Session.Session, Indicator, Unsafe.As<T, double>(ref value)));
                    break;
            }
        }


        private T GetValue()
        {
            T value = default;
            switch (value)
            {
                case Boolean:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadBool>()(_Session.Session, Indicator,ref Unsafe.As<T,byte>(ref value)));
                    break;
                case Byte:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadU8>()(_Session.Session, Indicator, ref Unsafe.As<T, byte>(ref value)));
                    break;
                case SByte:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadI8>()(_Session.Session, Indicator, ref Unsafe.As<T, sbyte>(ref value)));
                    break;
                case Int16:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadI16>()(_Session.Session, Indicator, ref Unsafe.As<T, short>(ref value)));
                    break;
                case UInt16:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadU16>()(_Session.Session, Indicator, ref Unsafe.As<T, ushort>(ref value)));
                    break;
                case Int32:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadI32>()(_Session.Session, Indicator, ref Unsafe.As<T, int>(ref value)));
                    break;
                case UInt32:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadU32>()(_Session.Session, Indicator, ref Unsafe.As<T, uint>(ref value)));
                    break;
                case Int64:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadI64>()(_Session.Session, Indicator, ref Unsafe.As<T, long>(ref value)));
                    break;
                case UInt64:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadU64>()(_Session.Session, Indicator, ref Unsafe.As<T, ulong>(ref value)));
                    break;
                case Single:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadSgl>()(_Session.Session, Indicator, ref Unsafe.As<T, float>(ref value)));
                    break;
                case double:
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadDbl>()(_Session.Session, Indicator, ref Unsafe.As<T, double>(ref value)));
                    break;
            }
            return value;
        }
        public T Value { get=>GetValue(); set=>SetValue(value); }
    }
    public sealed class FPGAFXPProperty: FPGABaseProperty
    {
        private NiFpga_FxpTypeInfo _TypeInfo;
        private IFxpConvert _Convert;
        internal FPGAFXPProperty(FPGASession session, uint indicator,NiFpga_FxpTypeInfo typeInfo,IFxpConvert convert, bool isIndicator = true) : base(session, indicator,isIndicator)
        {
            _TypeInfo = typeInfo;
            _Convert = convert;
        }
        private float GetValue()
        {
            ulong value=0;
            float temp = 0;
            _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadU64>()(_Session.Session, Indicator, ref value));
            _Convert.FxpConvertToFloat(ref value, _TypeInfo, ref temp, 1);
            return temp;
        }
        private void SetValue(float value)
        {
            ulong temp = 0;
            _Convert.FloatConvertToDxp(ref value, _TypeInfo, ref temp, 1);
            _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteU64>()(_Session.Session, Indicator, temp));
        }
        public float Value { get=>GetValue(); set=>SetValue(value); }
    }
    public sealed class FPGAArrayProperty<T> : FPGABaseProperty
    where T : unmanaged
    {
        private T[] tempvalue = new T[0];
        internal FPGAArrayProperty(FPGASession session, uint indicator,uint count, bool isIndicator = true) : base(session, indicator,isIndicator)
        {
            Count= count;
            tempvalue= new T[Count];
        }
        private void SetValue(T[] values)
        {
            if (values.Length == 0 || values.Length!=Count) return;
            switch (values[0])
            {
                case (bool):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayBool>()(_Session.Session, Indicator, ref Unsafe.As<T, byte>(ref values[0]), Count));
                    break;
                case (byte):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayU8>()(_Session.Session, Indicator, ref Unsafe.As<T, byte>(ref values[0]), Count));
                    break;
                case (sbyte):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayI8>()(_Session.Session, Indicator, ref Unsafe.As<T, sbyte>(ref values[0]), Count));
                    break;
                case (short):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayI16>()(_Session.Session, Indicator, ref Unsafe.As<T, short>(ref values[0]), Count));
                    break;
                case (ushort):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayU16>()(_Session.Session, Indicator, ref Unsafe.As<T, ushort>(ref values[0]), Count));
                    break;
                case (int):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayI32>()(_Session.Session, Indicator, ref Unsafe.As<T, int>(ref values[0]), Count));
                    break;
                case (uint):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayU32>()(_Session.Session, Indicator, ref Unsafe.As<T, uint>(ref values[0]), Count));
                    break;
                case (long):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayI64>()(_Session.Session, Indicator, ref Unsafe.As<T, long>(ref values[0]), Count));
                    break;
                case (ulong):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayU64>()(_Session.Session, Indicator, ref Unsafe.As<T, ulong>(ref values[0]), Count));
                    break;
                case (float):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArraySgl>()(_Session.Session, Indicator, ref Unsafe.As<T, float>(ref values[0]), Count));
                    break;
                case (double):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayDbl>()(_Session.Session, Indicator, ref Unsafe.As<T, double>(ref values[0]), Count));
                    break;
            }
        }
        private T[] GetValues()
        {
            if (Count == 0) return tempvalue;
            switch (tempvalue[0])
            {
                case (bool):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayBool>()(_Session.Session, Indicator, ref Unsafe.As<T, byte>(ref tempvalue[0]), Count));
                    break;
                case (byte):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayU8>()(_Session.Session, Indicator, ref Unsafe.As<T, byte>(ref tempvalue[0]), Count));
                    break;
                case (sbyte):
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayI8>()(_Session.Session, Indicator, ref Unsafe.As<T, sbyte>(ref tempvalue[0]), Count));
                    break;                                                                                                                                 
                case short:                                                                                                                        
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayI16>()(_Session.Session, Indicator, ref Unsafe.As<T, short>(ref tempvalue[0]),Count));
                    break;                                                                                                                                 
                case (ushort):                                                                                                                        
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayU16>()(_Session.Session, Indicator, ref Unsafe.As<T, ushort>(ref tempvalue[0]),Count));
                    break;                                                                                                                                 
                case (int):                                                                                                                          
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayI32>()(_Session.Session, Indicator, ref Unsafe.As<T, int>(ref tempvalue[0]),Count));
                    break;                                                                                                                                 
                case (uint):                                                                                                                         
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayU32>()(_Session.Session, Indicator, ref Unsafe.As<T, uint>(ref tempvalue[0]),Count));
                    break;                                                                                                                                 
                case (long):                                                                                                                         
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayI64>()(_Session.Session, Indicator, ref Unsafe.As<T, long>(ref tempvalue[0]),Count));
                    break;                                                                                                                                 
                case (ulong):                                                                                                                        
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayU64>()(_Session.Session, Indicator, ref Unsafe.As<T, ulong>(ref tempvalue[0]),Count));
                    break;                                                                                                                                 
                case (float):                                                                                                                        
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArraySgl>()(_Session.Session, Indicator, ref Unsafe.As<T, float>(ref tempvalue[0]),Count));
                    break;                                                                                                                                 
                case (double):                                                                                                                       
                    _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayDbl>()(_Session.Session, Indicator, ref Unsafe.As<T, double>(ref tempvalue[0]), Count));
                    break;
            }
            return tempvalue;
        }
        public uint Count { get; }
        public T[] Values { get=>GetValues(); set=>SetValue(value); }
    }

    public sealed class FPGAArrayFXPProperty : FPGABaseProperty
    {
        private NiFpga_FxpTypeInfo _TypeInfo;
        private IFxpConvert _Convert;
        private ulong[] tempvalue = new ulong[0];
        private float[] doubles= new float[0];
        internal FPGAArrayFXPProperty(FPGASession session, uint indicator, uint count,NiFpga_FxpTypeInfo typeInfo, IFxpConvert convert, bool isIndicator = true) : base(session, indicator,isIndicator)
        {
            _Convert = convert;
            Count = count;
            tempvalue = new ulong[count];
            doubles = new float[count];
            _TypeInfo = typeInfo;
        }
        private void SetData(float[] values)
        {
            if (values.Length != Count) return;
            _Convert.FloatConvertToDxp(ref values[0], _TypeInfo, ref tempvalue[0], Count);
            _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_WriteArrayU64>()(_Session.Session,Indicator, ref tempvalue[0], Count));
        }
        private float[] GetData()
        {
            _Session.CheckResult(_Session.GetDelegate<Interop.NiFpgaDll_ReadArrayU64>()(_Session.Session,Indicator, ref tempvalue[0], Count));
            _Convert.FxpConvertToFloat(ref tempvalue[0], _TypeInfo,ref doubles[0], Count);
            return doubles;
        }
        public uint Count { get; }
        public float[] Value { get=>GetData(); set=>SetData(value); }
    }
}
