#!/usr/bin/python

#Audio Tools, a module and set of tools for manipulating audio data
#Copyright (C) 2007-2012  Brian Langenberger

#This program is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation; either version 2 of the License, or
#(at your option) any later version.

#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.

#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

import audiotools
import audiotools.delta
import sys
import os
import os.path
import re
import tempfile
import subprocess
import cStringIO
import anydbm
import gettext

gettext.install("audiotools", unicode=True)

try:
    from hashlib import sha1
except ImportError:
    from sha import sha as sha1


def checksum(path):
    f = open(path, "rb")
    c = sha1("")
    try:
        audiotools.transfer_data(f.read, c.update)
        return c.hexdigest()
    finally:
        f.close()


def display_messages(messenger, track, messages):
    for message in messages:
        messenger.info(_(u"* %(filename)s: %(message)s") % \
                           {"filename": messenger.filename(track.filename),
                            "message": message})


def audiofiles(paths, messenger):
    directories = [p for p in paths if os.path.isdir(p)]
    files = [p for p in paths if os.path.isfile(p)]

    for f in audiotools.open_files(files, messenger=messenger):
        yield f
    for d in directories:
        for f in audiotools.open_directory(d, messenger=messenger):
            yield f


def update_without_backup(track, messenger):
    fixes = []

    temp_track_f = tempfile.NamedTemporaryFile(suffix="." + track.SUFFIX)
    try:
        #perform cleanup on a temporary track
        track.clean(fixes, temp_track_f.name)

        #if changes are made, copy temporary track over original
        if (len(fixes) > 0):
            input_f = open(temp_track_f.name, 'rb')
            output_f = open(track.filename, 'wb')
            audiotools.transfer_data(input_f.read, output_f.write)
            output_f.close()
            input_f.close()

            #then display output messages
            display_messages(messenger, track, fixes)
    finally:
        temp_track_f.close()


def update_and_backup(track, undo_db, messenger):
    fixes = []

    temp_track_f = tempfile.NamedTemporaryFile(suffix="." + track.SUFFIX)
    try:
        #perform cleanup on a temporary track
        track.clean(fixes, temp_track_f.name)

        #if changes are made
        if (len(fixes) > 0):
            #store undo information between old and new track
            undo_db.add(track.filename, temp_track_f.name)

            #then copy temporary track over original
            input_f = open(temp_track_f.name, 'rb')
            output_f = open(track.filename, 'wb')
            audiotools.transfer_data(input_f.read, output_f.write)
            output_f.close()
            input_f.close()

            #then display output messages
            display_messages(messenger, track, fixes)
    finally:
        temp_track_f.close()


def undo_from_backup(track, undo_db, messenger):
    if (undo_db.undo(track.filename)):
        messenger.info(_(u"Restored: %s") % \
                           (messenger.filename(track.filename)))


if (__name__ == '__main__'):
    parser = audiotools.OptionParser(
        usage=_(u'%prog [options] [--fix] [--undo] [--db file] ' +
                u'<track 1> [track 2] ...'),
        version="Python Audio Tools %s" % (audiotools.VERSION))

    parser.add_option('--fix',
                      action='store_true',
                      default=False,
                      dest='fix',
                      help=_(u'perform suggest fixes'))

    parser.add_option('--db',
                      action='store',
                      type='string',
                      dest='db',
                      help=_(u'undo database file'))

    parser.add_option('--undo',
                      action='store_true',
                      default=False,
                      dest='undo',
                      help=_(u'undo performed fixes'))

    parser.add_option('-V', '--verbose',
                      action='store',
                      dest='verbosity',
                      choices=audiotools.VERBOSITY_LEVELS,
                      default=audiotools.DEFAULT_VERBOSITY,
                      help=_(u'the verbosity level to execute at'))

    (options, args) = parser.parse_args()
    msg = audiotools.Messenger("tracklint", options)

    if (options.undo and (options.db is None)):
        msg.error(_(u"Cannot perform undo without undo db"))
        sys.exit(1)

    if (options.fix):
        if (options.db is not None):
            #if we're fixing tracks and have an undo DB,
            #save undo information to it during the fixing process
            try:
                undo_db = audiotools.delta.open_db(options.db)
            except:
                msg.error(_(u"Unable to open \"%s\"") %
                          (msg.filename(options.db)))
                sys.exit(1)
            try:
                for track in audiofiles(args, messenger=msg):
                    try:
                        update_and_backup(track, undo_db, msg)
                    except IOError:
                        msg.error(_(u"Unable to write \"%s\"") % \
                                      (msg.filename(track.filename)))
                        sys.exit(1)
                    except ValueError, err:
                        msg.error(unicode(err))
                        sys.exit(1)
            finally:
                undo_db.close()
        else:
            #if we're fixing tracks and have no undo DB,
            #simply overwrite the track and track metadata directly
            #if changes have been made
            for track in audiofiles(args, messenger=msg):
                try:
                    update_without_backup(track, msg)
                except IOError:
                    msg.error(_(u"Unable to write \"%s\"") % \
                                  (msg.filename(track.filename)))
                    sys.exit(1)
                except ValueError, err:
                    msg.error(unicode(err))
                    sys.exit(1)
    elif (options.undo):
        try:
            undo_db = audiotools.delta.open_db(options.db)
        except:
            msg.error(_(u"Unable to open \"%s\"") % (msg.filename(options.db)))
            sys.exit(1)
        try:
            for track in audiofiles(args, messenger=msg):
                try:
                    undo_from_backup(track, undo_db, msg)
                except IOError:
                    msg.error(_(u"Unable to write \"%s\"") % \
                                  (msg.filename(track.filename)))
                    sys.exit(1)
        finally:
            undo_db.close()
    else:  # a dry-run of the fixing procedure, with no changes made
        for track in audiofiles(args, messenger=msg):
            fixes = []
            track.clean(fixes)
            display_messages(msg, track, fixes)
