
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Numerics;

abstract class Sample
{
    [DllImport("./libMatxFft.so", CallingConvention = CallingConvention.StdCall)]
    public static extern void CreateCudaStream();

    [DllImport("./libMatxFft.so", CallingConvention = CallingConvention.StdCall)]
    public static extern void DestroyCudaStream();


    [DllImport("./libMatxFft.so", CallingConvention = CallingConvention.StdCall)]
    public static extern void FftC(uint n, IntPtr data, IntPtr spectrum);

    [DllImport("./libMatxFft.so", CallingConvention = CallingConvention.StdCall)]
    public static extern void IFftC(uint n, IntPtr spectrum, IntPtr data);

    [DllImport("./libMatxFft.so", CallingConvention = CallingConvention.StdCall)]
    public static extern void Fft2C(uint w, uint h, IntPtr data, IntPtr spectrum);

    [DllImport("./libMatxFft.so", CallingConvention = CallingConvention.StdCall)]
    public static extern void IFft2C(uint w, uint h, IntPtr spectrum, IntPtr data);


    [DllImport("./libMatxFft.so", CallingConvention = CallingConvention.StdCall)]
    public static extern void FftCD(uint n, IntPtr data, IntPtr spectrum);

    [DllImport("./libMatxFft.so", CallingConvention = CallingConvention.StdCall)]
    public static extern void IFftCD(uint n, IntPtr spectrum, IntPtr data);

    [DllImport("./libMatxFft.so", CallingConvention = CallingConvention.StdCall)]
    public static extern void Fft2CD(uint w, uint h, IntPtr data, IntPtr spectrum);

    [DllImport("./libMatxFft.so", CallingConvention = CallingConvention.StdCall)]
    public static extern void IFft2CD(uint w, uint h, IntPtr spectrum, IntPtr data);


    // System.Numerics.Complex is double precision, define single precision complex number
    [StructLayout(LayoutKind.Sequential)]
    struct ComplexF
    {
        public float Real;

        public float Imag;

        public ComplexF(float real, float imag)
        {
            Real = real;
            Imag = imag;
        }
    }

    static int Main()
    {
        // start CUDA
        CreateCudaStream();

        Fft1DSample();
        Fft2DSample();

        // end CUDA
        DestroyCudaStream();

        return 0;
    }

    static void Fft1DSample()
    {
        const uint n = 32;

        /////////////////////////////////////////////////////////////////////////////////////////////////
        // single precision version

        // prepare data
        // dynamic allocation
        ComplexF[] cDataF = new ComplexF[n];
        ComplexF[] cSpectrumF = new ComplexF[n];
        for (int i = 0; i < n; i++)
        {
            cDataF[i] = new ComplexF(i, 0);
        }

        unsafe
        {
            fixed (ComplexF* d = cDataF, s = cSpectrumF)
            {
                // FFT -> IFFT
                FftC(n, new IntPtr(d), new IntPtr(s));
                IFftC(n, new IntPtr(s), new IntPtr(d));
            }
        }
        
        // print result
        for (int i = 0; i < n; i++)
        {
            Console.WriteLine(cDataF[i]);
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////
        // double precision version

        // prepare data
        // dynamic allocation
        Complex[] cData = new Complex[n];
        Complex[] cSpectrum = new Complex[n];
        for (int i = 0; i < n; i++)
        {
            cData[i] = new Complex(i, 0);
        }

        unsafe
        {
            fixed (Complex* d = cData, s = cSpectrum)
            {
                // FFT -> IFFT
                FftCD(n, new IntPtr(d), new IntPtr(s));
                IFftCD(n, new IntPtr(s), new IntPtr(d));
            }
        }
        
        // print result
        for (int i = 0; i < n; i++)
        {
            Console.WriteLine(cData[i]);
        }
    }

    static void Fft2DSample()
    {
        const uint w = 4;
        const uint h = 8;
        const uint length = w * h; 

        /////////////////////////////////////////////////////////////////////////////////////////////////
        // single precision version

        // prepare data (1D array)
	    // dynamic allocation
        ComplexF[] cImageF = new ComplexF[length];
        ComplexF[] cSpectrumF = new ComplexF[length];
        for (int i = 0; i < length; i++)
        {
            cImageF[i] = new ComplexF(i, 0);
        }
	
        // FFT -> IFFT
        unsafe
        {
            fixed (ComplexF* d = cImageF, s = cSpectrumF)
            {
                // FFT -> IFFT
                Fft2C(w, h, new IntPtr(d), new IntPtr(s));
                IFft2C(w, h, new IntPtr(s), new IntPtr(d));
            }
        }
        
        // print result
        for (int i = 0; i < length; i++)
        {
            Console.WriteLine(cImageF[i]);
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////
        // double precision version

        // prepare data (1D array)
	    // dynamic allocation
        Complex[] cImage = new Complex[length];
        Complex[] cSpectrum = new Complex[length];
        for (int i = 0; i < length; i++)
        {
            cImage[i] = new Complex(i, 0);
        }
	
        // FFT -> IFFT
        unsafe
        {
            fixed (Complex* d = cImage, s = cSpectrum)
            {
                // FFT -> IFFT
                Fft2CD(w, h, new IntPtr(d), new IntPtr(s));
                IFft2CD(w, h, new IntPtr(s), new IntPtr(d));
            }
        }
        
        // print result
        for (int i = 0; i < length; i++)
        {
            Console.WriteLine(cImage[i]);
        }
    }
}