import math

class LambertAzimuthal(object):
    """docstring for LambertAzimuthal"""
    def __init__(self, lat0,lon0):
        self.lat0 = lat0
        self.lon0 = lon0
        self.rd =math.pi/180.0
        self.phi0=lat0*self.rd
        self.ld0=lon0*self.rd

        self.s_p0=math.sin(self.phi0)
        self.c_p0=math.cos(self.phi0)


    def latlon2xy(self,lat,lon):
 
        ld=lon*self.rd
        phi=lat*self.rd

        s_p=math.sin(phi)
        c_p=math.cos(phi)

        l=ld-self.ld0
        c_l=math.cos(l)
        s_l=math.sin(l)

        c_pl=c_p*c_l

        w=math.sqrt(2.0/(max(1.0+ self.s_p0*s_p + self.c_p0*c_pl, 1.0e-10) ))
        x=c_p*s_l*w
        y=(self.c_p0*s_p -self.s_p0*c_pl)*w

        return [x,y]

    def xy2latlon(self,x,y):

        p=max(math.sqrt(x**2+y**2),1.0e-10)
        c=2.0*math.asin(p/2.0)

        s_c=math.sin(c)
        c_c=math.cos(c)

        phi=math.asin(c_c*self.s_p0 + y*s_c*self.c_p0/p)
        ld=self.ld0+ math.atan2(x*s_c,(p*self.c_p0*c_c - y*self.s_p0*s_c))

        lat=phi/self.rd
        lon=ld/self.rd

        if abs(lat)>90.0:
            if lat > 0:
                lat=lat -180
            else:
                lat=lat +180

        if abs(lon)>180.0:
            if lon > 0:
                lon=lon -360
            else:
                lon=lon +360

        return [lat,lon]


class GroundPixel(object):
    """docstring for GroundPixel"""
    def __init__(self, c_latlon, e_latlon):
        self.c_latlon=c_latlon
        self.e_latlon=e_latlon
        self.e_xy=[]

        #projection of the ground pixel edges to x and y
        self.la = LambertAzimuthal(c_latlon[0],c_latlon[1])
        for i in range(4):
            edge=e_latlon[i]
            rs=self.la.latlon2xy(edge[0],edge[1])
            self.e_xy.append((rs[0],rs[1]))


        #sort edges
        self.e_xy.sort(key=lambda p: math.atan2(p[1],p[0]))

        #rotation correction

        dx10=self.e_xy[1][0] - self.e_xy[0][0]
        dy10=self.e_xy[1][1] - self.e_xy[0][1]

        dx23=self.e_xy[2][0] - self.e_xy[3][0]
        dy23=self.e_xy[2][1] - self.e_xy[3][1]

        self.alpha= math.atan2((dy10 + dy23)/2.0, (dx10 + dx23)/2.0)
        self.c_a=math.cos(self.alpha)
        self.s_a=math.sin(self.alpha)

        dx30=self.e_xy[3][0] - self.e_xy[0][0]
        dy30=self.e_xy[3][1] - self.e_xy[0][1]

        dx21=self.e_xy[2][0] - self.e_xy[1][0]
        dy21=self.e_xy[2][1] - self.e_xy[1][1]

        self.beta= math.atan2((dy30 + dy21)/2.0, (dx30 + dx21)/2.0)
        self.c_b=math.cos(self.beta)
        self.s_b=math.sin(self.beta)

        self.distx = math.hypot(dx10, dy10)
        self.disty = math.hypot(dx30, dy30)


    def get_points(self,step_km_x,step_km_y):
        """docstring for get_points"""
        ix=step_km_x/6371.0
        iy=step_km_y/6371.0

        if self.distx <= ix:
            step_x=0
            ix=0
            px=self.e_xy[0][0] + 0.5*self.distx*self.c_a
            py=self.e_xy[0][1] + 0.5*self.distx*self.s_a

        else:
            step_x= int(self.distx/ix +0.5)
            px=self.e_xy[0][0]
            py=self.e_xy[0][1]
            ix=self.distx/step_x

        if self.disty <= iy:
            step_y=0
            iy=0
            px=px + 0.5*self.disty*self.c_b
            py=py + 0.5*self.disty*self.s_b
        else:
            step_y= int(self.disty/iy +0.5)
            iy=self.disty/step_y

        result=[]
        for i in range(step_x+1):
            for j in range(step_y+1):

                p=(px + j*iy*self.c_b +  i*ix*self.c_a ,  j*iy*self.s_b +  py +  i*ix*self.s_a)
                tmp=self.la.xy2latlon(p[0],p[1])
                result=result+[tmp]


        return result

