#!/usr/pkg/bin/python3.8

# Audio Tools, a module and set of tools for manipulating audio data
# Copyright (C) 2007-2015  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 audiotools
import audiotools.ui
import os.path
import shutil
import termios
import audiotools.text as _
from audiotools import PY3


if audiotools.ui.AVAILABLE:
    urwid = audiotools.ui.urwid

    class Trackrename(urwid.Pile):
        def __init__(self, audio_tracks, format_string):
            """audio_tracks is a list of AudioFile objects
            format_string is a formatting string"""

            assert(isinstance(format_string, str))
            for t in audio_tracks:
                assert(isinstance(t, audiotools.AudioFile))

            self.__cancelled__ = True

            self.audio_tracks = audio_tracks

            # setup a previous/finish button set
            metadata_buttons = urwid.Filler(
                urwid.Columns(
                    widget_list=[('weight', 1,
                                  urwid.Button(_.LAB_CANCEL_BUTTON,
                                               on_press=self.exit)),
                                 ('weight', 2,
                                  urwid.Button(_.LAB_TRACKRENAME_RENAME,
                                               on_press=self.apply))],
                    dividechars=3,
                    focus_column=1))

            # setup a widget with an output preview
            self.track_previews = [urwid.Text(u"") for t in audio_tracks]
            self.output_tracks_list = urwid.ListBox(
                self.track_previews)
            self.output_tracks_frame = urwid.Frame(
                body=self.output_tracks_list)
            self.invalid_output_format = urwid.Filler(
                urwid.Text(_.ERR_INVALID_FILENAME_FORMAT,
                           align="center"))

            # setup a widget with the rename template
            output_format = urwid.Edit(
                edit_text=(format_string if PY3 else
                           format_string.decode('utf-8')),
                wrap='clip')
            urwid.connect_signal(output_format,
                                 'change',
                                 self.format_changed,
                                 audio_tracks)

            browse_fields = audiotools.ui.BrowseFields(output_format)
            template_row = urwid.Columns(
                [('fixed', 10,
                  urwid.Text(('label',
                              u"%s : " %
                              (_.LAB_OPTIONS_FILENAME_FORMAT)),
                             align="right")),
                 ('weight', 1, output_format),
                 ('fixed', 10, browse_fields)])

            # perform initial data population
            self.format_changed(output_format,
                                format_string,
                                audio_tracks)

            urwid.Pile.__init__(
                self,
                [('fixed', 3, urwid.LineBox(urwid.Filler(template_row))),
                 ('weight', 1,
                  urwid.LineBox(self.output_tracks_frame,
                                title=_.LAB_OPTIONS_OUTPUT_FILES)),
                 ('fixed', 1, metadata_buttons)])

        def apply(self, button):
            if not self.has_errors:
                self.__cancelled__ = False
                raise urwid.ExitMainLoop()

        def exit(self, button):
            self.__cancelled__ = True
            raise urwid.ExitMainLoop()

        def cancelled(self):
            return self.__cancelled__

        def handle_text(self, i):
            if i == 'esc':
                self.exit(None)

        def format_changed(self, widget, new_value, audio_tracks):
            try:
                # generate list of Filename objects
                # from list of AudioFile objects
                format_string = new_value if PY3 else new_value.encode("UTF-8")

                self.output_filenames = [
                    audiotools.Filename(
                        track.__class__.track_name(
                            file_path=track.filename,
                            track_metadata=track.get_metadata(),
                            format=format_string))
                    for track in audio_tracks]

                # and populate output files list
                for (path, preview) in zip(self.output_filenames,
                                           self.track_previews):
                    preview.set_text(path.__unicode__())

                if ((self.output_tracks_frame.get_body() is not
                     self.output_tracks_list)):
                    self.output_tracks_frame.set_body(
                        self.output_tracks_list)
                self.has_errors = False
            except (audiotools.UnsupportedTracknameField,
                    audiotools.InvalidFilenameFormat):
                # invalid filename string
                if ((self.output_tracks_frame.get_body() is not
                     self.invalid_output_format)):
                    self.output_tracks_frame.set_body(
                        self.invalid_output_format)
                self.has_errors = True

        def to_rename(self):
            """yields (old_name, new_name) tuples
            where old_name and new_name differ
            and the names are Filename objects"""

            if not self.has_errors:
                for (old_name,
                     new_name) in zip([audiotools.Filename(t.filename)
                                       for t in self.audio_tracks],
                                      self.output_filenames):
                    if old_name != new_name:
                        yield (old_name, new_name)


if (__name__ == '__main__'):
    import argparse

    parser = argparse.ArgumentParser(description=_.DESCRIPTION_TRACKRENAME)

    parser.add_argument("--version",
                        action="version",
                        version="Python Audio Tools %s" % (audiotools.VERSION))

    parser.add_argument("-I", "--interactive",
                        action="store_true",
                        default=False,
                        dest="interactive",
                        help=_.OPT_INTERACTIVE_OPTIONS)

    parser.add_argument("-V", "--verbose",
                        dest="verbosity",
                        choices=audiotools.VERBOSITY_LEVELS,
                        default=audiotools.DEFAULT_VERBOSITY,
                        help=_.OPT_VERBOSE)

    parser.add_argument('--format',
                        default=audiotools.FILENAME_FORMAT,
                        dest='format',
                        help=_.OPT_FORMAT)

    parser.add_argument("filenames",
                        metavar="FILENAME",
                        nargs="+",
                        help=_.OPT_INPUT_FILENAME)

    options = parser.parse_args()
    msg = audiotools.Messenger(options.verbosity == "quiet")

    # ensure interactive mode is available, if selected
    if options.interactive and (not audiotools.ui.AVAILABLE):
        audiotools.ui.not_available_message(msg)
        sys.exit(1)

    try:
        audiofiles = audiotools.open_files(options.filenames,
                                           messenger=msg,
                                           no_duplicates=True)
    except audiotools.DuplicateFile as err:
        msg.error(_.ERR_DUPLICATE_FILE % (err.filename,))
        sys.exit(1)

    if len(audiofiles) < 1:
        msg.error(_.ERR_FILES_REQUIRED)
        sys.exit(1)

    # get a set of files to be renamed
    # and generate an error if a duplicate occurs
    renamed_filenames = {audiotools.Filename(t.filename) for t in audiofiles}

    if options.interactive:
        widget = Trackrename(audio_tracks=audiofiles,
                             format_string=options.format)
            #audio_class=audiofiles[0].__class__,
            #format_string=options.format,
            #input_filenames=[audiotools.Filename(t.filename) for t in
            #                 audiofiles],
            #metadatas=[t.get_metadata() for t in audiofiles])
        loop = audiotools.ui.urwid.MainLoop(
            widget,
            audiotools.ui.style(),
            screen=audiotools.ui.Screen(),
            unhandled_input=widget.handle_text,
            pop_ups=True)

        try:
            loop.run()
            msg.ansi_clearscreen()
        except (termios.error, IOError):
            msg.error(_.ERR_TERMIOS_ERROR)
            msg.info(_.ERR_TERMIOS_SUGGESTION)
            msg.info(audiotools.ui.xargs_suggestion(sys.argv))
            sys.exit(1)

        if not widget.cancelled():
            to_rename = list(widget.to_rename())
        else:
            sys.exit(0)
    else:
        to_rename = []  # a (old_name, new_name) tuple
        try:
            for track in audiofiles:
                original_filename = audiotools.Filename(track.filename)
                new_filename = audiotools.Filename(
                    os.path.join(
                        os.path.dirname(track.filename),
                        track.track_name(file_path=track.filename,
                                         track_metadata=track.get_metadata(),
                                         format=options.format)))
                if new_filename != original_filename:
                    if new_filename not in renamed_filenames:
                        renamed_filenames.add(new_filename)
                        to_rename.append((original_filename, new_filename))
                    else:
                        msg.error(_.ERR_DUPLICATE_OUTPUT_FILE %
                                  (new_filename,))
                        sys.exit(1)
        except audiotools.UnsupportedTracknameField as err:
            err.error_msg(msg)
            sys.exit(1)
        except audiotools.InvalidFilenameFormat as err:
            msg.error(err)
            sys.exit(1)

    # create subdirectories for renamed files if necessary
    for (original_filename, new_filename) in to_rename:
        try:
            audiotools.make_dirs(str(new_filename))
        except OSError as err:
            msg.os_error(err)
            sys.exit(1)

    # perform the actual renaming itself
    for (i, (original_filename, new_filename)) in enumerate(to_rename):
        try:
            shutil.move(str(original_filename), str(new_filename))
            msg.info(
                audiotools.output_progress(
                    _.LAB_ENCODE % {"source": original_filename,
                                    "destination": new_filename},
                    i + 1, len(to_rename)))
        except IOError as err:
            msg.error(_.ERR_RENAME %
                      {"source": original_filename,
                       "target": new_filename})
            sys.exit(1)
