# This file is part of Pantry, command-line nutrient analysis.
# 
# Version 23, released Friday, November 09, 2007.
# 
# Copyright 2007 Omari Norman.
# 
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the
# Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute,
# sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
# 
# The above copyright notice and this permission notice shall
# be included in all copies or substantial portions of the
# Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# 
# Pantry's "built-in" XML validator is a modified version of
# xmlproc.  xmlproc is Copyright 1998-2000 by Lars Marius
# Garshol, Oslo, Norway.
# 
# All Rights Reserved
# 
# Permission to use, copy, modify, and distribute this
# software and its documentation for any purpose and without
# fee is hereby granted, provided that the above copyright
# notice appear in all copies and that modified copies are
# clearly marked as such.
# 
# LARS MARIUS GARSHOL DISCLAIMS ALL WARRANTIES WITH REGARD TO
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL LARS MARIUS
# GARSHOL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

from __future__ import division
class ReportBase:
    def __init__(self, prog, stdout, stderr):
        from pantry.lib import io
        self.stdout = stdout
        self.stderr = stderr
        self.prog = prog
        self.output = io.puni(stdout)
        self.errors = io.ErrorPrinter(io.puni(stderr), prog, io.WARNING)

class names(ReportBase):
    def printMain(self, food):
        try:
            self.output(food.traits[TRAITSBYNAME['name']] + '\n')
        except KeyError:
            pass

class recipeYield(ReportBase):
    name = 'yield'

    def printMain(self, food):
        try:
            self.output(food.traits[TRAITSBYNAME['yieldDesc']] + ' ')
        except KeyError:
            pass
        self.output('(%.0fg)\n' % food.totalGrams)

class ingredients(ReportBase):
    def printMain(self, food):
        if not hasattr(food, 'ingredients'):
            self.errors('food %s is not a recipe.' % 
                    food.traits.get(TRAITSBYNAME['name'], '<no name>'))
            return
        for ingr in food.ingredients:
            traits = {}
            for traitName in ('qty', 'unit', 'name', 'comment'):
                traits[traitName] = \
                        ingr.traits.get(TRAITSBYNAME[traitName], '')

            grams = ingr.grams
            oz = ingr.grams / 28.35
            self.output('%s %s (%.3foz, %.0fg) %s' % 
                    (traits['qty'], traits['unit'], oz,
                        grams, traits.get('name', '<unnamed food>')))

            if TRAITSBYNAME['comment'] in traits:
                self.output(' (%s)' % ingr.traits[TRAITSBYNAME['comment']])
            self.output('\n')

class directions(ReportBase):
    def printMain(self, food):
        import re
        if not hasattr(food, 'ingredients'):
            self.errors('food %s is not a recipe.' % 
                    food.traits.get(TRAITSBYNAME['name'], '<no name>'))
            return
        try:
            directions = food.traits[TRAITSBYNAME['directions']].strip()
        except KeyError:
            return
        directions = re.sub(r'\s+', ' ', directions)
        directions = re.sub(r' ?<p[^>]*> ?', '', directions)
        directions = re.sub(r' ?</p> ?', '\n\n', directions)
        self.output(directions)

class recipe(ReportBase):
    def printMain(self, food):
        if not hasattr(food, 'ingredients'):
            self.errors('food %s is not a recipe.' % 
                    food.traits.get(TRAITSBYNAME['name'], '<no name>'))
            return
        
        for report in ('names', 'recipeYield', 'blank', 'ingredients', 'blank',
                'directions'):
            reporter = globals()[report](self.prog, self.stdout, self.stderr)
            reporter.printMain(food)

class paste(ReportBase):
    def printMain(self, food):
        OPTEXACT='-x'
        OPTNAME='-n'
        OPTCHANGEUNIT='-U'

        name = food.traits.get(TRAITSBYNAME['name'], '')
        name = "'%s'" % name.replace("'", "'\\''")
        availUnits = food.units
        availUnits = [ "'%s'" % unit.replace("'", "'\\''")
                for unit in availUnits ]

        for availUnit in availUnits:
            self.output(' '.join([OPTEXACT, OPTNAME, name, 
                OPTCHANGEUNIT, availUnit, '\n']))

class traits(ReportBase):
    def printMain(self, food):
        from pantry.lib import uniconv

        traits = food.traits

        self.output(traits.get(TRAITSBYNAME['name'], '') + '\n')
        group = traits.get(TRAITSBYNAME['group'], '')
        if group:
            self.output('Group: %s\n' % group)

        atStartOfLine = True
        for traitName in ['date', 'meal', 'order']:
            traitVal = traits.get(TRAITSBYNAME[traitName], '')
            if traitVal:
                self.output('%s: %s ' % 
                        (traitName.capitalize(), traitVal))
                atStartOfLine = False
        if not atStartOfLine:
            self.output('\n',)

        comment = traits.get(TRAITSBYNAME['comment'], '')
        if comment:
            self.output('Comment: %s\n' % comment)

        pctRefuse = traits.get(TRAITSBYNAME['pctRefuse'], '')
        refDesc = traits.get(TRAITSBYNAME['refDesc'], '')
        if pctRefuse and uniconv.convertUni(pctRefuse) != 0:
            self.output('Refuse: %s percent %s\n' % (pctRefuse, refDesc))

        self.output(('%s %s (%.0fg)\n' 
                % (traits[TRAITSBYNAME['qty']], traits[TRAITSBYNAME['unit']], food.grams)))

class measures(ReportBase):
    def printMain(self, food):
        for unit, grams in food.units.items():
            self.output('   %s (%.0fg)\n' % (unit, grams))

class units(ReportBase):
    def printMain(self, food):
        from pantry.lib import foods
        units = set(food.units).difference(foods.ABSOLUTES)
        for unit in units:
            self.output('   %s\n' % unit)

class debug(ReportBase):
    needNuts = True
    def printMain(self, food):
        self.output(str(food) + '\n')

class nutrientList(ReportBase):
    name = 'list'

    def __init__(self, prog, stdout, stderr, goalReporter):
        self.goalReporter = goalReporter
        ReportBase.__init__(self, prog, stdout, stderr)

    def printEnd(self):
        from pantry.lib import foods

        format = "%-25s %-14s %-6s\n"
        headerLabels = ('Nutrient Name', 'Units', 'Goal')
        headerString = format % headerLabels
        self.output(headerString)
        self.output("-" * len(headerString) + '\n')

        for nutNo, goal in self.goalReporter.goals:
            nutName = foods.NUTSBYNO[nutNo]['name']
            nutUnits = foods.NUTSBYNO[nutNo]['units']
            self.output(format % (nutName, nutUnits, goal))

class nutReport(ReportBase):
    def __init__(self, prog, stdout, stderr, goalReporter):
        self.goalReporter = goalReporter
        ReportBase.__init__(self, prog, stdout, stderr)

    def printNuts(self, food):
        """ Prints nutrient report for food.

        If food is None, prints sum nutrient report.
        """
        NUTSBYNO = pantry.lib.foods.NUTSBYNO
        headerFormat = "%-25s %-14s %-6s"
        headerLabels = ['Nutrient', 'Amount', '%G' ]
        if food:
            headerFormat += ' %-6s'
            headerLabels.append('%TOT')
        else:
            headerFormat = 'SUM:\n%s' % headerFormat
        headerFormat += '\n'
        headerString = headerFormat % tuple(headerLabels)
        self.output(headerString)
        self.output("-" * len(headerString) + '\n')
        lineFormat = "%-25s %-4s %-10s %-6s"
        if food:
            lineFormat += ' %-6s\n'
            data = self.goalReporter[food.hash]
        else:
            lineFormat += '\n'
            data = self.goalReporter.totals

        for line in data:
            toPrint = [NUTSBYNO[line['no']]['name'],
                    line['nutAmt'], 
                    NUTSBYNO[line['no']]['units'], line['pctGoal']]
            if food:
                toPrint.append(line['pctTotal'])

            self.output(lineFormat % tuple(toPrint))

class nuts(ReportBase):
    needNuts = True
    def __init__(self, prog, stdout, stderr, goalReporter):
        self.nutReporter = nutReport(prog, stdout, stderr, goalReporter)
        ReportBase.__init__(self, prog, stdout, stderr)
    def printMain(self, food):
        self.nutReporter.printNuts(food)

class sum(ReportBase):
    needNuts = True
    def __init__(self, prog, stdout, stderr, goalReporter):
        self.nutReporter = nutReport(prog, stdout, stderr, goalReporter)
        ReportBase.__init__(self, prog, stdout, stderr)
    def printEnd(self):
        self.nutReporter.printNuts(None)

class blank(ReportBase):
    def printMain(self, food):
        self.output('\n')

class groups(ReportBase):
    def __init__(self, prog, stdout, stderr, cabinet):
        self.cabinet = cabinet
        ReportBase.__init__(self, prog, stdout, stderr)

    def printEnd(self):
        groupDict = {}
        for food in self.cabinet.values():
            groupName = food.traits.get(TRAITSBYNAME['group'], 
                    '<No group trait>')
            try:
                groupDict[groupName] += 1
            except KeyError:
                groupDict[groupName] = 1

        groupDict = dict([(groupName, groupDict[groupName])
            for groupName in sorted(groupDict)])

        self.output('  %-35s %7s\n' % ('Group', 'No. of Foods'))
        self.output('-' * 60 + '\n')
        for groupName in sorted(groupDict.keys()):
            self.output('  %-35s %7u\n' % (groupName, groupDict[groupName]))
        self.output('-' * 60 + '\n')
        self.output('  %-35s %7u foods total\n' % ('', len(self.cabinet)))
        self.output('  %-35s %7u groups total\n' % ('', len(groupDict)))

import pantry.lib.foods
TRAITSBYNAME = pantry.lib.foods.TRAITSBYNAME

reports = [names, recipeYield, ingredients, directions, recipe, paste,
        traits, measures, units, debug, nutrientList, nuts, sum, blank,
        groups]
