#!/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 gettext

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


def check_binaries(msg, binary_list):
    for binary in binary_list:
        msg.new_row()
        if (audiotools.BIN.can_execute(audiotools.BIN[binary])):
            msg.output_column(u"+ ")
            msg.output_column(_(u"found"), True)
            msg.output_column(u" : ")
            msg.output_column(binary.decode('ascii'))
        else:
            msg.output_column(u"- ")
            msg.output_column(_(u"not found"), True)
            msg.output_column(u" : ")
            msg.output_column(binary.decode('ascii'))
    msg.output_rows()


class FormatSummary:
    def __init__(self, format, quality, binaries, replaygain):
        """format and quality are unicode.  The rest are list of unicode.

        All should be formatted for output."""

        self.format = format
        self.quality = quality
        self.binaries = binaries
        self.replaygain = replaygain

    @classmethod
    def headers(cls, messenger):
        messenger.new_row()
        messenger.output_column(u" ")
        messenger.output_column(u" ")
        messenger.output_column(u" ")
        messenger.output_column(u" ")
        messenger.output_column(u" default ")
        messenger.output_column(u" ")
        messenger.output_column(u" ")

        messenger.new_row()
        messenger.output_column(_(u" type "))
        messenger.output_column(u" ")
        messenger.output_column(_(u" binaries "))
        messenger.output_column(u" ")
        messenger.output_column(_(u" quality "))
        messenger.output_column(u" ")
        messenger.output_column(_(u" ReplayGain "))
        messenger.divider_row([u"-", u"+", u"-",
                                   u"+", u"-", u"+", u"-"])

    def output_rows(self, messenger):
        #first, put our rows of data into lists of columns

        format_col = [self.format]
        quality_col = [self.quality]
        binaries_col = self.binaries[:]
        replaygain_col = self.replaygain[:]

        #then, make our columns consistently-sized
        total_rows = max(map(len, [format_col,
                                   quality_col,
                                   binaries_col,
                                   replaygain_col]))

        for col in [format_col, quality_col, binaries_col, replaygain_col]:
            col.extend([u""] * (total_rows - len(col)))

        #finally, output our rows to the Messenger
        for (format, quality, binary, replaygain) in zip(format_col,
                                                         quality_col,
                                                         binaries_col,
                                                         replaygain_col):
            messenger.new_row()
            messenger.output_column(u" %s " % (format), True)
            messenger.output_column(u"|")
            messenger.output_column(u" %s " % (binary))
            messenger.output_column(u"|")
            messenger.output_column(u" %s " % (quality))
            messenger.output_column(u"|")
            messenger.output_column(u" %s " % (replaygain))
        messenger.divider_row([u"-", u"+", u"-",
                               u"+", u"-", u"+", u"-"])

    @classmethod
    def from_audiofile(cls, messenger, audiofile, binaries):
        if (audiofile.has_binaries(binaries)):
            code = [messenger.FG_GREEN]
        else:
            code = [messenger.FG_RED]

        if (audiofile.NAME == audiotools.DEFAULT_TYPE):
            code.append(messenger.BOLD)
            code.append(messenger.UNDERLINE)

        format = messenger.ansi(audiofile.NAME.decode('ascii'), code)

        binaries_list = []
        for binary in audiofile.BINARIES:
            if (binaries.can_execute(binaries[binary])):
                code = [messenger.FG_GREEN]
            else:
                code = [messenger.FG_RED]
            binaries_list.append(messenger.ansi(binaries[binary], code))

        replaygain = []
        if (len(audiofile.REPLAYGAIN_BINARIES) > 0):
            for binary in audiofile.REPLAYGAIN_BINARIES:
                if (binaries.can_execute(binaries[binary])):
                    code = [messenger.FG_GREEN]
                else:
                    code = [messenger.FG_RED]
                replaygain.append(messenger.ansi(binaries[binary], code))
        elif (audiofile.can_add_replay_gain()):
            replaygain.append(messenger.ansi("YES", [messenger.FG_GREEN]))
        else:
            replaygain.append("N/A")

        return cls(format,
                   audiotools.__default_quality__(
                audiofile.NAME).decode('ascii'),
                   binaries_list,
                   replaygain)


if (__name__ == '__main__'):
    try:
        import audiotools
    except ImportError:
        print _(u"* audiotools Python module not found!")
        print _(u"Perhaps you should re-install the Python Audio Tools")
        sys.exit(1)
    try:
        import audiotools.player
    except ImportError:
        print _(u"* audiotools.player Python module not found!")
        print _(u"Perhaps you should re-install the Python Audio Tools")
        sys.exit(1)

    parser = audiotools.OptionParser(
        usage=_(u'%prog [options]'),
        version="Python Audio Tools %s" % (audiotools.VERSION))

    opt_map = {"system_cdrom": ("System", "cdrom"),
               "system_cdrom_read_offset": ("System", "cdrom_read_offset"),
               "system_cdrom_write_offset": ("System", "cdrom_write_offset"),
               "system_fs_encoding": ("System", "fs_encoding"),
               "system_io_encoding": ("System", "io_encoding"),
               "system_maximum_jobs": ("System", "maximum_jobs"),

               "thumbnail_format": ("Thumbnail", "format"),
               "thumbnail_size": ("Thumbnail", "size"),

               "freedb_server": ("FreeDB", "server"),
               "freedb_port": ("FreeDB", "port"),
               "use_freedb": ("FreeDB", "service"),

               "musicbrainz_server": ("MusicBrainz", "server"),
               "musicbrainz_port": ("MusicBrainz", "port"),
               "use_musicbrainz": ("MusicBrainz", "service"),

               "filename_format": ("Filenames", "format"),
               "verbosity": ("Defaults", "verbosity"),

               "id3_digit_padding": ("ID3", "pad"),
               "id3v2_version": ("ID3", "id3v2"),
               "id3v1_version": ("ID3", "id3v1"),
               "add_replaygain": ("ReplayGain", "add_by_default")}

    system = audiotools.OptionGroup(parser, _(u"System Options"))

    system.add_option('-c', '--cdrom',
                      action='store',
                      dest='system_cdrom',
                      metavar='PATH',
                      help=_(u'the CD-ROM drive to use'))

    system.add_option('--cdrom-read-offset',
                      action='store',
                      type='int',
                      metavar='INT',
                      dest='system_cdrom_read_offset',
                      help=_(u'the CD-ROM read offset to use'))

    system.add_option('--cdrom-write-offset',
                      action='store',
                      type='int',
                      metavar='INT',
                      dest='system_cdrom_write_offset',
                      help=_(u'the CD-ROM write offset to use'))

    system.add_option(
        '--fs-encoding',
        action='store',
        metavar='ENCODING',
        dest='system_fs_encoding',
        help=_(u"the filesystem's text encoding"))

    system.add_option(
        '--io-encoding',
        action='store',
        metavar='ENCODING',
        dest='system_io_encoding',
        help=_(u"the system's text encoding"))

    parser.add_option_group(system)

    transcoding = audiotools.OptionGroup(parser, _(u"Transcoding Options"))

    transcoding.add_option(
        '-t', '--type',
        action='store',
        dest='type',
        choices=audiotools.TYPE_MAP.keys(),
        help=_(u'the default audio type to use, ' +
               'or the type for a given default quality level'))

    transcoding.add_option(
        '-q', '--quality',
        action='store',
        type='string',
        dest='quality',
        help=_(u'the default quality level for a given audio type'))

    transcoding.add_option(
        '-j', '--joint',
        action='store',
        type='int',
        metavar='MAX_PROCESSES',
        dest='system_maximum_jobs',
        help=_(u'the maximum number of processes to run at a time'))

    parser.add_option_group(transcoding)

    thumbnails = audiotools.OptionGroup(parser, _(u"Thumbnail Options"))

    thumbnails.add_option(
        '--thumbnail-format',
        action='store',
        metavar='TYPE',
        dest='thumbnail_format',
        help=_(u'the image format to use for thumbnails ' +
               u'such as "jpeg" or "png"'))

    thumbnails.add_option(
        '--thumbnail-size',
        action='store',
        type='int',
        metavar='INT',
        dest='thumbnail_size',
        help=_(u'the maximum size of each thumbnail, in pixels squared'))

    parser.add_option_group(thumbnails)

    freedb = audiotools.OptionGroup(parser, _(u"FreeDB Options"))

    freedb.add_option(
        '--use-freedb',
        metavar='yes/no',
        choices=("yes", "no"),
        dest='use_freedb')

    freedb.add_option(
        '--freedb-server',
        action='store',
        metavar='HOSTNAME',
        dest='freedb_server',
        help=_(u'the FreeDB server to use'))

    freedb.add_option(
        '--freedb-port',
        action='store',
        metavar='PORT',
        type='int',
        dest='freedb_port',
        help=_(u'the FreeDB port to use'))

    parser.add_option_group(freedb)

    musicbrainz = audiotools.OptionGroup(parser, _(u"MusicBrainz Options"))

    musicbrainz.add_option(
        '--use-musicbrainz',
        metavar='yes/no',
        choices=("yes", "no"),
        dest='use_musicbrainz')

    musicbrainz.add_option(
        '--musicbrainz-server',
        action='store',
        metavar='HOSTNAME',
        dest='musicbrainz_server',
        help=_(u'the MusicBrainz server to use'))

    musicbrainz.add_option(
        '--musicbrainz-port',
        action='store',
        metavar='PORT',
        type='int',
        dest='musicbrainz_port',
        help=_(u'the MusicBrainz port to use'))

    parser.add_option_group(musicbrainz)

    id3 = audiotools.OptionGroup(parser, _("ID3 Options"))

    id3.add_option(
        '--id3v2-version',
        action='store',
        type='choice',
        choices=('id3v2.2', 'id3v2.3', 'id3v2.4', 'none'),
        dest='id3v2_version',
        metavar='VERSION',
        help=_('which ID3v2 version to use by default, if any'))

    id3.add_option(
        '--id3v1-version',
        action='store',
        type='choice',
        choices=('id3v1.1', 'none'),
        dest='id3v1_version',
        metavar='VERSION',
        help=_('which ID3v1 version to use by default, if any'))

    id3.add_option(
        '--id3v2-pad',
        action='store',
        type='choice',
        choices=('true', 'false'),
        dest='id3_digit_padding',
        metavar='true / false',
        help=_(u'whether or not to pad ID3v2 digit fields to 2 digits'))

    parser.add_option_group(id3)

    replaygain = audiotools.OptionGroup(parser, _("ReplayGain Options"))

    replaygain.add_option(
        '--replay-gain',
        action='store',
        type='choice',
        choices=('yes', 'no'),
        dest='add_replaygain',
        metavar='yes / no',
        help=_(u"whether to add ReplayGain metadata by default"))

    parser.add_option_group(replaygain)

    binaries = audiotools.OptionGroup(parser, _(u"Binaries Options"))

    bins = set([])
    for audioclass in audiotools.AVAILABLE_TYPES:
        for binary in audioclass.BINARIES:
            bins.add(binary)

    for binary in sorted(list(bins)):
        binaries.add_option('--' + binary,
                            action='store',
                            metavar='PATH',
                            dest='binary_' + binary)
        opt_map["binary_" + binary] = ("Binaries", binary)

    parser.add_option_group(binaries)

    parser.add_option(
        '--format',
        action='store',
        metavar='FORMAT',
        dest='filename_format',
        help=_(u'the format string for new filenames'))

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

    (options, args) = parser.parse_args()

    msg = audiotools.Messenger("audiotools-config", None)

    if (len(sys.argv) < 2):
        FormatSummary.headers(msg)
        for audiotype in sorted(audiotools.AVAILABLE_TYPES,
                                lambda x, y: cmp(x.NAME, y.NAME)):
            FormatSummary.from_audiofile(
                msg, audiotype, audiotools.BIN).output_rows(msg)
        msg.output_rows()

        msg.new_row()
        msg.output_column(_(u"CD Burning via track2cd"), True)
        msg.output_column(u" : ")
        burning_options = []
        if (audiotools.BIN.can_execute(audiotools.BIN["cdrecord"])):
            burning_options.append(_(u"without cue"))
        if (audiotools.BIN.can_execute(audiotools.BIN["cdrdao"])):
            burning_options.append(_(u"with cue"))
        if (len(burning_options) == 0):
            burning_options.append(_(u"no"))
        msg.output_column(u", ".join([s.decode('ascii')
                                      for s in burning_options]))
        msg.output_rows()
        check_binaries(msg, ["cdrecord", "cdrdao"])

        msg.output(u"-" * 30)

        msg.output(_(u"System configuration:"))
        msg.output(u"")

        msg.new_row()
        msg.output_column(u"Use MusicBrainz service", True)
        msg.output_column(u" : ")
        msg.output_column({True: u"yes",
                           False: u"no"}[audiotools.MUSICBRAINZ_SERVICE])

        msg.new_row()
        msg.output_column(_(u"Default MusicBrainz server"), True)
        msg.output_column(u" : ")
        msg.output_column(audiotools.MUSICBRAINZ_SERVER.decode('ascii',
                                                               'replace'))
        msg.new_row()
        msg.output_column(_(u"Default MusicBrainz port"), True)
        msg.output_column(u" : ")
        msg.output_column(unicode(audiotools.MUSICBRAINZ_PORT))
        msg.blank_row()

        msg.new_row()
        msg.output_column(u"Use FreeDB service", True)
        msg.output_column(u" : ")
        msg.output_column({True: u"yes",
                           False: u"no"}[audiotools.FREEDB_SERVICE])

        msg.new_row()
        msg.output_column(_(u"Default FreeDB server"), True)
        msg.output_column(u" : ")
        msg.output_column(audiotools.FREEDB_SERVER.decode('ascii',
                                                          'replace'))
        msg.new_row()
        msg.output_column(_(u"Default FreeDB port"), True)
        msg.output_column(u" : ")
        msg.output_column(unicode(audiotools.FREEDB_PORT))

        msg.blank_row()

        msg.new_row()
        msg.output_column(_(u"Default CD-ROM device"), True)
        msg.output_column(u" : ")
        msg.output_column(audiotools.DEFAULT_CDROM.decode(
                audiotools.FS_ENCODING))

        msg.new_row()
        msg.output_column(_(u"CD-ROM sample read offset"), True)
        msg.output_column(u" : ")
        msg.output_column(
            unicode(audiotools.config.getint_default("System",
                                                     "cdrom_read_offset", 0)))

        msg.new_row()
        msg.output_column(_(u"CD-ROM sample write offset"), True)
        msg.output_column(u" : ")
        msg.output_column(
            unicode(audiotools.config.getint_default("System",
                                                     "cdrom_write_offset", 0)))

        msg.blank_row()

        msg.new_row()
        msg.output_column(_(u"Default simultaneous jobs"), True)
        msg.output_column(u" : ")
        msg.output_column(unicode(audiotools.MAX_JOBS))

        msg.new_row()
        msg.output_column(_(u"Default verbosity level"), True)
        msg.output_column(u" : ")
        msg.output_column(audiotools.DEFAULT_VERBOSITY.decode('ascii'))

        output_options = []
        for player in audiotools.player.AUDIO_OUTPUT:
            if (player.available()):
                if (len(output_options) == 0):
                    output_options.append(msg.ansi(player.NAME.decode('ascii'),
                                                   [msg.BOLD,
                                                    msg.UNDERLINE]))
                else:
                    output_options.append(player.NAME.decode('ascii'))

        msg.new_row()
        msg.output_column(_(u"Audio output"), True)
        msg.output_column(u" : ")
        msg.output_column(u", ".join(output_options))

        msg.new_row()
        msg.output_column(_(u"Filesystem text encoding"), True)
        msg.output_column(u" : ")
        msg.output_column(audiotools.FS_ENCODING.decode('ascii'))

        msg.new_row()
        msg.output_column(_(u"TTY text encoding"), True)
        msg.output_column(u" : ")
        msg.output_column(audiotools.IO_ENCODING.decode('ascii'))

        msg.blank_row()

        msg.new_row()
        msg.output_column(_(u"ID3v2 tag version"), True)
        msg.output_column(u" : ")
        msg.output_column({"id3v2.2": u"ID3v2.2",
                           "id3v2.3": u"ID3v2.3",
                           "id3v2.4": u"ID3v2.4",
                           "none": _(u"no ID3v2 tags")}.get(
                audiotools.config.get_default("ID3", "id3v2", "id3v2.3"),
                u"ID3v2.3"))
        msg.new_row()
        msg.output_column(_(u"ID3v2 digit padding"), True)
        msg.output_column(u" : ")
        msg.output_column({True: _(u'padded to 2 digits ("01", "02", etc.)'),
                           False: _(u'no padding ("1", "2", etc.)')}[
                audiotools.config.getboolean_default("ID3", "pad", False)])
        msg.new_row()
        msg.output_column(_(u"ID3v1 tag version"), True)
        msg.output_column(u" : ")
        msg.output_column({"id3v1.1": u"ID3v1.1",
                           "none": _(u"no ID3v1 tags")}.get(
                audiotools.config.get_default("ID3", "id3v1", "id3v1.1"),
                u"ID3v1.1"))
        msg.blank_row()

        msg.new_row()
        msg.output_column(_(u"Can create thumbnails"), True)
        msg.output_column(u" : ")
        if (audiotools.can_thumbnail()):
            msg.output_column(_(u"yes"))
        else:
            msg.output_column(_(u"no") + u" " +
                              _(u"(Python Imaging Library not found)"))

        msg.new_row()
        msg.output_column(_(u"Default thumbnail format"), True)
        msg.output_column(u" : ")
        msg.output_column(audiotools.THUMBNAIL_FORMAT.decode('ascii'))

        msg.new_row()
        msg.output_column(_(u"Default thumbnail size"), True)
        msg.output_column(u" : ")
        msg.output_column(u"%d\u00D7%d" % (audiotools.THUMBNAIL_SIZE,
                                           audiotools.THUMBNAIL_SIZE))

        msg.blank_row()

        msg.new_row()
        msg.output_column(_(u"Add ReplayGain by default"), True)
        msg.output_column(u" : ")
        msg.output_column({True: u"yes",
                           False: u"no"}[audiotools.ADD_REPLAYGAIN])

        msg.blank_row()
        msg.output_rows()

        msg.output(_(u"File name format : %s") % \
                       (audiotools.FILENAME_FORMAT.decode('ascii', 'replace')))
    else:
        import os.path

        config = audiotools.config
        for attr in dir(options):
            if ((not attr.startswith('_')) and
                (not callable(getattr(options, attr))) and
                (getattr(options, attr) is not None)):
                if (attr in opt_map):
                    (section, option) = opt_map[attr]
                    if (not config.has_section(section)):
                        config.add_section(section)
                    config.set(section, option, getattr(options, attr))

        if (options.type is not None):
            AudioType = audiotools.TYPE_MAP[options.type]

            if (options.quality is not None):
                if (options.quality == 'help'):
                    if (len(AudioType.COMPRESSION_MODES) > 1):
                        msg.info(_(u"Available compression types for %s:") %
                                 (AudioType.NAME))
                        for mode in AudioType.COMPRESSION_MODES:
                            msg.new_row()
                            if (mode == audiotools.__default_quality__(
                                    AudioType.NAME)):
                                msg.output_column(
                                    msg.ansi(mode.decode('ascii'),
                                             [msg.BOLD, msg.UNDERLINE]), True)
                            else:
                                msg.output_column(mode.decode('ascii'), True)
                            if (mode in AudioType.COMPRESSION_DESCRIPTIONS):
                                msg.output_column(u" : ")
                            else:
                                msg.output_column(u"   ")
                            msg.output_column(
                                AudioType.COMPRESSION_DESCRIPTIONS.get(mode,
                                                                       u""))
                        msg.info_rows()
                    else:
                        msg.error(
                            _(u"Audio type %s has no compression modes") %
                            (AudioType.NAME))
                    sys.exit(0)
                elif (options.quality not in AudioType.COMPRESSION_MODES):
                    msg.error(
                        _(u"\"%(quality)s\" is not a supported " +
                          u"compression mode for type \"%(type)s\"") %
                        {"quality": options.quality,
                         "type": AudioType.NAME})
                    sys.exit(1)

                #we're setting the quality level for a given type
                if (not config.has_section("Quality")):
                    config.add_section("Quality")
                config.set("Quality", options.type, options.quality)
            else:
                #we're setting the default type
                if (not config.has_section("System")):
                    config.add_section("System")
                config.set("System", "default_type", options.type)

        configpath = os.path.expanduser('~/.audiotools.cfg')
        configfile = open(configpath, 'w')
        config.write(configfile)
        configfile.close()
        msg.info(_(u'* "%s" written') % (msg.filename(configpath)))
