# coding=utf-8
"""Methods for resolving MRT from Radiance and EnergyPlus output files."""
from __future__ import division

from ladybug.sql import SQLiteResult


def irradiance_contrib_map(
    sql, direct_specular, indirect_specular, ref_specular, indirect_diffuse, ref_diffuse,
    sun_up_hours, aperture_id=None
):
    """Get matrices of irradiance contribution for a given dynamic aperture.

    Args:
        result_sql: Path to an SQLite file that was generated by EnergyPlus.
            This file must contain results for window transmittance.
        direct_specular: Path to an .ill file output by Radiance containing direct
            irradiance for the specular version of the aperture group.
        indirect_specular: Path to an .ill file output by Radiance containing
            the indirect irradiance for the specular version of the aperture group.
        ref_specular: Path to an .ill file output by Radiance containing ground-reflected
            irradiance for the specular version of the aperture group.
        indirect_diffuse: Path to an .ill file output by Radiance containing
            the indirect irradiance for the diffuse version of the aperture group.
        ref_diffuse: Path to an .ill file output by Radiance containing ground-reflected
            irradiance for the diffuse version of the aperture group.
        sun_up_hours: Path to a sun-up-hours.txt file output by an annual
            irradiance simulation.
        aperture_id: Text string for the identifier of the aperture associated with
            the irradiance. If unspecified, it will the first aperture found in the
            result-sql, essentially assuming there is only one dynamic group in the file.

    Returns:
        A tuple fo three values.

        * direct_mtx -- A matrix with the direct irradiance contribution.

        * indirect_mtx -- A matrix with the indirect irradiance contribution.

        * ref_mtx - A matrix with the ground-reflected irradiance contribution.
    """
    # load the relevant transmittance data from the SQLite
    sql_obj = SQLiteResult(sql)
    incident_out = 'Surface Outside Face Incident Solar Radiation Rate per Area'
    beam_to_beam_out = 'Surface Window Transmitted Beam To Beam Solar Radiation Rate'
    beam_to_diff_out = 'Surface Window Transmitted Beam To Diffuse Solar Radiation Rate'
    diff_to_diff_out = 'Surface Window Transmitted Diffuse Solar Radiation Rate'
    incident_dat = sql_obj.data_collections_by_output_name(incident_out)
    beam_to_beam_dat = sql_obj.data_collections_by_output_name(beam_to_beam_out)
    beam_to_diff_dat = sql_obj.data_collections_by_output_name(beam_to_diff_out)
    diff_to_diff_dat = sql_obj.data_collections_by_output_name(diff_to_diff_out)

    # compute beam and diff transmittance for the relevant aperture
    incident_per_area = _data_for_surface(incident_dat, aperture_id)
    beam_to_beam = _data_for_surface(beam_to_beam_dat, aperture_id)
    beam_to_diff = _data_for_surface(beam_to_diff_dat, aperture_id)
    diff_to_diff = _data_for_surface(diff_to_diff_dat, aperture_id)
    ap_dict = sql_obj.tabular_data_by_name('Exterior Fenestration')
    ap_area = ap_dict[aperture_id.upper()][1] if aperture_id is not None \
        else ap_dict.values()[0][1]
    incident = incident_per_area * ap_area
    dts = incident_per_area.datetimes
    beam_trans_all, diff_trans_all = {}, {}
    all_data = zip(beam_to_beam, beam_to_diff, diff_to_diff, incident, dts)
    for bb, bd, dd, inc, dt in all_data:
        if inc != 0:
            try:
                beam_trans_all[dt.int_hoy].append(bb / inc)
                diff_trans_all[dt.int_hoy].append((bd + dd) / inc)
            except KeyError:
                beam_trans_all[dt.int_hoy] = [bb / inc]
                diff_trans_all[dt.int_hoy] = [(bd + dd) / inc]

    # open the sun-up-hours file and get transmittance for just those hours
    with open(sun_up_hours) as soh_f:
        sun_indices = [int(float(h)) for h in soh_f]
    beam_trans, diff_trans = [], []
    for s_ind in sun_indices:
        try:
            b_trans = sum(beam_trans_all[s_ind]) / len(beam_trans_all[s_ind])
            d_trans = sum(diff_trans_all[s_ind]) / len(diff_trans_all[s_ind])
        except KeyError:  # no incident solar on window for that hour
            b_trans = d_trans = 0
        beam_trans.append(b_trans)
        diff_trans.append(d_trans)

    # compute the direct irradiance contribution
    direct_mtx = []
    for pt_irr in _ill_vals(direct_specular):
        direct_mtx.append([irr * bt for irr, bt in zip(pt_irr, beam_trans)])

    # compute the indirect irradiance contribution
    indirect_mtx = []
    for s_irr, d_irr in zip(_ill_vals(indirect_specular), _ill_vals(indirect_diffuse)):
        sen_mtx = []
        for si, di, bt, dt in zip(s_irr, d_irr, beam_trans, diff_trans):
            sen_mtx.append((si * bt) + (di * dt))
        indirect_mtx.append(sen_mtx)

    # compute the ground-reflected irradiance contribution
    ref_mtx = []
    for s_irr, d_irr in zip(_ill_vals(ref_specular), _ill_vals(ref_diffuse)):
        sen_mtx = []
        for si, di, bt, dt in zip(s_irr, d_irr, beam_trans, diff_trans):
            sen_mtx.append((si * bt) + (di * dt))
        ref_mtx.append(sen_mtx)

    return direct_mtx, indirect_mtx, ref_mtx


def _data_for_surface(data_colls, aperture_id):
    """Get a data collection for a specific aperture.

    Args:
        data_colls: A list of data collections from an SQLite file.
        aperture_id: The identifier if an aperture to be selected from the list of
            data collections.

    Return:
        A data collection for the specified aperture_id.
    """
    if aperture_id is None:
        return data_colls[0]
    ap_id = aperture_id.upper()
    for dat_c in data_colls:
        if dat_c.header.metadata['Surface'] == ap_id:
            return dat_c
    return data_colls[0]


def _ill_vals(ill_file):
    """Parse an .ill file into a iterator of values.

    Args:
        ill_file: Path to an .ill file.

    Return:
        A list of list where each sub-list contains irradiance data.
    """
    with open(ill_file) as results:
        return [[float(v) for v in pt_res.split()] for pt_res in results]
