using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace NIDAQmx
{
    sealed class DAQmxSession
    {
        private ConcurrentDictionary<IntPtr, Delegate> _Cache = new ConcurrentDictionary<IntPtr, Delegate>();
        private readonly string windowsdllname= "nicaiu.dll";
        private readonly string linuxdllname="nidaqmx.so.1";
        public Boolean Error { get; private set; } = false;
        public String Message { get; private set; } = string.Empty;
        [AllowNull]
        private readonly NativeLibraryLoader.NativeLoader _NativeLoader;
        public DAQmxSession() 
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || false) _NativeLoader = new NativeLibraryLoader.NativeLoader(windowsdllname);
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                _NativeLoader = new NativeLibraryLoader.NativeLoader(linuxdllname);
                _NativeLoader.SearchDirectories.Add("/usr/lib/x86_64-linux-gnu/");
            }
            else
            {
                throw new PlatformNotSupportedException();
            }
            _NativeLoader.Init();
        }
        internal void LoadTask(string taskname)
        {
            CheckResult(GetDelegate<Internal.Task.DAQmxLoadTask>()(taskname, ref ptr));
        }
        internal void CreateTask(string taskname)
        {
            CheckResult(GetDelegate<Internal.Task.DAQmxCreateTask>()(taskname, ref ptr));
        }
        internal void StopTask()
        {
            CheckResult(GetDelegate<Internal.Task.DAQmxStopTask>()(TaskHandle));
        }
        internal void ClearTask()
        {

            CheckResult(GetDelegate<Internal.Task.DAQmxClearTask>()(TaskHandle));
            ptr = IntPtr.Zero;
        }
        private IntPtr ptr= IntPtr.Zero;
        public IntPtr TaskHandle => ptr;
        public void Invoke<T>(params object[] objects)  where T : Delegate
        {
            DAQmxEnum.Result? result = (DAQmxEnum.Result?)_NativeLoader.LoadFunction<T>(typeof(T).Name).DynamicInvoke(objects);
            if (!result.HasValue) return;
            CheckResult(result.Value);
        }
        public unsafe string GetValue<TDelegate>(int buffersize=1024) where TDelegate:Delegate
        {
            byte[] bytes = new byte[buffersize];
            IntPtr dataptr = (IntPtr)Unsafe.AsPointer(ref bytes[0]);
            DAQmxEnum.Result? result = (DAQmxEnum.Result?)_NativeLoader.LoadFunction<TDelegate>(typeof(TDelegate).Name).DynamicInvoke(TaskHandle,dataptr,(uint)bytes.Length);
            if(result.HasValue)
            {
                CheckResult(result.Value);
            }
            return System.Text.Encoding.UTF8.GetString(bytes).Trim('\0');
        }
        public string[] GetStrings<TDelegate>(int buffersize=1024) where TDelegate : Delegate
        {
            return GetValue<TDelegate>(buffersize).Split(',').Select(x=>x.Trim('\0')).Where(x=>!string.IsNullOrEmpty(x)).ToArray();
        }
        public unsafe T GetValue<TDelegate,T>()
            where TDelegate : Delegate
            where T :unmanaged
        {
            T val =default(T);
            IntPtr dataintPtr = (IntPtr)Unsafe.AsPointer(ref val);
            DAQmxEnum.Result? result = (DAQmxEnum.Result?)_NativeLoader.LoadFunction<TDelegate>(typeof(TDelegate).Name).DynamicInvoke(TaskHandle, dataintPtr);
            if (result.HasValue)
            {
                CheckResult(result.Value);
            }
            return val;
        }
        public unsafe void SetValue<TDelegate, T>(T val)
            where TDelegate : Delegate
            where T : unmanaged
        {
            DAQmxEnum.Result? result = (DAQmxEnum.Result?)_NativeLoader.LoadFunction<TDelegate>(typeof(TDelegate).Name).DynamicInvoke(TaskHandle, val);
            if (result.HasValue)
            {
                CheckResult(result.Value);
            }
        }
        public unsafe void SetValue<TDelegate>(string val)
            where TDelegate : Delegate
        {
            DAQmxEnum.Result? result = (DAQmxEnum.Result?)_NativeLoader.LoadFunction<TDelegate>(typeof(TDelegate).Name).DynamicInvoke(TaskHandle, val);
            if (result.HasValue)
            {
                CheckResult(result.Value);
            }
        }
        public T GetDelegate<T>() where T : Delegate
        {
            return _NativeLoader.LoadFunction<T>(typeof(T).Name);
        }
        public void CheckResult(DAQmxEnum.Result result)
        {
            Error = ((Int32)result) < 0;
            Message = result.ToString();
            if(Error)
            {
                StringBuilder stringBuilder= new StringBuilder(256);
                GetDelegate<Internal.Internal.DAQmxGetErrorString>()((int)result, stringBuilder, (uint)stringBuilder.Capacity + 1);
                Message += "," + stringBuilder.ToString();
            }
        }
    }
}
