#!/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 sys
import os.path
import audiotools
import audiotools.text as _

MAX_CPUS = audiotools.MAX_JOBS


class Counter:
    def __init__(self):
        self.value = 0

    def __int__(self):
        return self.value

    def increment(self):
        self.value += 1


class FailedAudioFile:
    def __init__(self, class_name, filename, err):
        self.NAME = class_name
        self.filename = filename
        self.err = err

    def verify(self):
        raise self.err


def open_file(filename):
    #this is much like audiotools.open
    #except that file init errors fail differently

    f = open(filename, "rb")
    try:
        audio_class = audiotools.file_type(f)
        if (((audio_class is not None) and
             (audio_class.has_binaries(audiotools.BIN)))):
            class_name = audio_class.NAME
            try:
                return audio_class(filename)
            except audiotools.InvalidFile, err:
                return FailedAudioFile(class_name, filename, err)
        else:
            raise audiotools.UnsupportedFile(filename)
    finally:
        f.close()


def get_tracks(args, queued_files, accept_list=None):
    if (accept_list is not None):
        accept_list = set(accept_list)

    for path in args:
        if (os.path.isfile(path)):
            try:
                filename = audiotools.Filename(path)
                if (filename not in queued_files):
                    queued_files.add(filename)
                    track = open_file(str(filename))
                    if ((accept_list is None) or (track.NAME in accept_list)):
                        yield track
            except (audiotools.UnsupportedFile, IOError, OSError):
                continue
        elif (os.path.isdir(path)):
            for (d, ds, fs) in os.walk(path):
                for track in get_tracks([os.path.join(d, f) for f in fs],
                                        queued_files,
                                        accept_list=accept_list):
                    yield track


def verify(progress, track):
    try:
        track.verify(progress)
        return (audiotools.Filename(track.filename), track.NAME, None)
    except audiotools.InvalidFile, err:
        return (audiotools.Filename(track.filename), track.NAME, unicode(err))


class Results:
    def __init__(self, messenger):
        self.msg = messenger
        self.summary_success = {}
        self.summary_failure = {}

    def display(self, result):
        (filename, track_type, error) = result
        if (error is None):
            self.summary_success.setdefault(track_type, Counter()).increment()
            return _.LAB_TRACKVERIFY_RESULT % {
                "path": filename,
                "result": self.msg.ansi(_.LAB_TRACKVERIFY_OK,
                                        [self.msg.FG_GREEN])}
        else:
            self.summary_failure.setdefault(track_type, Counter()).increment()
            return _.LAB_TRACKVERIFY_RESULT % {
                "path": filename,
                "result": self.msg.ansi(error,
                                        [self.msg.FG_RED])}


if (__name__ == '__main__'):
    parser = audiotools.OptionParser(
        usage=_.USAGE_TRACKVERIFY,
        version="Python Audio Tools %s" % (audiotools.VERSION))

    parser.add_option('-t', '--type',
                      action='append',
                      dest='accept_list',
                      metavar='type',
                      choices=audiotools.TYPE_MAP.keys(),
                      help=_.OPT_TYPE_TRACKVERIFY)

    parser.add_option('-R', '--no-summary',
                      action='store_true',
                      dest='no_summary',
                      help=_.OPT_NO_SUMMARY)

    parser.add_option('-j', '--joint',
                      action='store',
                      type='int',
                      default=MAX_CPUS,
                      dest='max_processes',
                      help=_.OPT_JOINT)

    parser.add_option('-V', '--verbose',
                      action='store',
                      dest='verbosity',
                      choices=audiotools.VERBOSITY_LEVELS,
                      default=audiotools.DEFAULT_VERBOSITY,
                      help=_.OPT_VERBOSE)

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

    queued_files = set([])  # a set of Filename objects already encountered
    results = Results(msg)
    queue = audiotools.ExecProgressQueue(audiotools.ProgressDisplay(msg))
    for track in get_tracks(args, queued_files, options.accept_list):
        queue.execute(function=verify,
                      progress_text=
                      unicode(audiotools.Filename(track.filename)),
                      completion_output=results.display,
                      track=track)
    msg.ansi_clearline()

    queue.run(options.max_processes)

    summary_success = results.summary_success
    summary_failure = results.summary_failure
    formats = sorted(list(set(summary_success.keys()) |
                          set(summary_failure.keys())))
    success_total = sum(map(int, summary_success.values()))
    failure_total = sum(map(int, summary_failure.values()))

    if ((len(formats) > 0) and (not options.no_summary)):
        msg.output(_.LAB_TRACKVERIFY_RESULTS)
        msg.output(u"")
        msg.new_row()
        msg.output_column(_.LAB_TRACKVERIFY_RESULT_FORMAT, True)
        msg.output_column(u" ")
        msg.output_column(_.LAB_TRACKVERIFY_RESULT_SUCCESS, True)
        msg.output_column(u" ")
        msg.output_column(_.LAB_TRACKVERIFY_RESULT_FAILURE, True)
        msg.output_column(u" ")
        msg.output_column(_.LAB_TRACKVERIFY_RESULT_TOTAL, True)
        msg.divider_row([u"-", u" ", u"-", u" ", u"-", u" ", u"-"])

        for format in formats:
            success = int(summary_success.get(format, 0))
            failure = int(summary_failure.get(format, 0))
            msg.new_row()
            msg.output_column(format.decode('ascii'), True)
            msg.output_column(u" ")
            msg.output_column(unicode(success), True)
            msg.output_column(u" ")
            msg.output_column(unicode(failure), True)
            msg.output_column(u" ")
            msg.output_column(unicode(success + failure), True)

        msg.divider_row([u"-", u" ", u"-", u" ", u"-", u" ", u"-"])
        msg.new_row()
        msg.output_column(u"summary", True)
        msg.output_column(u" ")
        msg.output_column(unicode(success_total), True)
        msg.output_column(u" ")
        msg.output_column(unicode(failure_total), True)
        msg.output_column(u" ")
        msg.output_column(unicode(success_total + failure_total), True)

        msg.output_rows()

    if (failure_total > 0):
        sys.exit(1)
