import os
from typing import Tuple

import numpy as np

from assets.commons import (load_config_file,
                            validate_parameter)


def parse_grid_overrides(par_name: str,
                         config: dict) -> np.array:
    """
    Parse the global configuration for constructing the grid, computing the parameter values for which the grid has to
    be computed.
    :param par_name: parameter to parse
    :param config: configuration dictionary to extract the grid properties
    :return: the array of values to be considered in the grid
    """
    config_overrides = config['overrides']
    if config_overrides[f'{par_name}_grid_type'] == 'linear':
        grid_values = np.arange(float(config_overrides[f'{par_name}_limits'][0]),
                                float(config_overrides[f'{par_name}_limits'][1]),
                                float(config_overrides[f'{par_name}_step']))
    else:
        _step = float(config_overrides[f'{par_name}_step']) if float(config_overrides[f'{par_name}_step']) > 1 else 10 ** float(config_overrides[f'{par_name}_step'])
        grid_values = 10 ** np.arange(np.log10(float(config_overrides[f'{par_name}_limits'][0])),
                                      np.log10(float(config_overrides[f'{par_name}_limits'][1])),
                                      np.log10(_step))
    return np.around(grid_values, 2)


def get_grid_properties(grid_config: dict,
                        keyword: str) -> list:
    """
    Compute the grid axes properties using the grid configuration dictionary. If less than 3 axes are provided, fill
        the missing ones with the first axis.
    :param grid_config: the dictionary containing the grid configuration metadata
    :param keyword: the keyword to read
    :return: a list of the requested keyword values over the three spatial axes
    """
    grid_property_list = [grid_config['dim1'][keyword]]
    for axis_idx in range(2):
        try:
            grid_property_list.append(grid_config[f'dim{axis_idx + 2}'][keyword])
        except KeyError:
            grid_property_list.append(grid_config['dim1'][keyword])
    return grid_property_list


def parse_input_main() -> Tuple[str, str, np.array, np.array, list, int, str]:
    """
    Parse the stg and mdl configuration files, as well as the main one, to apply overrides, compute the arrays of
    temperatures and number densities and the type of models defined in the grid (isothermal/uniform density vs.
    power-law profiles)
    :return: a tuple containing the dust profile type (isothermal/heated), the density profile (homogeneous/spherical),
        the distinct values of dust temperatures and densities in the grid, the line pairs used to compute ratios,
        the number of processes to be used in case of multiprocessing, and the model run_type string
    """
    default_config_name = 'config.yml'
    stg_config = load_config_file(os.path.join('stg', 'config', default_config_name))
    pl_density_idx = float(stg_config['grid']['density_powerlaw_idx'])
    pl_dust_temperature_idx = float(stg_config['grid']['dust_temperature_powerlaw_idx'])
    _model_type = 'spherical' if pl_density_idx != 0 else 'homogeneous'
    _tdust_model_type = 'heated' if pl_dust_temperature_idx != 0 else 'isothermal'
    config = load_config_file(os.path.join('config', default_config_name))
    # grid definition
    dust_temperatures = parse_grid_overrides(par_name='dust_temperature',
                                             config=config)
    densities = parse_grid_overrides(par_name='gas_density',
                                     config=config)
    line_pairs = config['overrides']['lines_to_process']
    n_processes = validate_parameter(config['computation']['threads'], default=10)
    return _tdust_model_type, _model_type, dust_temperatures, densities, line_pairs, n_processes, config['run_type']


def read_abundance_variation_schema(line_config: dict) -> dict:
    """
    Read and fill the abundance variation schema, defined as a step function
    :param line_config: the dictionary of the line configurations, for the abundances
    :return: the dictionary of the abundance variations per species, with the abundance jump factor and the temperature
        threshold at which evaporation happens
    """
    species_list = list(line_config['molecular_abundances'].keys())
    results_dict = {}
    for species in species_list:
        results_dict[species] = {
            'threshold': 20,
            'abundance_jump': 1
        }
        if 'hot_core_specs' in line_config.keys():
            if species in line_config['hot_core_specs']:
                results_dict[species] = line_config['hot_core_specs'][species]
    return results_dict
