# coding: utf-8

from __future__ import division, unicode_literals

"""
This module provides classes that define a chemical reaction.
"""


__author__ = "Shyue Ping Ong, Anubhav Jain"
__copyright__ = "Copyright 2011, The Materials Project"
__version__ = "2.0"
__maintainer__ = "Shyue Ping Ong"
__email__ = "shyuep@gmail.com"
__status__ = "Production"
__date__ = "Jul 11 2012"

import logging
import itertools
import numpy as np
import re

from pymatgen.serializers.json_coders import PMGSONable
from pymatgen.core.composition import Composition
from monty.json import MontyDecoder

logger = logging.getLogger(__name__)


class BalancedReaction(PMGSONable):
    """
    An object representing a complete chemical reaction.
    """

    def __init__(self, reactants_coeffs, products_coeffs):
        """
        Reactants and products to be specified as dict of {Composition: coeff}.

        Args:
            reactants ({Composition: float}): Reactants as dict of
                {Composition: amt}.
            products ({Composition: float}): Products as dict of
                {Composition: amt}.
        """
        #sum reactants and products
        all_reactants = sum([k * v for k, v in reactants_coeffs.items()],
                            Composition({}))
        all_products = sum([k * v for k, v in products_coeffs.items()],
                           Composition({}))

        if not all_reactants.almost_equals(all_products,
                                atol=Reaction.TOLERANCE):
            raise ReactionError("Reaction is unbalanced!")

        self._els = all_reactants.elements

        self.reactants_coeffs = reactants_coeffs
        self.products_coeffs = products_coeffs

        #calculate net reaction coefficients
        self._coeffs = []
        self._els = []
        self._all_comp = []
        for c in set(list(reactants_coeffs.keys()) +
                list(products_coeffs.keys())):
            coeff = products_coeffs.get(c,0) - \
                                    reactants_coeffs.get(c,0)

            if abs(coeff) > Reaction.TOLERANCE:
                self._all_comp.append(c)
                self._coeffs.append(coeff)

        self._num_comp = len(self._all_comp)

    def calculate_energy(self, energies):
        """
        Calculates the energy of the reaction.

        Args:
            energies ({Composition: float}): Energy for each composition.
                E.g ., {comp1: energy1, comp2: energy2}.

        Returns:
            reaction energy as a float.
        """
        return sum([self._coeffs[i] * energies[self._all_comp[i]]
                    for i in range(self._num_comp)])

    def normalize_to(self, comp, factor=1):
        """