# -*- coding: utf-8 -*-
"""Akkar, Sandikkaya, & Bommer (2014, :cite:`akkar14`) model."""

import collections

import numpy as np

from . import model

__author__ = 'Albert Kottke'


class AkkarSandikkayaBommer2014(model.GroundMotionModel):
    """Akkar, Sandikkaya, & Bommer (2014, :cite:`akkar14`) model.

    The model is specified for three different distance metrics. However,
    the implementation uses only one distance metric. They are used in
    the following order:

        1. `dist_jb`

        2. `dist_hyp`

        3. `dist_epi`

    This order was selected based on evaluation of the total standard
    deviation. To compute the response for differing metrics, call the
    model multiple times with different keywords.

    Parameters
    ----------
    scenario : :class:`pygmm.model.Scenario`
        earthquake scenario

    """

    NAME = 'Akkar, Sandikkaya, & Bommer (2014)'
    ABBREV = 'ASB14'

    # Reference velocity (m/sec)
    V_REF = 750.

    # Load the coefficients for the model
    COEFF = collections.OrderedDict(
        (k, model.load_data_file('akkar-sandikkaya-bommer-2014-%s.csv' % k, 2))
        for k in ['dist_jb', 'dist_hyp', 'dist_epi'])
    PERIODS = np.array(COEFF['dist_jb'].period)

    INDICES_PSA = np.arange(2, 64)
    INDEX_PGA = 0
    INDEX_PGV = 1
    PARAMS = [
        model.NumericParameter('dist_jb', False, 0, 200),
        model.NumericParameter('dist_epi', False, 0, 200),
        model.NumericParameter('dist_hyp', False, 0, 200),
        model.NumericParameter('mag', True, 4, 8),
        model.NumericParameter('v_s30', True, 150, 1200),
        model.CategoricalParameter('mechanism', True, ['SS', 'NS', 'RS']),
    ]

    def __init__(self, scenario: model.Scenario):
        """Initialize the model."""
        super(AkkarSandikkayaBommer2014, self).__init__(scenario)

        s = self._scenario
        for k in self.COEFF:
            if s[k] is not None:
                dist = s[k]
                c = self.COEFF[k]
                break
        else:
            raise NotImplementedError(
                "Must provide at least one distance metric.")

        # Compute the reference response
        ln_resp_ref = (
            c.a_1 + c.a_3 * (8.5 - s.mag) ** 2 +
            (c.a_4 + c.a_5 *
             (s.mag - c.c_1)) * np.log(np.sqrt(dist ** 2 + c.a_6 ** 2)))
        mask = (s.mag <= c.c_1)
        ln_resp_ref[mask] += (c.a_2 * (s.mag - c.c_1))[mask]
        ln_resp_ref[~mask] += (c.a_7 * (s.mag - c.c_1))[~mask]

        if s.mechanism == 'NS':
            ln_resp_ref += c.a_8
        elif s.mechanism == 'RS':
            ln_resp_ref += c.a_9

        pga_ref = np.exp(ln_resp_ref[self.INDEX_PGA])

        # Compute the nonlinear site term
        if s.v_s30 <= self.V_REF:
            vs_ratio = s.v_s30 / self.V_REF
            site = (c.b_1 * np.log(vs_ratio) + c.b_2 * np.log(
                (pga_ref + c.c * vs_ratio ** c.n) / (
                    (pga_ref + c.c) * vs_ratio ** c.n)))
        else:
            site = c.b_1 * np.log(np.minimum(s.v_s30, c.v_con) / self.V_REF)

        self._ln_resp = ln_resp_ref + site
        self._ln_std = np.array(c.sd_total)
