# -*- coding: utf-8 -*-
"""
This module implements the denisty parameterisation of aqueous sulfuric acid
of Myhre et al. from 1998.
"""
from __future__ import (absolute_import, division, print_function)

import numpy as np
import warnings

from ..util import NoConvergence


_data = np.array([[999.8426, 0.03345402, -0.005691304, 0, 0],
                  [547.2659, -5.300445, 0.01187671, 0.0005990008, 0],
                  [5262.95, 37.20445, 0.1201909, -0.004148594, 1.197973E-5],
                  [-62139.58, -287.767, -0.4064638, 0.01119488, 3.607768E-5],
                  [409029.3, 1270.854, 0.326971, -0.01377435, -2.633585E-5],
                  [-1596989, -3062.836, 0.1366499, 0.006373031, 0],
                  [3857411, 4083.714, -0.1927785, 0, 0],
                  [-5808064, -2844.401, 0, 0, 0],
                  [5301976, 809.1053, 0, 0, 0],
                  [-2682616, 0, 0, 0, 0],
                  [576428.8, 0, 0, 0, 0]])


def sulfuric_acid_density(w, T=None, T0=None, units=None, warn=True):
    """
    Density of sulfuric acid (kg/m³) as function of temperature (K)
    and mass fraction acid (w).

    Parameters
    ----------
    w: float
        Acid mass fraction (0.1 <= w <= 0.9)
    T: float
        Temperature (in Kelvin) (273 <= T <= 323) (default: 298.15)
    T0: float
        Value of T for 0 degree Celsius (default: 273.15)
    units: object (optional)
        object with attributes: kelvin, meter, kilogram
    warn: bool (default: True)
        Emit UserWarning when outside T or w range.

    Returns
    -------
    Density of sulfuric acid (float of kg/m³ if T is float and units is None)

    Examples
    --------
    >>> print('%d' % sulfuric_acid_density(.5, 293))
    1396

    References
    ----------
    Cathrine E. L. Myhre , Claus J. Nielsen ,* and Ole W. Saastad
        "Density and Surface Tension of Aqueous H2SO4 at Low Temperature"
        J. Chem. Eng. Data, 1998, 43 (4), pp 617–622
        http://pubs.acs.org/doi/abs/10.1021/je980013g
        DOI: 10.1021/je980013g
    """
    if units is None:
        K = 1
        m = 1
        kg = 1
    else:
        K = units.Kelvin
        m = units.meter
        kg = units.kilogram
    if T is None:
        T = 298.15*K
    m3 = m**3
    if T0 is None:
        T0 = 273.15*K
    t = T - T0
    if warn:
        if np.any(t < 0*K) or np.any(t > 50*K):
            warnings.warn("Temperature is outside range (0-50 degC)")
        if np.any(w < 0.1) or np.any(w > .9):
            warnings.warn("Mass fraction is outside range (0.1-0.9)")
    t_arr = np.array([float(t/K)**j for j in range(5)]).reshape((1, 5))
    w_arr = np.array([w**i for i in range(11)]).reshape((11, 1))
    return np.sum((t_arr*w_arr)*_data)*kg/m3  # Equation (2) in reference


# generated at doi2bib.org:
bibtex = """
@article{Myhre1998,
  doi = {10.1021/je980013g},
  url = {http://dx.doi.org/10.1021/je980013g},
  year  = {1998},
  month = {jul},
  publisher = {American Chemical Society ({ACS})},
  volume = {43},
  number = {4},
  pages = {617--622},
  author = {Cathrine E. L. Myhre and Claus J. Nielsen and Ole W. Saastad},
  title = {Density and Surface Tension of Aqueous H2{SO}4 at Low Temperature},
  journal = {Journal of Chemical {\&} Engineering Data}
}
"""


def density_from_concentration(conc, T=None, molar_mass=None,
                               rho_cb=sulfuric_acid_density,
                               units=None, atol=None, maxiter=50, **kwargs):
    """ Calculates the density of a solution from its concentration

    Given a function which calculates the density of a solution from the mass
    fraction of the solute, this function calculates (iteratively) the density
    of said solution for a given concentration.

    Parameters
    ----------
    conc: float (optionally with units)
        concentration (mol / m³)
    T: float (optionally with units)
        passed to ``rho_cb``
    molar_mass: float (optionally with units)
        Molar mass of solute
    rho_cb: callback
        callback with signature f(w, T, units=None) -> rho
        (default: :func:`sulfuric_acid_density`)
    units: object (optional)
        object with attributes: meter, kilogram, mol
    atol: float (optionally with units)
        convergence criterion for fixed-point iteration
        (default: 1e-3 kg/m³)
    maxiter: int
        maximum number of iterations (when exceeded a NoConvergence excpetion
        is raised)
    \*\*kwargs:
        keyword arguments passed onto ``rho_cb``

    Returns
    -------
    Density of sulfuric acid (float of kg/m³ if T is float and units is None)

    Examples
    --------
    >>> print('%d' % density_from_concentration(400, 293))
    1021

    Raises
    ------
    chempy.util.NoConvergence:
        When maxiter is exceeded

    """
    if units is None:
        m = 1
        kg = 1
        mol = 1
    else:
        m = units.meter
        kg = units.kilogram
        mol = units.mol
    kg_per_m3 = kg * m**-3
    if atol is None:
        atol = 1e-3 * kg_per_m3
    if molar_mass is None:
        molar_mass = (1.00794*2 + 32.066 + 4*15.9994)*1e-3 * kg / mol

    rho = 1100 * kg_per_m3
    delta_rho = float('inf') * kg_per_m3

    iter_idx = 0
    while atol < abs(delta_rho):
        # fixed point iteration
        new_rho = rho_cb(conc*molar_mass/rho, T, units=units, **kwargs)
        delta_rho = new_rho - rho
        rho = new_rho
        iter_idx += 1
        if iter_idx > maxiter:
            raise NoConvergence("maxiter exceeded")
    return rho
