"""Write calculate solution results

Description:
------------


"""
# Standard library imports
from collections import namedtuple

# External library imports
import numpy as np

# Midgard imports
from midgard.dev import plugins
from midgard.writers._writers import get_existing_fields, get_field, get_header

# Where imports
import where
from where.lib import config
from where.lib import util

WriterField = namedtuple(
    "WriterField", ["name", "field", "attrs", "dtype", "format", "width", "header", "unit", "description"]
)
WriterField.__new__.__defaults__ = (None,) * len(WriterField._fields)
WriterField.__doc__ = """A convenience class for defining a output field for the writer

    Args:
        name  (str):             Unique field name
        field (str):             Dataset field name
        attrs (Tuple[str]):      Field attributes
        dtype (Numpy dtype):     Type of field
        format (str):            Format string
        width (int):             Width of header information
        header (str):            Header information
        unit (str):              Unit of field
        description (str):       Description of field
    """

# Define fields to plot
#
# #
# #  SAT               EPOCH    AZI   ELEV          RANGE        SAT_CLK    TROPO  REL_CLK     IONO      TGD            RES
# #      YYYY/MM/DD hh:mm:ss    deg    deg          meter          meter    meter    meter    meter    meter          meter
# # _______________________________________________________________________________________________________________________
#   E02  2019/02/01 00:00:00  -95.2   42.3   24946173.069     -13692.221    3.477    0.180    0.468   -2.303         -1.350
#   E04  2019/02/01 00:00:00  110.7   23.9   26442907.456      63488.395    5.755   -0.037    1.015   -2.583         -0.978
#   E11  2019/02/01 00:00:00   94.2   71.3   23507989.761   -1714889.338    2.473    0.213    0.405   -6.003         -0.520
#   E12  2019/02/01 00:00:00   84.4   17.7   27053455.379   -1819105.131    7.619   -0.066    0.949   -4.886         -0.920
# ----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----+----9----+----0----+----1------
#
FIELDS = (
    WriterField("satellite", "satellite", (), object, "%5s", 4, "SAT", "", "Satellite number"),
    WriterField(
        "date",
        "date",
        (),
        object,
        "%21s",
        20,
        "EPOCH",
        "YYYY/MM/DD hh:mm:ss",
        "Date in format year, month, day and hour, minute and second",
    ),
    WriterField("mjd", "time", ("gps", "mjd"), float, "%14.6f", 14, "MJD", "", "Modified Julian Day"),
    WriterField("gpsweek", "time", ("gps", "gps_ws", "week"), int, "%5d", 5, "WEEK", "", "GPS week"),
    WriterField(
        "gpssec", "time", ("gps", "gps_ws", "seconds"), float, "%11.3f", 11, "GPSSEC", "second", "GPS seconds"
    ),
    WriterField(
        "azimuth",
        "site_pos",
        ("azimuth",),
        float,
        "%7.1f",
        7,
        "AZI",
        "deg",
        "Azimuth of satellite in relation to station position",
    ),
    WriterField(
        "elevation",
        "site_pos",
        ("elevation",),
        float,
        "%7.1f",
        7,
        "ELEV",
        "deg",
        "Elevation of satellite in relation to station position",
    ),
    WriterField(
        "observation", "observation", (), float, "%15.3f", 15, "OBS", "meter", "Observation used in calculation stage"
    ),
    WriterField(
        "range", "delay", ("gnss_range",), float, "%15.3f", 15, "RANGE", "meter", "Station-satellite distance"
    ),
    WriterField(
        "range_rate", 
        "delay", 
        ("gnss_range_rate",), 
        float, 
        "%15.4f", 
        15, 
        "RANGE_RATE", 
        "m/s", 
        "Rate of station-satellite distance",
    ),
    WriterField(
        "satellite_clock",
        "delay",
        ("gnss_satellite_clock",),
        float,
        "%15.4f",
        15,
        "SAT_CLK",
        "meter",
        "Satellite clock correction",
    ),
    WriterField(
        "earth_rotation_drift",
        "delay",
        ("gnss_earth_rotation_drift",),
        float,
        "%15.4f",
        15,
        "ERTH_ROT_DRIFT",
        "m/s",
        "Earth rotation drift",
    ),
    WriterField(
        "satellite_clock_rate",
        "delay",
        ("gnss_satellite_clock_rate",),
        float,
        "%15.4f",
        15,
        "SAT_CLK_RATE",
        "m/s",
        "Satellite clock correction",
    ),
    WriterField(
        "troposphere", "delay", ("troposphere_radio",), float, "%9.3f", 9, "TROPO", "meter", "Troposphere delay"
    ),
    WriterField(
        "relativistic_clock",
        "delay",
        ("gnss_relativistic_clock",),
        float,
        "%9.3f",
        9,
        "REL_CLK",
        "meter",
        "Relativistic clock effect due to orbit eccentricity",
    ),
    WriterField(
        "relativistic_clock_rate",
        "delay",
        ("gnss_relativistic_clock_rate",),
        float,
        "%15.4f",
        15,
        "REL_CLK_RATE",
        "m/s",
        "Relativistic clock rate effect due to orbit eccentricity",
    ),
    WriterField("ionosphere", "delay", ("gnss_ionosphere",), float, "%9.3f", 9, "IONO", "meter", "Ionosphere delay"),
    WriterField(
        "satellite_phase_center_offset",
        "delay",
        ("gnss_satellite_phase_center_offset",),
        float,
        "%9.3f",
        9,
        "SAT_OFF",
        "meter",
        "Satellite clock correction",
    ),
    WriterField(
        "total_group_delay",
        "delay",
        ("gnss_total_group_delay",),
        float,
        "%9.3f",
        9,
        "TGD",
        "meter",
        "Total group delay",
    ),
    WriterField(
        "atm_tides_x",
        "site",
        ("atm_tides", "trs", "x"),
        float,
        "%9.3f",
        9,
        "ATMO_X",
        "meter",
        "X-coordinate of site displacement due to atmospheric loading tides related to TRF",
    ),
    WriterField(
        "atm_tides_y",
        "site",
        ("atm_tides", "trs", "y"),
        float,
        "%9.3f",
        9,
        "ATMO_Y",
        "meter",
        "Y-coordinate of site displacement due to atmospheric loading tides related to TRF",
    ),
    WriterField(
        "atm_tides_z",
        "site",
        ("atm_tides", "trs", "z"),
        float,
        "%9.3f",
        9,
        "ATMO_Z",
        "meter",
        "Z-coordinate of site displacement due to atmospheric loading tides related to TRF",
    ),
    WriterField(
        "nt_atm_loading_x",
        "site",
        ("nt_atm_loading", "trs", "x"),
        float,
        "%9.3f",
        9,
        "N_ATMO_X",
        "meter",
        "X-coordinate of site displacement due to non-tidal atmospheric loading related to TRF",
    ),
    WriterField(
        "nt_atm_loading_y",
        "site",
        ("nt_atm_loading", "trs", "y"),
        float,
        "%9.3f",
        9,
        "N_ATMO_Y",
        "meter",
        "Y-coordinate of site displacement due to non-tidal atmospheric loading related to TRF",
    ),
    WriterField(
        "nt_atm_loading_z",
        "site",
        ("nt_atm_loading", "trs", "z"),
        float,
        "%9.3f",
        9,
        "N_ATMO_Z",
        "meter",
        "Z-coordinate of site displacement due to non-tidal atmospheric loading related to TRF",
    ),
    WriterField(
        "ocean_ptides_x",
        "site",
        ("ocean_ptides", "trs", "x"),
        float,
        "%9.3f",
        9,
        "OCN_P_X",
        "meter",
        "X-coordinate of site displacement due to ocean loading tides related to TRF",
    ),
    WriterField(
        "ocean_ptides_y",
        "site",
        ("ocean_ptides", "trs", "y"),
        float,
        "%9.3f",
        9,
        "OCN_P_Y",
        "meter",
        "Y-coordinate of site displacement due to ocean loading tides related to TRF",
    ),
    WriterField(
        "ocean_ptides_z",
        "site",
        ("ocean_ptides", "trs", "z"),
        float,
        "%9.3f",
        9,
        "OCN_P_Z",
        "meter",
        "Z-coordinate of site displacement due to ocean loading tides related to TRF",
    ),
    WriterField(
        "ocean_tides_x",
        "site",
        ("ocean_tides", "trs", "x"),
        float,
        "%9.3f",
        9,
        "OCN_X",
        "meter",
        "X-coordinate of site displacement due to ocean pole tides related to TRF",
    ),
    WriterField(
        "ocean_tides_y",
        "site",
        ("ocean_tides", "trs", "y"),
        float,
        "%9.3f",
        9,
        "OCN_Y",
        "meter",
        "Y-coordinate of site displacement due to ocean pole tides related to TRF",
    ),
    WriterField(
        "ocean_tides_z",
        "site",
        ("ocean_tides", "trs", "z"),
        float,
        "%9.3f",
        9,
        "OCN_Z",
        "meter",
        "Z-coordinate of site displacement due to ocean pole tides related to TRF",
    ),
    WriterField(
        "solid_ptides_x",
        "site",
        ("solid_ptides", "trs", "x"),
        float,
        "%9.3f",
        9,
        "SLD_P_X",
        "meter",
        "X-coordinate fo site displacement due to solid pole tides related to TRF",
    ),
    WriterField(
        "solid_ptides_y",
        "site",
        ("solid_ptides", "trs", "y"),
        float,
        "%9.3f",
        9,
        "SLD_P_Y",
        "meter",
        "Y-coordinate fo site displacement due to solid pole tides related to TRF",
    ),
    WriterField(
        "solid_ptides_z",
        "site",
        ("solid_ptides", "trs", "z"),
        float,
        "%9.3f",
        9,
        "SLD_P_Z",
        "meter",
        "Z-coordinate fo site displacement due to solid pole tides related to TRF",
    ),
    WriterField(
        "solid_tides_x",
        "site",
        ("solid_tides", "trs", "x"),
        float,
        "%9.3f",
        9,
        "SLD_X",
        "meter",
        "X-coordinate of site displacement due to solid tides related to TRF",
    ),
    WriterField(
        "solid_tides_y",
        "site",
        ("solid_tides", "trs", "y"),
        float,
        "%9.3f",
        9,
        "SLD_Y",
        "meter",
        "Y-coordinate of site displacement due to solid tides related to TRF",
    ),
    WriterField(
        "solid_tides_z",
        "site",
        ("solid_tides", "trs", "z"),
        float,
        "%9.3f",
        9,
        "SLD_Z",
        "meter",
        "Z-coordinate of site displacement due to solid tides related to TRF",
    ),
)


@plugins.register
def calculate_solution(dset: "Dataset") -> None:
    """Write calculate solution results


    Args:
        dset:  A dataset containing the data.
    """
    file_path = config.files.path("output_calculate_solution", file_vars={**dset.vars, **dset.analysis})
    
    # Update WriterField depending on used pipeline
    fields_def = list(FIELDS)
    fields_def.append(WriterField(
                        "residual_prefit", 
                        "residual_prefit", 
                        (), 
                        float, 
                        "%15.4f" if dset.vars["pipeline"] == "gnss_vel" else "%15.3f", 
                        15, 
                        "RESIDUAL", 
                        "m/s" if dset.vars["pipeline"] == "gnss_vel" else "meter", 
                        "Pre-fit residual",
                      )
    )
    
    # Add date field to dataset
    if "date" not in dset.fields:
        dset.add_text("date", val=[d.strftime("%Y/%m/%d %H:%M:%S") for d in dset.time.datetime], write_level="detail")

    # Select fields available in Dataset
    fields = get_existing_fields(dset, fields_def)

    # Put together fields in an array as specified by the 'dtype' tuple list
    output_list = list(zip(*(get_field(dset, f.field, f.attrs, f.unit) for f in fields)))
    output_array = np.array(output_list, dtype=[(f.name, f.dtype) for f in fields])

    # Write to disk
    header = get_header(
        fields,
        pgm_version=f"where {where.__version__}",
        run_by=util.get_user_info()["inst_abbreviation"] if "inst_abbreviation" in util.get_user_info() else "",
        summary="Calculate solutions results",
    )
    np.savetxt(
        file_path,
        output_array,
        fmt=tuple(f.format for f in fields),
        header=header,
        delimiter="",
        encoding="utf8",
    )
