#!/usr/bin/python3
#
# SoundConverter - GNOME application for converting between audio formats.
# Copyright 2004 Lars Wirzenius
# Copyright 2005-2025 Gautier Portet
# Copyright 2020-2025 Sezanzeb
#
# 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; version 3 of the License.
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA

"""SoundConverter Launcher."""

# imports and package setup

import gettext
import locale
import os
import sys
from importlib.metadata import distribution
from optparse import OptionGroup, OptionParser

_ = gettext.gettext

# read values from package metadata
dist = distribution("soundconverter")
NAME = dist.metadata["Name"]
VERSION = dist.version
GLADEFILE = str(dist.locate_file("data/soundconverter.glade"))

if not os.path.exists(GLADEFILE):
    paths = "/usr/share/soundconverter", "/usr/local/share/soundconverter", "/app/share/soundconverter"
    for path in paths:
        gladefile = os.path.join(path, "soundconverter.glade")
        if os.path.exists(gladefile):
            GLADEFILE = gladefile
            break

if GLADEFILE.startswith("/app"):

    # For flatpak, we need to explicitly (re-)bind the locale and the gettext
    # textdomain, because Python's default localedir is pointing to
    # /usr/share/locale, whereas flatpaks install their locales to
    # /app/share/locale.
    # For reference, see https://docs.python.org/3/library/gettext.html#id3
    locale.setlocale(locale.LC_ALL, "")
    locale.bindtextdomain(NAME, "/app/share/locale")
    gettext.bindtextdomain(NAME, "/app/share/locale")
    gettext.textdomain(NAME)

try:
    import gi

    gi.require_version("GstPbutils", "1.0")
    gi.require_version("Gst", "1.0")
    gi.require_version("Gtk", "3.0")
    from gi.repository import Gst, Gtk, GLib, Gdk  # noqa: F401, I001
except (ImportError, ValueError) as error:
    print(f'{NAME} needs GTK >= 3.0 (Error: "{error}")')
    sys.exit(1)

# Gst.init takes those args it knows from sys.argv and returns the remaining.
# For args compatible to gstreamer, see `gst-launch-1.0 --help-gst`
args = Gst.init(sys.argv)

if isinstance(args, list):
    # in tests it just suddenly returns a boolean instead. when writing tests,
    # beware that `--gst-...` arguments are not filtered here
    args = sys.argv

from soundconverter.gstreamer.converter import Converter
from soundconverter.interface.batch import (
    CLICheck,
    batch_main,
    use_memory_gsettings,
    validate_args,
)
from soundconverter.interface.preferences import rates
from soundconverter.interface.ui import gui_main
from soundconverter.util.logger import logger, update_verbosity
from soundconverter.util.settings import settings


def mode_callback(option, opt, value, parser, **kwargs):
    """Write the main mode (batch, gui, tags) into the options."""
    setattr(parser.values, option.dest, kwargs[option.dest])


class ModifiedOptionParser(OptionParser):
    """An OptionParser class that doesn't remove newlines on the epilog in
    order to show usage examples.

    https://stackoverflow.com/questions/1857346/

    See optparse.OptionParser for the original docstring
    """

    def format_epilog(self, formatter):
        if self.epilog is None:
            return ""
        return self.epilog


def parse_command_line():
    """Create and return the OptionParser.

    Parses the command line arguments and displays help with --help.
    """
    parser = ModifiedOptionParser(
        epilog=(
            "\nExamples:\n"
            "  soundconverter -b [input paths] -f flac -o [output path]\n"
            "  soundconverter -b ./file_1.flac ./file_2.flac -f mp3 -q 0 -m vbr -o ~/compressed -e skip\n"
            "  soundconverter -b ~/music -r -f m4a -q 320 -o /mnt/sd/compressed -p {artist}/{title}\n"
        ),
    )
    parser.add_option(
        "-b",
        "--batch",
        dest="main",
        action="callback",
        callback=mode_callback,
        callback_kwargs={"main": "batch"},
        help=_(
            "Convert in batch mode, from the command line, without "
            "a graphical user interface. You can use this from, "
            "say, shell scripts.",
        ),
    )
    parser.add_option(
        "-t",
        "--tags",
        dest="main",
        action="callback",
        callback=mode_callback,
        callback_kwargs={"main": "tags"},
        help=_(
            "Show tags for input files instead of converting "
            "them. This disables the graphical user interface.",
        ),
    )
    parser.add_option(
        "-d",
        "--debug",
        action="store_true",
        dest="debug",
        help=_("Displays additional debug information"),
        default=False,
    )
    parser.add_option(
        "-j",
        "--jobs",
        action="store",
        type="int",
        dest="forced-jobs",
        metavar="NUM",
        help=_("Force number of concurrent conversions."),
    )
    parser.add_option(
        "-D",
        "--delete-original",
        action="store_true",
        dest="delete-original",
        help=_("Deletes the original file when conversion is done."),
    )
    parser.add_option(
        "-R",
        "--output-resample",
        dest="output-resample",
        help=_("Resamples audio during conversion. Possible values: %(rates)s")
        % {"rates": ", ".join([str(item) for item in rates])},
        type=int,
    )

    # batch mode settings
    batch_option_group = OptionGroup(
        parser,
        "Batch Mode Options",
        "Those options will only have effect when the -b, -c or -t "
        "option is provided",
    )
    batch_option_group.add_option(
        "-f",
        "--format",
        dest="format",
        metavar="NAME",
        help=_(
            "Set the output format. "
            "aac/m4a, flac, mp3, ogg, opus and wav are supported.",
        ),
    )
    # mode might also be used for format options of other formats at some
    # point if applicable
    batch_option_group.add_option(
        "-m",
        "--mode",
        dest="mode",
        metavar="MODE",
        help=_("One of cbr, abr or vbr (default) for mp3"),
    )
    batch_option_group.add_option(
        "-e",
        "--existing",
        dest="existing",
        metavar="MODE",
        help=_(
            f"One of {Converter.SKIP}, {Converter.OVERWRITE} or {Converter.INCREMENT}",
        ),
        default=Converter.INCREMENT,
    )
    batch_option_group.add_option(
        "-r",
        "--recursive",
        action="store_true",
        dest="recursive",
        help=_("Go recursively into subdirectories"),
        default=False,
    )
    batch_option_group.add_option(
        "-o",
        "--output",
        action="store",
        dest="output-path",
        metavar="PATH",
        help=_(
            "Put converted files into a different directory while rebuilding "
            "the original directory structure. This includes the name of the "
            "original directory if a directory was selected.",
        ),
        default=None,
    )
    batch_option_group.add_option(
        "-p",
        "--pattern",
        action="store",
        dest="custom-filename-pattern",
        metavar="PATH",
        help=_(
            "For example {artist}/{album}/{title}. title defaults to the "
            'filename if unset. Unknown tags default to "Unknown ..." '
            "otherwise.",
        ),
        default=None,
    )

    batch_option_group.add_option(
        "-q",
        "--quality",
        action="store",
        type="float",
        dest="quality",
        metavar="NUM",
        help=_(
            "Quality of the converted output file. Possible values: "
            "vorbis: 0.0 - 1.0; "
            "aac/m4a: 0 - 400; "
            "opus: 6 - 510; "
            "mp3 vbr: 9 (low) - 0 (high); "
            "mp3 abr: 64 - 320; "
            "mp3 cbr: 64 - 320; "
            "wav: 8, 16, 32 (bitdepth); "
            "flac: 0 - 8 (compression strength)",
        ),
    )

    parser.add_option_group(batch_option_group)

    return parser


parser = parse_command_line()

options, files = parser.parse_args(args[1:])
options = vars(options)
# the only cli args that are not available over gio settings.
settings["main"] = options["main"] or "gui"
settings["debug"] = options["debug"]
settings["recursive"] = options["recursive"]
settings["existing"] = options["existing"]

# now that the settings are populated, the verbosity can be determined:
update_verbosity()

logger.info(f"{NAME} {VERSION}")

if settings["main"] == "gui":
    gui_main(NAME, VERSION, GLADEFILE, files)
else:
    if not files:
        logger.info("nothing to do…")

    # first check if the cli is used correctly
    if not validate_args(options):
        raise SystemExit

    # then store them in the gio settings
    use_memory_gsettings(options)

    if settings["main"] == "tags":
        CLICheck(files, verbose=True)
    elif settings["main"] == "batch":
        batch_main(files)
