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

namespace csharp_plugin
{
    //Пример структуры
    struct RGBStruct
    {
        int R;
        int G;
        int B;
        public RGBStruct(int r, int g, int b)
        {
            R = r;
            G = g;
            B = b;
        }
    }

    public static class Main
    {
        //Указатель на блок неуправляемой памяти.
        //Для перегонки структур/строк. Работает медленно.
        //Нет потокобезопасности.
        //Использовать с умом.
        private static IntPtr UnmanagedMemory;

        //Обертка для заталкивания объектов в неуправляемую память
        private static IntPtr ToUnmanagedMemory(object obj)
        {                         
            //Сюда можно вставить lock, если надо прикрутить многопоточность.
            //+ можно использовать пул блоков памяти.

            //если память выделялась ранее
            if (UnmanagedMemory != IntPtr.Zero)
                Marshal.FreeHGlobal(UnmanagedMemory);
            switch (obj)
            {
                //если строка
                case string s:
                    UnmanagedMemory = Marshal.StringToHGlobalAnsi(s); //без юникода
                    break;
                //любой другой объект
                case object o:
                    UnmanagedMemory = Marshal.AllocHGlobal(Marshal.SizeOf(o));
                    Marshal.StructureToPtr(o, UnmanagedMemory, false);
                    break;
            }
            //возвращаем указатель
            return UnmanagedMemory;
        }

        //Метод экспортируется
        [DllExport("hello")] //декорация имени
        //Входной параметр принимается как C-like строка.
        public static IntPtr Hello([MarshalAs(UnmanagedType.LPStr)] string arg)
        {
            return ToUnmanagedMemory(string.Format("Hello, {0}", arg));
        }

        //Возвращает структуру
        [DllExport("rgb")]
        public static IntPtr NewRGBInstance([MarshalAs(UnmanagedType.I4)] int r,
                                            [MarshalAs(UnmanagedType.I4)] int g,
                                            [MarshalAs(UnmanagedType.I4)] int b)
        {
            return ToUnmanagedMemory(new RGBStruct(r, g, b));
        }

        //Полностью сишный varargs невозможно реализовать
        //Сначала передаем кол-во элементов в массиве
        //Затем указатель на массив
        [DllExport("mymul")]
        [return: MarshalAs(UnmanagedType.I4)]
        public static int Multiply([MarshalAs(UnmanagedType.I4)] int argc, IntPtr args)
        {
            int[] data = new int[argc];
            Marshal.Copy(args, data, 0, argc); //получаем управляемую память
            int result = 1;
            foreach (var i in data)
            {
                result *= i;
            }
            return result; //возвращаем скаляр, маршалинг не требуется
        }

        //Заполняет блок памяти.
        //можно использовать out IntPtr mem
        //тогда заполнять нужно в промежуточный массив, а затем делать
        //Marshal.AllocHGlobal()/Marshal.Copy().
        //Учитываем, что потом надо сделать FreeHGlobal()
        [DllExport("fill")]
        public static void FillBuffer([MarshalAs(UnmanagedType.I4)]int size, IntPtr mem)
        {
            Random rnd = new Random();
            //размер инта в байтах
            int intSize = Marshal.SizeOf(typeof(int));
            for (int i = 0; i < size; i++)
                Marshal.WriteInt32(mem, intSize*i, rnd.Next());
        }
    }
}
