using System;

namespace FullScreenShaders.Structure
{
    class KaiserKernel
    {
        const long BesselIterations = 100;

        static double Factorial(double n)
        {
            if (n == 0) return 1;
            return n * Factorial(n - 1);
        }

        static double Bessel(double z)
        {
            double sum = 0;

            for (long k = 0; k < BesselIterations; k++)
            {
                double denominator = Math.Pow(Factorial(k), 2);
                double numerator = Math.Pow(0.25 * Math.Pow(z, 2), k);
                double quotient = numerator / denominator;
                sum += quotient;
            }

            return sum;
        }

        static double[] Kaiser(int N, double alpha)
        {
            double denominator = Bessel(alpha);
            var window = new double[N / 2];

            for (int i = 0; i < N / 2; i++)
            {
                double n = i + N / 2 + 0.5;
                double numerator = Bessel(4 * Math.Sqrt(1 - Math.Pow(2 * n / N - 1, 2)));
                window[i] = numerator / denominator;
            }

            return window;
        }

        static double Sinc(double x)
        {
            return Math.Sin(Math.PI * x) / (Math.PI * x);
        }

        public static float[] GetWeights(int taps)
        {
            return GetWeights(taps, 4);
        }
        public static float[] GetWeights(int taps, double alpha)
        {
            double[] kaiserWindow = Kaiser(taps, alpha);
            int halfTaps = taps / 2;
            var tempWeights = new double[halfTaps];

            double sum = 0;
            for (int i = 0; i < halfTaps; i++)
            {
                tempWeights[i] = kaiserWindow[i] * Sinc(i + 0.5);
                sum += tempWeights[i];
            }

            var weights = new float[halfTaps];
            for (int i = 0; i < halfTaps; i++)
                weights[i] = (float)(tempWeights[i] * (0.5 / sum));

            return weights;
        }
    }
}
