using NIDAQmx.DAQmxEnum;
using NIDAQmxConst;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text;

namespace NIDAQmx
{
    internal abstract class BaseChannelAttributeSession : DAQmxBase
    {

        internal BaseChannelAttributeSession(DAQmxSession session) : base(session)
        {
        }
        
        public DAQmxSession Session => _Session;
        public string ActiveChans { get; set; } = string.Empty;
    }
    internal sealed class ChannelAttributeSession: BaseChannelAttributeSession
    {
        public ChannelAttributeSession(DAQmxSession session) : base(session)
        {
        }
        internal unsafe T GetValue<TDelegate,T>()
            where TDelegate : Delegate
            where T :unmanaged
        {
            if (typeof(T) == typeof(bool))
            {
                uint val = 0;
                IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref val);
                _Session.Invoke<TDelegate>(_Session.TaskHandle, ActiveChans, ptr);
                return (T)(object)Convert.ToBoolean(val);
            }
            else
            {
                T val = default(T);
                IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref val);
                _Session.Invoke<TDelegate>(_Session.TaskHandle, ActiveChans, ptr);
                return val;
            }
        }
        internal void SetValue<TDelegate,T>(T val)
            where TDelegate : Delegate
            where T :unmanaged
        {
            _Session.Invoke<TDelegate>(_Session.TaskHandle, ActiveChans, val);
        }
        internal unsafe string GetValue<TDelegate>(int buffersize = 1024) where TDelegate : Delegate
        {
            byte[] bytes = new byte[buffersize];

            IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref bytes[0]);
            _Session.Invoke<TDelegate>(_Session.TaskHandle, ActiveChans, ptr, (uint)bytes.Length);
            return Encoding.UTF8.GetString(bytes).Trim('\0');
        }
        internal void SetValue<TDelegate>(string val)where TDelegate : Delegate
        {
            _Session.Invoke<TDelegate>(_Session.TaskHandle, ActiveChans, val);
        }
    }
    public abstract class BaseChannelConfig
    {
        [AllowNull]
        private protected DAQmxSession _DAQmxSession;
        internal BaseChannelConfig(BaseChannelAttributeSession session)
        { 
            _DAQmxSession = session.Session;
        }
        [AllowNull]
        internal virtual BaseChannelAttributeSession AttributeSession { get; }
    }

    public class ChannelConfig
    {
        private List<BaseChannelConfig> configs = new List<BaseChannelConfig>();
        internal ChannelConfig(DAQmxSession session)
        {
            AttributeSession = new ChannelAttributeSession(session);
            AI = new AIChannelConfig(new ChannelAttributeSession(session));
            configs.Add(AI);
            AO = new AOChannelConfig(new ChannelAttributeSession(session));
            configs.Add(AO);
            DI = new DIChannelConfig(new ChannelAttributeSession(session));
            configs.Add(DI);
            DO = new DOChannelConfig(new ChannelAttributeSession(session));
            configs.Add(DO);
        }
        public string ActiveChans
        {
            get => AttributeSession.ActiveChans; 
            internal set
            {
                AttributeSession.ActiveChans = value;
                foreach (var val in configs)
                {
                    val.AttributeSession.ActiveChans = value;
                }
            }
        }
        [AllowNull]
        internal ChannelAttributeSession AttributeSession { get; }
        public ChannelType ChannelType => AttributeSession.GetValue<Internal.Internal.DAQmxGetChanType,ChannelType>();
        public string PhycicalChannelName { get => AttributeSession.GetValue<Internal.Internal.DAQmxGetPhysicalChanName>(); set => AttributeSession.SetValue<Internal.Internal.DAQmxSetPhysicalChanName>(value); }

        public bool IsGloba => AttributeSession.GetValue<Internal.Internal.DAQmxGetChanIsGlobal,bool>();
        public string ChanDescr { get=>AttributeSession.GetValue<Internal.Internal.DAQmxGetChanDescr>(); set=>AttributeSession.SetValue<Internal.Internal.DAQmxSetChanDescr>(value); }
        public SyncUnlockBehavior SyncUnlockBehavior { get => AttributeSession.GetValue<Internal.Internal.DAQmxGetChanSyncUnlockBehavior,SyncUnlockBehavior>(); set => AttributeSession.SetValue<Internal.Internal.DAQmxSetChanSyncUnlockBehavior,SyncUnlockBehavior>(value); }
        public AIChannelConfig AI { get; }
        public AOChannelConfig AO { get; }
        public DIChannelConfig DI { get; }
        public DOChannelConfig DO { get; }
        

    }
}
