# -*- coding: utf-8 -*-

"""
HyTools:  Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin

Authors: Adam Chlus, Zhiwei Ye, Philip Townsend.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

Topographic correction

"""
import json
import numpy as np
import ray
from .modminn import apply_modminn,calc_modminn_coeffs
from .scsc import apply_scsc,calc_scsc_coeffs, calc_scsc_coeffs_group
from .cosine import apply_cosine,calc_cosine_coeffs
from .c import apply_c,calc_c_coeffs, calc_c_coeffs_group
from .scs import apply_scs,calc_scs_coeffs
from ..masks import mask_create
from ..misc import set_topo

def calc_cosine_i(solar_zn, solar_az, aspect ,slope):
    """Generate cosine i image. The cosine of the incidence angle (i) is
       defined as the angle between the normal to the pixel surface
       and the solar zenith direction.
       All input geometry units must be in radians.

    Args:
        solar_az (numpy.ndarray): Solar azimuth angle.
        solar_zn (numpy.ndarray): Solar zenith angle.
        aspect (numpy.ndarray): Ground aspect.
        slope (numpy.ndarray): Ground slope.

    Returns:
        cnumpy.ndarray: Cosine i image.

    """

    relative_az = aspect - solar_az
    cosine_i = np.cos(solar_zn)*np.cos(slope) + np.sin(solar_zn)*np.sin(slope)*  np.cos(relative_az)
    return cosine_i

def apply_topo_correct(hy_obj,data,dimension,index):
    '''

    Args:
        hy_obj (TYPE): DESCRIPTION.
        band (TYPE): DESCRIPTION.
        index (TYPE): DESCRIPTION.

    Returns:
        band (TYPE): DESCRIPTION.

    '''

    if ('apply_topo' not in hy_obj.mask) & ('apply_mask' in hy_obj.topo):
        hy_obj.gen_mask(mask_create,'apply_topo',hy_obj.topo['apply_mask'])

    if hy_obj.topo['type'] == 'mod_minneart':
        data = apply_modminn(hy_obj,data,dimension,index)
    elif hy_obj.topo['type']  == 'scs+c':
        data = apply_scsc(hy_obj,data,dimension,index)
    elif hy_obj.topo['type']  == 'cosine':
        data = apply_cosine(hy_obj,data,dimension,index)
    elif hy_obj.topo['type']  == 'c':
        data = apply_c(hy_obj,data,dimension,index)
    elif hy_obj.topo['type']  == 'scs':
        data = apply_scs(hy_obj,data,dimension,index)
    return data

def load_topo_precomputed(hy_obj,topo_dict):
    with open(topo_dict['coeff_files'][hy_obj.file_name], 'r') as outfile:
        hy_obj.topo = json.load(outfile)

def get_topo_sample_mask(hy_obj,topo_dict):

    sample_ratio = float(topo_dict["sample_perc"])

    subsample_mask = np.copy(hy_obj.mask['calc_topo'])

    idx = np.array(np.where(subsample_mask!=0)).T

    if idx.shape[0]>5:
        idxRand= idx[np.random.choice(range(len(idx)),int(len(idx)*(1-sample_ratio)), replace = False)].T
        subsample_mask[idxRand[0],idxRand[1]] = 0

    subsample_mask = subsample_mask.astype(np.int8)

    hy_obj.ancillary['sample_mask']=subsample_mask

def calc_topo_coeffs(actors,topo_dict,actor_group_list=None,group_tag_list=None):
#def calc_topo_coeffs(actors,actor_group_list,topo_dict,group_tag_list):
    if topo_dict['type'] == 'precomputed':
        print("Using precomputed topographic coefficients.")
        _ = ray.get([a.do.remote(load_topo_precomputed,topo_dict) for a in actors]) # actors

        #_ = ray.get([a.do.remote(lambda x: x.corrections.append('topo')) for a in actors])

    else:
        print("Calculating topographic coefficients.")

        _ = ray.get([a.do.remote(set_topo,topo_dict) for a in actors])

        _ = ray.get([a.gen_mask.remote(mask_create,'calc_topo',
                                       topo_dict['calc_mask']) for a in actors])

        if (actor_group_list is None) or (topo_dict['type'] in ['scs','mod_minneart','cosine']):
        # no grouping
            if topo_dict['type'] == 'scs+c':
                _ = ray.get([a.do.remote(calc_scsc_coeffs,topo_dict) for a in actors])

            elif topo_dict['type'] == 'scs':
                _ = ray.get([a.do.remote(calc_scs_coeffs,topo_dict) for a in actors])

            elif topo_dict['type'] == 'mod_minneart':
                _ = ray.get([a.do.remote(calc_modminn_coeffs,topo_dict) for a in actors])

            elif topo_dict['type'] == 'cosine':
                _ = ray.get([a.do.remote(calc_cosine_coeffs,topo_dict) for a in actors])

            elif topo_dict['type'] == 'c':
                _ = ray.get([a.do.remote(calc_c_coeffs,topo_dict) for a in actors])

            #_ = ray.get([a.do.remote(lambda x: x.corrections.append('topo')) for a in actors])

        else:

            _ = ray.get([a.do.remote(get_topo_sample_mask,topo_dict) for a in actors])

            for group_order, sub_actors in enumerate(actor_group_list):

                #return 0

                if topo_dict['type'] == 'scs+c':
                    calc_scsc_coeffs_group(sub_actors,topo_dict,group_tag_list[group_order])

                elif topo_dict['type'] == 'c':
                    calc_c_coeffs_group(sub_actors,topo_dict,group_tag_list[group_order])

    _ = ray.get([a.do.remote(lambda x: x.corrections.append('topo')) for a in actors])


def calc_topo_coeffs_single(hy_obj,topo_dict):

    if topo_dict['type'] == 'precomputed':
        print("Using precomputed topographic coefficients.")
        load_topo_precomputed(hy_obj,topo_dict)

    else:
        print("Calculating topographic coefficients.")

        hy_obj.gen_mask(mask_create,'calc_topo',topo_dict['calc_mask'])


        if topo_dict['type'] == 'scs+c':
            calc_scsc_coeffs(hy_obj,topo_dict)

        elif topo_dict['type'] == 'scs':
            calc_scs_coeffs(hy_obj,topo_dict)

        elif topo_dict['type'] == 'mod_minneart':
            calc_modminn_coeffs(hy_obj,topo_dict)

        elif topo_dict['type'] == 'cosine':
            calc_cosine_coeffs(hy_obj,topo_dict)

        elif topo_dict['type'] == 'c':
            calc_c_coeffs(hy_obj,topo_dict)

    hy_obj.corrections.append('topo')
