import os,sys,math
import numpy as np

factorial = math.factorial
comb = math.comb
perm = math.perm
#print (math.comb(n, k))

from .amspolyfuns import poly_pow
from .amspolyfuns import poly_derivative
from .amspolyfuns import polyval

from .amslegendre import *

#import scipy as sp
#import scipy.interpolate as interp
#scipy.interpolate.LinearNDInterpolator
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.LinearNDInterpolator.html#scipy.interpolate.LinearNDInterpolator

def sphericalharmonic_polysin(L,M):
    """
    Returns sin(theta) power and normed cosine polynomial for spherical harmonic
    Return: [spower, polynomial]
    Y^M_L(theta,phi) = sin(theta)^spower * poly(cos(theta)) * exp(i*M*phi)
    """

    L = int(L)
    M = int(M)
    Ma = abs(M)

    polysin = [0, [0.0]]
    if(L<0):
        return polysin
    if(abs(M)>L):
        return polysin

    Nrm = np.sqrt((2*L+1)/(4*np.pi) * math.factorial(L-Ma)/math.factorial(L+Ma))*((-1)**Ma)
    p = alegendre_polypart(L,Ma)
    p = p*Nrm

    polysin = [Ma, p]

    return polysin

def sphericalharmonic_eval(theta, phi, L, M):
    """
    Returns the values of the spherical harmonic Y^M_L(theta,phi)
    Y is normalized such that <Y^M_L, Y^M_L> over the unit sphere = 1
    Integral(conj(Y^M1_L1)*Y^M2_L2 * sin(theta)*dtheta*dphi) = del(L1,L2)del(M1,M2)
    """

    L = int(L)
    M = int(M)
    Ma = abs(M)

    theta = np.array(theta)
    phi = np.array(phi)

    if(theta.shape != phi.shape):
        print("sphericalharmonic_eval: error: theta and phi must have the same shape.")
        vals = np.array([])
        return vals

    vals = np.zeros(theta.shape)
    if(L<0):
        return vals
    if(abs(M)>L):
        return vals

    [spower, poly] = sphericalharmonic_polysin(L,M)

    vals = polyval(np.cos(theta),poly)
    vals = vals*(np.sin(theta)**Ma)
    vals = vals*np.exp(1j * phi * M)
    
    return vals

def gso_cosharmonic(theta,phi,n,m):
    """
    Returns grahm-schmidt semi-normalized cosine spherical harmonics

    y = P^n_m(cos(theta))*cos(m*phi)
    P^n_m(cos(theta)) is the graham-schmidt semi-normalized 
    associated legendre function described in
    Winch et. al. 2005
    """
    theta = np.array(theta)
    phi = np.array(phi)
    n = int(n)
    m = int(m)

    alegendre = gso_cosinealegendre(theta,m,n)
    y = alegendre*np.cos(m*phi)


    return y

def gso_sinharmonic(theta,phi,n,m):
    """
    Returns grahm-schmidt semi-normalized sine spherical harmonics

    y = P^n_m(cos(theta))*sin(m*phi)
    P^n_m(cos(theta)) is the graham-schmidt semi-normalized 
    associated legendre function described in
    Winch et. al. 2005
    """
    theta = np.array(theta)
    phi = np.array(phi)
    n = int(n)
    m = int(m)

    alegendre = gso_cosinealegendre(theta,m,n)
    y = alegendre*np.sin(m*phi)

    return y
