#!/usr/bin/env python

# panconvert.py - convert Pantry files created with versions 19, 20,
# and 21 to work with version 22.

usage = ('usage: panconvert.py FILE...\n'
        'Converts Pantry native files created with versions\n'
        '19, 20, and 21 to work with version 22.\n'
        'The original FILEs are not changed; the converted\n'
        'file is written to FILE-new.\n')

# hashlib new in python-2.5
try:
    import hashlib
    hasher = hashlib.sha1
except ImportError:
    import sha 
    hasher = sha.sha

import sys
import shelve
import errno
from anydbm import error as anydbmError

def getNewHash(traits):
    """ Given a dict of traits, returns a new hash. """
    fingerPrint = ''.join([traits[traitNo].encode('utf-8')
        for traitNo in sorted(traits.keys())])
    return hasher(fingerPrint).digest()
    
### The next three functions are only needed to convert 
### recipe ingredients. They are not needed to convert
### any other aspect of a Pantry native file.
def convertIngredient(ingredient):
    """ Convert a single ingredient (food or recipe).

    ingredient argument is a dict of dicts that is the food
    or recipe.

    Returns a dict of dicts e.g.
    {'traits':{1:u'hello'},
    'nutsPer100g:{203:4.5}} and so on. """

    if 'ingredients' in ingredient:
        return convertRecipe(ingredient)
    return convertFood(ingredient)

def convertFood(food):
    newTraits = dict([(traitNo, traitVal)
        for traitNo, traitVal in food['traits'].items()
        if traitVal != ''])
    newNutsPer100g = food['nuts']
    newArbUnits = food['arbUnits']
    return {'nutsPer100g':newNutsPer100g,
        'arbUnits':newArbUnits,
        'traits':newTraits}

def convertRecipe(recipe):
    oldRTraits = recipe['rTraits']
    newTraits = dict([(traitNo, traitVal)
        for traitNo, traitVal in recipe['traits'].items()
        if traitVal != ''])
    # old rTraits were yield (key 0) yieldDesc (key 1)
    # servings (key 2) and directions (key 3).
    # new corresponding values are yieldGrams (key 11)
    # yieldDesc (key 12) servings (key 13)
    # and directions (key 14).
    newTraits.update([(newKey, oldRTraits[oldKey])
        for oldKey, newKey in ((0, 11), (1, 12), (2, 13), (3, 14))
        if oldRTraits[oldKey] != ''])
    newArbUnits = recipe['arbUnits']
    
    # handy bit of recursion
    newIngredients = [convertIngredient(ingredient)
            for ingredient in recipe['ingredients']]
    
    return {'traits':newTraits, 'arbUnits':newArbUnits,
            'ingredients':newIngredients}

if len(sys.argv[1:]) < 1:
    sys.stdout.write(usage)
    sys.exit(1)

if sys.argv[1] == '-h' or sys.argv[1] == '--help':
    sys.stdout.write(usage)
    sys.exit()

for oldFileName in sys.argv[1:]:
    try:
        oldShelf = shelve.open(oldFileName, 'r')
    except anydbmError:
        sys.stderr.write('panconvert: ERROR: File not found: %s\nAborting\n' 
                % oldFileName)
        sys.exit(1)
    except:
        sys.stderr.write('panconvert: ERROR: could not open file %s.\n'
                'Study the error message below for assistance.\n\n')
        raise

    try:
        newShelf = shelve.open(oldFileName + '-new', 'n')
    except:
        sys.stderr.write(('panconvert: ERROR: could not open new file'
            '%s-new.\nStudy the error message below for assistance.\n\n')
            % oldFileName)
        raise

    newShelf['VERSION'] = 22
    newAllTraits = {}
    newAllArbUnits = {}
    oldAllRTraits = oldShelf.get('rTraits', {})
    oldAllArbUnits = oldShelf.get('arbUnits', {})

    for oldHash, oldTraitDict in oldShelf['traits'].items():
        # convert traits and rTraits
        newTraitDict = dict([(oldTraitNo, oldValue)
            for oldTraitNo, oldValue in oldTraitDict.items()
            if oldValue != ''])
        try:
            oldRTraits = oldAllRTraits[oldHash]
        except KeyError:
            pass
        else:
            newTraitDict.update([(newTraitNo, oldRTraits[oldRTraitNo])
                for oldRTraitNo, newTraitNo
                in ((0, 11), (1, 12), (2, 13), (3, 14))
                if oldRTraits[oldRTraitNo] != ''])

        newHash = getNewHash(newTraitDict)
        newAllTraits[newHash] = newTraitDict
        
        # convert nuts
        try:
            oldNuts = oldShelf['nuts:%s' % oldHash]
        except KeyError:
            pass
        else:
            newShelf['n:%s' % newHash] = oldNuts

        # convert arbUnits
        try:
            oldArbUnits = oldAllArbUnits[oldHash]
        except KeyError:
            pass
        else:
            newAllArbUnits[newHash] = oldArbUnits

        # convert ingredients
        try:
            oldIngredients = oldShelf['ingredients:%s' % oldHash]
        except KeyError:
            pass
        else:
            newShelf['i:%s' % newHash] = [ convertIngredient(ingredient)
                    for ingredient in oldIngredients]

    newShelf['traits'] = newAllTraits
    newShelf['arbUnits'] = newAllArbUnits
    newShelf.close()
    sys.stdout.write(('panconvert.py: converted file written to'
        ' %s-new\n') % oldFileName)
