using System;
using Unity.Mathematics;

namespace UnityEngine.PathTracing.Core
{
    static internal class SphericalHarmonicsUtil
    {
        // 1/2 * sqrt(1/π)
        const float SH_L0_Normalization = 0.2820947917738781434740397257803862929220253146644994284220428608f;
        // 1/2 * sqrt(3/π)
        const float SH_L1_Normalization = 0.4886025119029199215863846228383470045758856081942277021382431574f;
        // sqrt(15/π)/2
        const float SH_L2_2_Normalization = 1.0925484305920790705433857058026884026904329595042589753478516999f;
        // sqrt(15/π)/2
        const float SH_L2_1_Normalization = SH_L2_2_Normalization;
        // sqrt(5/π)/4
        const float SH_L20_Normalization = 0.3153915652525200060308936902957104933242475070484115878434078878f;
        // sqrt(15/π)/2
        const float SH_L21_Normalization = SH_L2_2_Normalization;
        // sqrt(15/π)/4
        const float SH_L22_Normalization = 0.5462742152960395352716928529013442013452164797521294876739258499f;


        // Basis function Y_0.
        static float SHL0()
        {
            return SH_L0_Normalization;
        }

        // Basis function Y_1,-1.
        static float SHL1_1(float3 direction)
        {
            return SH_L1_Normalization * direction.x;
        }

        // Basis function Y_1,0.
        static float SHL10(float3 direction)
        {
            return SH_L1_Normalization * direction.y;
        }

        // Basis function Y_1,1.
        static float SHL11(float3 direction)
        {
            return SH_L1_Normalization * direction.z;
        }

        // Basis function Y_2,-2.
        static float SHL2_2(float3 direction)
        {
            return SH_L2_2_Normalization * direction.x * direction.y;
        }

        // Basis function Y_2,-1.
        static float SHL2_1(float3 direction)
        {
            return SH_L2_1_Normalization * direction.y * direction.z;
        }

        // Basis function Y_2,0.
        static float SHL20(float3 direction)
        {
            return SH_L20_Normalization * (3.0f * direction.z * direction.z - 1.0f);
        }

        // Basis function Y_2,1.
        static float SHL21(float3 direction)
        {
            return SH_L21_Normalization * direction.x * direction.z;
        }

        // Basis function Y_2,2.
        static float SHL22(float3 direction)
        {
            return SH_L22_Normalization * (direction.x * direction.x - direction.y * direction.y);
        }

        static public float3 EvaluateSH(Span<float> sh, float3 direction)
        {
            float3 res = new float3();
            res += new float3(sh[0], sh[9] , sh[18]) * SHL0();
            res += new float3(sh[1], sh[10], sh[19]) * SHL1_1(direction);
            res += new float3(sh[2], sh[11], sh[20]) * SHL10(direction);
            res += new float3(sh[3], sh[12], sh[21]) * SHL11(direction);
            res += new float3(sh[4], sh[13], sh[22]) * SHL2_2(direction);
            res += new float3(sh[5], sh[14], sh[23]) * SHL2_1(direction);
            res += new float3(sh[6], sh[15], sh[24]) * SHL20(direction);
            res += new float3(sh[7], sh[16], sh[25]) * SHL21(direction);
            res += new float3(sh[8], sh[17], sh[26]) * SHL22(direction);
            return res;
        }
    }
}