#!/usr/bin/env python
# coding: utf-8

from datetime import datetime
import os
import time

import netCDF4
import numpy as np
import rasterio
import shapely

import orbitsim

from . import config as cfg


r_earth = 6371.0


def geo_distance(lon0, lat0, lon1, lat1):
    """
    Compute distance between to geographical coordinates
    see: http://en.wikipedia.org/wiki/Great-circle_distance
    """
    DEG2RAD = 0.017453292519943295

    d = DEG2RAD * (lon1 - lon0)
    s = DEG2RAD * lat0
    f = DEG2RAD * lat1

    cd = np.cos(d)
    sd = np.sin(d)
    cf = np.cos(f)
    sf = np.sin(f)
    cs = np.cos(s)
    ss = np.sin(s)

    return r_earth * np.atan2(
        np.sqrt((cf * sd)**2 + (cs * sf - ss * cf * cd)**2),
        ss * sf + cs * cf * cd
    )


def sunsync_inclination(trev):
    """\
    Calculate inclination of sun-synchronous orbit from revolution time
    (source: https://en.wikipedia.org/wiki/Sun-synchronous_orbit)

    trev :: time for revolution in days
    """
    return np.rad2deg(np.arccos(-(trev * 24. / 3.795)**(7.0/3.0)))


def incl2trev(incl):
    return (-(np.cos(np.deg2rad(incl)) * 3.795**(7.0/3.0)))**(3.0/7.0) / 24.0



def pixel_size(filename, binning=None):
    s = orbitsim.Orbit_sim.from_file(filename)
    s.phase_start = 0.49
    s.phase_end = 0.51
    s.calculate_revolution()
    _, _, lon, lat = reshape_orbit(s)

    dx = [geo_distance(lon[10,i,0], lat[10,i,0], lon[10,i,1], lat[10,i,1]) for i in range(s.nrow)]
    dy = [geo_distance(lon[10,i,0], lat[10,i,0], lon[10,i,-1], lat[10,i,-1] ) for i in range(s.nrow)]
    dx, dy = np.array(dx), np.array(dy)
    x = np.arange(dx.size) - dx.size / 2.0

    return x, dx, dy


def plot_pixel_size(filename):
    x, dx, dy = pixel_size(filename)

    fig = plt.figure(figsize=(5.0, 3.0))
    ax = fig.add_subplot(111)

    ax.plot(x,dx, '.-', label='across')
    ax.plot(x,dy, '.-', label='along')

    ax.set_xlabel('across-track position')
    ax.set_ylabel('pixel size (km)')
    ax.set_ylim(0, 40)
    ax.grid(True)

    plt.yticks(np.arange(0,41,5))
    plt.legend()
    plt.tight_layout()



def reshape_orbit(s, overwrite=False):
    """\
    Reshape orbit lon/lat to along/across position.
    """
    if overwrite:
        s.utc = s.utc.reshape(-1, s.nrow, 7)

        if hasattr(s, 'times'):
            s.times = s.times.reshape(-1, s.nrow)

        s.lat_center = s.lat_center.reshape(-1, s.nrow)
        s.lon_center = s.lon_center.reshape(-1, s.nrow)

        s.lat_corner = s.lat_corner.reshape(-1, s.nrow, 4)
        s.lon_corner = s.lon_corner.reshape(-1, s.nrow, 4)

        s.sza = s.sza.reshape(-1, s.nrow)
        s.vza = s.vza.reshape(-1, s.nrow)
        s.saz = s.saz.reshape(-1, s.nrow)
        s.vaz = s.vaz.reshape(-1, s.nrow)

        s.nobs = s.nobs // s.nrow

        return s

    else:
        lat = s.lat_center.reshape(-1,s.nrow)
        lon = s.lon_center.reshape(-1,s.nrow)
        lat_corner = s.lat_corner.reshape(-1,s.nrow,4)
        lon_corner = s.lon_corner.reshape(-1,s.nrow,4)

        return lon, lat, lon_corner, lat_corner



def calculate_pixels(scenario_filename, orbit, phase_start=0.33, phase_end=0.37,
        longitude_eq=None, do_reshape=False):
    """\
    Calculate ground pixels for satellite scenario.
    """
    s = orbitsim.Orbit_sim.from_file(scenario_filename)
    s.phase_start = orbit + phase_start
    s.phase_end = orbit + phase_end

    if longitude_eq is not None:
        s.longitude_eq = np.deg2rad(longitude_eq)

    s.calculate_revolution()

    # add total seconds since 2015-01-01
    s.times = np.array([
        (datetime(*s.utc[i]) - datetime(2015,1,1)).total_seconds()
        for i in range(s.nobs)
    ])

    if do_reshape:
        s = reshape_orbit(s, True)

    return s



def save_orbit(orb, scenario_name, orbit_number, output_path):
    """\
    Save orbit to netCDF file.

    name:
     - Sentinel_7_CO2
     - Sentinel_7_NO2_1km
     - Sentinel_7_NO2_2km
     - Sentinel_7_NO2_4km
     - Sentinel_5
    """
    filename = os.path.join(
            output_path,
            '%s_o%03d_l%03d_pixel.nc' % (scenario_name, orbit_number, orb.longitude_eq)
    )

    with netCDF4.Dataset(filename, 'w', format='NETCDF4') as nc:

        # global attributes (add input data)
        nc.setncatts({
            'DESCRIPTION':  'Orbit information for %s instrument' % scenario_name,
            'DATAORIGIN':   ('SRON Orbit Simulator by Joost Aan de Brugh '
                            '(J.M.J.aan.de.Brugh@sron.nl)'),
            'CREATOR':      'Gerrit Kuhlmann',
            'EMAIL':        'gerrit.kuhlmann@empa.ch',
            'AFFILIATION':  'Empa Duebendorf, Switzerland',
            'DATE CREATED': time.ctime(time.time()),
            'STUDY':        'ESA project - Smartcarb',
        })
        nc.createVariable('input', 'c').setncatts({
            # satellite
            'time_for_one_revolution_in_hours': orb.trev * 24.0,
            'satellite_height_in_km': orb.altitude - r_earth,
            'local_solar_time_at_the_northward_equator_pass_in_hours': orb.t_node,
            'orbital_inclination_angle_in_degrees': np.rad2deg(orb.inclination),

            # revolution
            'date': '%04d-%02d-%02d' % tuple(orb.date),
            'longitude_at_daytime_equator_pass_in_degrees': np.rad2deg(orb.longitude_eq),

            # scanlines
            'orbit_phase_at_start_of_scanning': orb.phase_start,
            'orbit_phase_at_end_of_scanning': orb.phase_end,
            'time_per_scan_in_seconds': orb.tsamp,

            # swath
            'number_of_detector_rows': orb.nrow,
            'polynomial_parameters_for_alpha': ', '.join(str(v) for v in orb.polyterms_alpha),
            'polynomial_parameters_for_beta': ', '.join(str(v) for v in orb.polyterms_beta),
        })


        # create dimensions
        nobs = nc.createDimension('nobs', orb.nobs / orb.nrow)
        nrows = nc.createDimension('nrows', orb.nrow)
        ncorner = nc.createDimension('ncorners', orb.ncorner)

        #create variables
        times = nc.createVariable('time', 'f8', ('nobs','nrows'))
        lat = nc.createVariable('latitude', 'f4', ('nobs', 'nrows'))
        lon = nc.createVariable('longitude', 'f4', ('nobs', 'nrows'))
        lat_corner = nc.createVariable('latitude_corners', 'f4', ('nobs', 'nrows', 'ncorners'))
        lon_corner = nc.createVariable('longitude_corners', 'f4', ('nobs', 'nrows', 'ncorners'))
        sza = nc.createVariable('solar_zenith_angle', 'f4', ('nobs', 'nrows'))
        vza = nc.createVariable('viewing_zenith_angle', 'f4', ('nobs', 'nrows'))
        vaz = nc.createVariable('viewing_azimuth_angle', 'f4', ('nobs', 'nrows'))
        saz = nc.createVariable('solar_azimuth_angle', 'f4', ('nobs', 'nrows'))

        # Assign an attribute to the dataset
        sza.standard_name = "solar_zenith_angle"
        sza.long_name = "solar zenith angle"
        sza.units = "degree"
        vza.standard_name = "viewing_zenith_angle"
        vza.long_name = "viewing zenith angle"
        vza.units = "degree"
        vaz.standard_name = "viewing_azimuth_angle"
        vaz.long_name = "viewing azimuth angle"
        vaz.units = "degree"
        saz.standard_name = "solar_azimuth_angle"
        saz.long_name = "solar azimuth angle"
        saz.units = "degree"
        times.standard_name = "time_of_measurement"
        times.long_name = "time of measurement since 2015-01-01 00:00:00 "
        times.units = "seconds since 2015-01-01 00:00:00"
        lat.standard_name = "latitude"
        lat.long_name = "pixel center latitude"
        lat.units = "degrees north"
        lon.standard_name = "longitude"
        lon.long_name = "pixel center longitude"
        lon.units = "degrees east"
        lat_corner.standard_name = "latitude"
        lat_corner.long_name = "pixel corners latitude"
        lat_corner.units = "degrees north"
        lon_corner.standard_name = "longitude"
        lon_corner.long_name = "pixel corners longitude"
        lon_corner.units = "degrees east"

        # Write data
        times[:] = np.array([
                    (datetime(*orb.utc[i]) - datetime(2015,1,1)).total_seconds()
                    for i in range(orb.nobs)
        ]).reshape(-1, orb.nrow)

        lat[:,:] = orb.lat_center.reshape(-1,orb.nrow)
        lon[:,:] = orb.lon_center.reshape(-1,orb.nrow)
        lat_corner[:,:,:] = orb.lat_corner.reshape(-1,orb.nrow,4)
        lon_corner[:,:,:] = orb.lon_corner.reshape(-1,orb.nrow,4)
        sza[:,:] = orb.sza.reshape(-1,orb.nrow)
        vza[:,:] = orb.vza.reshape(-1,orb.nrow)
        saz[:,:] = orb.saz.reshape(-1,orb.nrow)
        vaz[:,:] = orb.vaz.reshape(-1,orb.nrow)



def in_domain(s, domain=cfg.DOMAIN):
    """\
    Checks if any pixel center is inside model domain.
    """
    lon, lat, _, _ = reshape_orbit(s)

    #TODO: check for problems with dateline
    lon = np.array([lon[i,j] for i,j in [(0,0), (0,-1), (-1,-1), (-1,0)]])
    lat = np.array([lat[i,j] for i,j in [(0,0), (0,-1), (-1,-1), (-1,0)]])
    orbit = create_pixel(lon, lat)

    return orbit.intersects(domain)



def compute_orbit(name, orbit_number, long_eq=None, do_reshape=False,
        write_to_file=False, output_root='.'):
    """\
    Compute a single orbit and save to netCDF file.

    names:
     - Sentinel_7_CO2
     - Sentinel_7_NO2_1km
     - Sentinel_7_NO2_2km
     - Sentinel_7_NO2_4km
     - Sentinel_5

    orbit_number:
     - 0-412 for Sentinel-5
     - 0-164 for Sentinel-7_*
    """
    scenario_filename = os.path.join(
        '/users/gkuhl/projects/smartcarb/analysis_src/config/',
        '%s.txt' % name
    )

    # Sentinel-7
    if name.startswith('Sentinel_7'):
        phase = 0.33, 0.37

    # Sentinel-5
    elif name.startswith('Sentinel_5'):
        phase = 0.32, 0.37


    s = calculate_pixels(scenario_filename, orbit_number, phase[0], phase[1], long_eq)



    if write_to_file:
        output_path = os.path.join(output_root, name)
        try:
            os.makedirs(output_path)
        except FileExistsError:
            pass
        save_orbit(s, name, orbit_number, output_path)

    if do_reshape:
        s = reshape_orbit(s, overwrite=True)

    return s


if __name__ == '__main__':
    pass







