#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals, division

import logging
import re

from pignacio_scripts.namedtuple import namedtuple_with_defaults

from .constants import DEFAULT_CONVERSIONS
from .conversions import get_conversion_table, CantConvert

logger = logging.getLogger(__name__)  # pylint: disable=invalid-name

_LogData = namedtuple_with_defaults(
    'LogData', ['name', 'nutritional_value', 'parts', 'log_line',
                'incomplete'],
    defaults=lambda: {'parts': [],
                      'log_line': None,
                      'incomplete': False})


class LogData(_LogData):
    __slots__ = ()

    @classmethod
    def from_parts(cls, name, parts):
        return cls(name=name,
                   parts=parts,
                   nutritional_value=NutritionalValue.sum(p.nutritional_value
                                                          for p in parts),
                   incomplete=any(p.incomplete for p in parts))


LogLine = namedtuple_with_defaults('LogLine', ['name', 'amount', 'unit',
                                               'ingredient'],
                                   defaults={'ingredient': None})

_NUTRITIONAL_VALUE_FIELDS = [
    'calories',
    'carbs',
    'sugar',
    'protein',
    'fat',
    'trans_fat',
    'saturated_fat',
    'fiber'
]  # yapf: disable
_NutritionalValue = namedtuple_with_defaults(
    'NutritionalValue',
    _NUTRITIONAL_VALUE_FIELDS,
    defaults={f: None for f in _NUTRITIONAL_VALUE_FIELDS}
)  # yapf: disable


class NutritionalValue(_NutritionalValue):
    UNKNOWN = None
    _LINE_ALIASES = {
        'k': 'calories',
        'cal': 'calories',
        'kcal': 'calories',
        'c': 'carbs',
        's': 'sugar',
        'f': 'fat',
        'sf': 'satueated_fat',
        'tf': 'trans_fat',
        'df': 'fiber',
    }

    @classmethod
    def from_json(cls, jobj):
        return cls(**jobj)

    @classmethod
    def sum(cls, values):
        values_sum = [0] * len(cls._fields)
        for n_value in values:
            for index, value in enumerate(n_value):
                if value is not None:
                    values_sum[index] += value
        return cls(*values_sum)

    @classmethod
    def from_line(cls, line):
        values = {}
        for part in re.split('[;,]', line):
            logger.debug('from_line: part: %s', part)
            try:
                key, value = (x.strip() for x in re.split('[:=]', part, 1))
                value = float(value)
                field = cls._LINE_ALIASES.get(key, key)
            except (KeyError, ValueError):
                pass
            else:
                if field in cls._fields:
                    values[field] = value
        return cls(**values)


NutritionalValue.UNKNOWN = NutritionalValue()

_Ingredient = namedtuple_with_defaults(
    'Ingredient',
    [
        'name',
        'sample_size',
        'sample_value',
        'sample_unit',
        'conversions',
    ],
    defaults={
        'conversions': {},
    }
)  # yapf: disable


class Ingredient(_Ingredient):
    def __init__(self, *args, **kwargs):
        super(Ingredient, self).__init__(*args, **kwargs)
        self.__conversion_table = None

    @classmethod
    def from_json(cls, jobj):
        jobj['sample_value'] = NutritionalValue.from_json(jobj['sample_value'])
        return cls(**jobj)

    def as_json(self):
        res = self._asdict()
        res['sample_value'] = res['sample_value']._asdict()
        return res

    def get_nutritional_value(self, amount, unit):
        if self.__conversion_table is None:
            self.__conversion_table = get_conversion_table(self.conversions,
                                                           DEFAULT_CONVERSIONS)

        if unit == self.sample_unit:
            unit_factor = 1.0
        else:
            try:
                unit_factor = self.__conversion_table[unit][self.sample_unit]
            except KeyError:
                raise CantConvert(
                    "Cannot convert '{}' from '{}' to '{}'".format(
                        self.name, self.sample_unit, unit))

        factor = amount / self.sample_size * unit_factor
        new_values = {
            k: v * factor
            for k, v in self.sample_value._asdict().items() if v is not None
        }

        return self.sample_value._replace(**new_values)

@@ -2999,24 +2999,46 @@
 nversions',%0A
+        'categories',%0A
     %5D,%0A    d
@@ -3074,16 +3074,42 @@
 s': %7B%7D,%0A
+        'categories': %5B%5D,%0A
     %7D%0A) 

add Ingredient.categories field