using FxpConvert.Common;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;

namespace BaseCpuFxpConvert
{
    public sealed class CpuCalc : ICalc
    {
        public IAdd Add { get; } = new CpuAdd();
        public ISubtract Subtract { get; } = new CpuSubtract();
        public IMultiply Multiply { get; } = new CpuMultiply();
        public IDivision Division { get; } = new CpuDivision();
        public IFFT FFT { get; } = new CpuFFT();
        public IArraySum Sum { get; } = new CpuArraySum();

        public unsafe void Fill(ref float result, float value, uint count)
        {
            if (count == 0) return;
            if(value ==0)
            {
                Unsafe.InitBlock(ref Unsafe.As<float, byte>(ref result), 0, count * 4);
                return;
            }
            Span<float> temp = new Span<float>(Unsafe.AsPointer(ref result),(int)count);
            temp.Fill(value);
        }
    }

    public sealed class CpuArraySum : IArraySum
    {
        public unsafe float Rms(ref float value, uint count)
        {
            if (count == 0) return 0;
            if (count == 1) return value;
            float temp = value*value;
            float* ptr = (float*)Unsafe.AsPointer(ref value);
            for(int i=1;i<count;i++)
            {
                temp += (ptr[i] * ptr[i]);
            }
            return MathF.Sqrt(temp / count);
        }

        public unsafe double Rms(ref double value, uint count)
        {
            if (count == 0) return 0;
            if (count == 1) return value;
            double temp = value * value;
            double* ptr = (double*)Unsafe.AsPointer(ref value);
            for (int i = 1; i < count; i++)
            {
                temp += (ptr[i] * ptr[i]);
            }
            return Math.Sqrt(temp / count);
        }

        public unsafe float Sum(ref float value, uint count)
        {
            if (count == 0) return 0;
            if (count == 1) return value;
            float* ptr = (float*)Unsafe.AsPointer(ref value);
            float temp = value;
            for(int i=1;i<count;i++)
            {
                temp += ptr[i];
            }
            return temp;
        }

        public unsafe double Sum(ref double value,uint count)
        {
            if (count == 0) return 0;
            if(count ==1)return value;
            double* ptr = (double*)Unsafe.AsPointer(ref value);
            double temp = value;
            for(int i=1;i<count;i++)
            {
                temp += ptr[i];
            }
            return temp;
        }
    }
    public sealed class CpuFFT:IFFT
    {
        public void FFT(double[] real, double[] imaginary)
        {
            MathNet.Numerics.IntegralTransforms.Fourier.Forward(real, imaginary, MathNet.Numerics.IntegralTransforms.FourierOptions.Matlab);
        }
        public void FFT(float[] real, float[] imaginary)
        {
            MathNet.Numerics.IntegralTransforms.Fourier.Forward(real, imaginary, MathNet.Numerics.IntegralTransforms.FourierOptions.Matlab);
        }

    }

    public sealed class CpuSubtract : ISubtract
    {
        public void Subtract(ref float left, float right, uint count, ref float result)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref result, i);
                temp = Unsafe.Add(ref left, i) - right;
            }
        }

        public void Subtract(ref float left, ref float right, uint count, ref float result)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref result, i);
                temp = Unsafe.Add(ref left, i) - Unsafe.Add(ref right, i);
            }
        }

        public void Subtract(ref float left, float right, uint count)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref left, i);
                temp -= right;
            }
        }

        public void Subtract(ref float left, ref float right, uint count)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref left, i);
                temp -= Unsafe.Add(ref right, i);
            }
        }
    }
    public sealed class CpuAdd:IAdd
    {
        public void Add(ref float left, float right, uint count, ref float result)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref result, i);
                temp = Unsafe.Add(ref left, i) + right;
            }
        }

        public void Add(ref float left, ref float right, uint count, ref float result)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref result, i);
                temp = Unsafe.Add(ref left, i) + Unsafe.Add(ref right, i);
            }
        }

        public void Add(ref float left, float right, uint count)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref left, i);
                temp += right;
            }
        }

        public void Add(ref float left, ref float right, uint count)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref left, i);
                temp += Unsafe.Add(ref right, i);
            }
        }
    }

    public sealed class CpuMultiply : IMultiply
    {
        public void Multiply(ref float left, float right, uint count, ref float result)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref result, i);
                temp = Unsafe.Add(ref left, i) * right;
            }
        }

        public void Multiply(ref float left, ref float right, uint count, ref float result)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref result, i);
                temp = Unsafe.Add(ref left, i) * Unsafe.Add(ref right, i);
            }
        }

        public void Multiply(ref float left, float right, uint count)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref left, i);
                temp *= right;
            }
        }

        public void Multiply(ref float left, ref float right, uint count)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref left, i);
                temp *= Unsafe.Add(ref right, i);
            }
        }
    }


    public sealed class CpuDivision : IDivision
    {
        public void Division(ref float left, float right, uint count, ref float result)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref result, i);
                temp = Unsafe.Add(ref left, i) / right;
            }
        }

        public void Division(ref float left, ref float right, uint count, ref float result)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref result, i);
                temp = Unsafe.Add(ref left, i) / Unsafe.Add(ref right, i);
            }
        }

        public void Division(ref float left, float right, uint count)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref left, i);
                temp /= right;
            }
        }

        public void Division(ref float left, ref float right, uint count)
        {
            if (count == 0) return;
            for (int i = 0; i < count; i++)
            {
                ref float temp = ref Unsafe.Add(ref left, i);
                temp /= Unsafe.Add(ref right, i);
            }
        }
    }
}
