#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "c_legendrecoefs.c"

static const float amsleg_pif = 3.14159265359;
static const double amsleg_pi = 3.14159265359;


float amsleg_polyvalf(float x, const float *coefs, int polyorder)
{
    float ret = 0.0f;
    float xv = 1.0f;
    int I;

    for(I=0;I<=polyorder;I++)
    {
        //printf("debug: %d %1.6g\n",I,coefs[I]);
        ret = ret + coefs[I]*xv;
        xv = xv * x;
    }

    return ret;
}

double amsleg_polyval(double x, const double *coefs, int polyorder)
{
    double ret = 0.0;
    double xv = 1.0;
    int I;

    for(I=0;I<=polyorder;I++)
    {
        ret = ret + coefs[I]*xv;
        xv = xv * x;
    }

    return ret;
}

//Schmidt Quasi-Normalized Legendre Function P^n_m(x)
//for degree n, and order m, m<=n
//n from 0 to 20
//m from 0 to n
float amsleg_legendref(float x, int n, int m)
{
    float ret = 0.0f;
    int I, mo2, mr, ind;

    float mpart = 1.0f;
    float ppart = 0.0f;

    const float *coefptr = NULL;

    if(x<-1.0f) x=-1.0f;
    if(x>1.0f) x = 1.0f;

    if(m<0)
    {
        m = -m;
    }
    if(m>n || n<0 || n>20)
    {
        ret = 0.0f;
        return ret;
    }

    if(m>0)
    {
        mo2 = m/2;
        mr = (m%2==1);
        for(I=0;I<mo2;I++)
        {
            mpart = mpart*(1.0f-x*x);
        }
        if(mr==1)
        {
            mpart = mpart*sqrt(1.0f-x*x);
        }
    }

    ind = n*(n+1)/2 + m;
    //printf("debug: ind=%d\n",ind);
    coefptr = &(amslegendrecoefsf[ind*(amslegendre_maxporder+1)]);

    // printf("debug ");
    // for(I=0;I<amslegendre_maxporder+1;I++)
    // {
    //     printf("%1.6g,",coefptr[I]);
    // }
    // printf("\n");

    ppart = amsleg_polyvalf(x,coefptr,amslegendre_maxporder);


    ret = ppart*mpart;

    return ret;
}

//Schmidt Quasi-Normalized Legendre Function P^n_m(x)
//for degree n, and order m, m<=n
//n from 0 to 20
//m from 0 to n
double amsleg_legendre(double x, int n, int m)
{
    double ret = 0.0;
    int I, mo2, mr, ind;

    double mpart = 1.0;
    double ppart = 0.0;

    const double *coefptr = NULL;

    if(x<-1.0f) x=-1.0f;
    if(x>1.0f) x = 1.0f;

    if(m<0)
    {
        m = -m;
    }
    if(m>n || n<0 || n>20)
    {
        ret = 0.0f;
        return ret;
    }

    if(m>0)
    {
        mo2 = m/2;
        mr = (m%2==1);
        for(I=0;I<mo2;I++)
        {
            mpart = mpart*(1.0f-x*x);
        }
        if(mr==1)
        {
            mpart = mpart*sqrt(1.0f-x*x);
        }
    }

    ind = n*(n+1)/2 + m;
    coefptr = &(amslegendrecoefs[ind*(amslegendre_maxporder+1)]);
    ppart = amsleg_polyval(x,coefptr,amslegendre_maxporder);


    ret = ppart*mpart;

    return ret;
}

float amsleg_rsphcosf(float theta,float phi,int n,int m)
{
    float ret = 0.0f;
    float p;

    ret = amsleg_legendref(cosf(theta),n,m)*cosf(m*phi);

    return ret;
}

float amsleg_rsphsinf(float theta,float phi,int n,int m)
{
    float ret = 0.0f;
    float p;
    
    ret = amsleg_legendref(cosf(theta),n,m)*sinf(m*phi);

    return ret;
}

float amsleg_dotprodf(int n1, int m1, int sc1, int n2, int m2, int sc2)
{
    float ret = 0.0f;
    float p1,p2;
    int Ntheta = 360;
    int Nphi = 720;
    int I,J;
    float dtheta,dphi,theta,phi;
    float domega = 0.0f;

    dtheta = amsleg_pif/(Ntheta);
    dphi = 2.0f*amsleg_pif/(Nphi);
    for(I=0;I<Ntheta;I++)
    {
        for(J=0;J<Nphi;J++)
        {
            theta = dtheta*I;
            phi = dphi*J;
            domega = dtheta*dphi*sin(theta);

            if(sc1==0)
                p1 = amsleg_rsphcosf(theta,phi,n1,m1);
            else
                p1 = amsleg_rsphsinf(theta,phi,n1,m1);

            if(sc2==0)
                p2 = amsleg_rsphcosf(theta,phi,n2,m2);
            else
                p2 = amsleg_rsphsinf(theta,phi,n2,m2);

            ret = ret + p1*p2*domega;
            //ret = ret + domega;
        }
    }

    return ret;
}

void amsleg_test1()
{
    int n = 20;
    int m = 20;
    float p;
    float x;

    printf("Test legendre fn %d %d \n",n,m);
    for(x=-1.0f; x<=1.005f; x = x + 0.01f)
    {
        p = amsleg_legendref(x,n,m);
        printf("%1.4g\t\t%1.4g\n",x,p);
    }

    return;
}

void amsleg_test2()
{
    int n1 = 3;
    int m1 = 1;

    int n2;
    int m2;

    float dp;

    //for Schmidt quasi normalized spherical harmonics:
    // 1/4pi * integral( C_mn C_MN sin(th)dthdphi ) = 1/(2n+1) del_nN del_mM
    // there is a factor of 1/(2n+1)

    printf("dot products with (%d,%d).\n",n1,m1);

    for(n2 = 0; n2<5; n2++)
    {
        for(m2 = 0; m2<=n2; m2++)
        {
            dp = amsleg_dotprodf(n2,m2,1,n2,m2,1);
            if(dp<1E-4) dp = 0;
            dp = dp/4.0f/amsleg_pif;
            dp = dp * (2*n2+1);
            printf("(%d,%d): %1.6g\n",n2,m2,dp);
        }
    }


}

int main(int argc, char* argv[])
{
    printf("Legendre Function Tests\n");

    amsleg_test2();

    return 0;
}

