# -*- coding: utf-8 -*-

from decimal import *


class BaseAQI(object):
    """A generic AQI class"""

    def iaqi(self, elem, cc):
        """Calculate an intermediate AQI for a given pollutant. This is
        the heart of the algo. Return the IAQI for the given pollutant.

        .. warning:: the concentration is passed as a string so
        :class:`decimal.Decimal` doesn't act up with binary floats.

        :param elem: pollutant constant
        :type elem: int
        :param cc: pollutant contentration (µg/m³ or ppm)
        :type cc: str
        """
        raise NotImplementedError

    def aqi(self, ccs, iaqis=False):
        """Calculate the AQI based on a list of pollutants. Return an
        AQI value, if `iaqis` is set to True, send back a tuple
        containing the AQI and a dict of IAQIs.

        :param ccs: a list of tuples of pollutants concentrations with
                    pollutant constant and concentration as values
        :type ccs: list
        :param iaqis: return IAQIs with result
        :type iaqis: bool
        """
        try:
            _iaqis = {}
            for (elem, cc) in ccs:
                _iaqi = self.iaqi(elem, cc)
                if _iaqi is not None:
                    _iaqis[elem] = _iaqi
            _aqi = max(_iaqis.values())
            if iaqis:
                return (_aqi, _iaqis)
            else:
                return _aqi
        except:
            return None

    def cc(self, elem, iaqi):
        """Calculate a concentration for a given pollutant. Return the
        concentration for the given pollutant based on the intermediate AQI.

        .. warning:: the intermediate AQI is passed as a string

        :param elem: pollutant constant
        :type elem: int
        :param cc: intermediate AQI
        :type cc: str
        """
        raise NotImplementedError

    def list_pollutants(self):
        """List pollutants covered by an algorithm, return a list of
        pollutant names.
        """
        raise NotImplementedError


class PiecewiseAQI(BaseAQI):
    """A piecewise function AQI class (like EPA or MEP)"""

    piecewise = None

    def iaqi(self, elem, cc):
        try:
            if self.piecewise is None:
                raise NameError("piecewise struct is not defined")
            if elem not in self.piecewise['bp'].keys():
                return None

            _cc = Decimal(cc).quantize(self.piecewise['prec'][elem],
                                    rounding=ROUND_DOWN)

            # define breakpoints for this pollutant at this contentration
            bps = self.piecewise['bp'][elem]
            bplo = None
            bphi = None
            idx = 0
            for bp in bps:
                if _cc >= bp[0] and _cc <= bp[1]:
                    bplo = bp[0]
                    bphi = bp[1]
                    break
                idx += 1
            # get corresponding AQI boundaries
            (aqilo, aqihi) = self.piecewise['aqi'][idx]

            # equation
            value = (aqihi - aqilo) / (bphi - bplo) * (_cc - bplo) + aqilo
            return value.quantize(Decimal('1.'), rounding=ROUND_HALF_EVEN)
        except:
            return cc


    def cc(self, elem, iaqi):
        try:
            if self.piecewise is None:
                raise NameError("piecewise struct is not defined")
            if elem not in self.piecewise['bp'].keys():
                return None

            _iaqi = int(iaqi)

            # define aqi breakpoints for this pollutant at this IAQI
            bps = self.piecewise['aqi']
            bplo = None
            bphi = None
            idx = 0
            for bp in bps:
                if _iaqi >= bp[0] and _iaqi <= bp[1]:
                    bplo = bp[0]
                    bphi = bp[1]
                    break
                idx += 1
            # get corresponding concentration boundaries
            (cclo, cchi) = self.piecewise['bp'][elem][idx]

            # equation
            value = (cchi - cclo) / (bphi - bplo) * (_iaqi - bplo) + cclo
            return Decimal(value).quantize(self.piecewise['prec'][elem],
                                    rounding=ROUND_DOWN)
        except:
            return iaqi

    def list_pollutants(self):
        return self.piecewise['units'].items()

    def get_units(self):
        return self.piecewise['units']