#!/usr/pkg/bin/python3.11
# COPYRIGHT (C) 2020 Nicotine+ Team
# COPYRIGHT (C) 2020 Lene Preuss <lene.preuss@gmail.com>
# COPYRIGHT (C) 2016-2017 Michael Labouebe <gfarmerfr@free.fr>
# COPYRIGHT (C) 2008-2011 Quinox <quinox@users.sf.net>
# COPYRIGHT (C) 2006-2008 eL_vErDe <gandalf@le-vert.net>
# COPYRIGHT (C) 2006-2009 Daelstorm <daelstorm@gmail.com>
#
# GNU GENERAL PUBLIC LICENSE
#    Version 3, 29 June 2007
#
# 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 3 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, see <http://www.gnu.org/licenses/>.

"""
Nicotine+ Launcher.
"""

import importlib
import sys
from gettext import gettext as _

from pynicotine.utils import apply_translation
from pynicotine.utils import get_user_directories


def usage():
    print(_("""Nicotine+ is a Soulseek client.
Usage: nicotine [OPTION]...
  -c file, --config=file      Use non-default configuration file
  -p dir,  --plugins=dir      Use non-default directory for plugins
  -t,      --enable-trayicon  Enable the tray icon
  -d,      --disable-trayicon Disable the tray icon
  -h,      --help             Show help and exit
  -s,      --hidden           Start the program hidden so only the tray icon is shown
  -b ip,   --bindip=ip        Bind sockets to the given IP (useful for VPN)
  -l port, --port=port        Listen on the given port. Overrides the portrange configuration
  -v,      --version          Display version and exit"""))


def checkenv():

    # Require Python 3.5 or newer
    try:
        assert sys.version_info[:2] >= (3, 5), '.'.join(
            map(str, sys.version_info[:3])
        )
    except AssertionError as e:
        return _("""You're using an unsupported version of Python (%s).
You should install Python 3.5 or newer.""") % (e)

    # Require GTK+ >= 3
    try:
        import gi
    except ImportError:
        return _("Cannot find %s, please install it.") % "pygobject"
    else:
        try:
            gi.require_version('Gtk', '3.0')
        except ValueError as e:
            return _("""You're using an unsupported version of GTK (%s).
You should install GTK 3.0 or newer.""") % e

    try:
        from gi.repository import Gtk  # noqa: F401
    except ImportError:
        return _("Cannot import the Gtk module. Bad install of the python-gobject module?")

    # Require gdbm or semidbm, for faster loading of shelves
    if not importlib.util.find_spec("_gdbm") and \
            not importlib.util.find_spec("semidbm"):
        return _("Cannot find %(option1)s or %(option2)s, please install either one.") % {
            "option1": "gdbm",
            "option2": "semidbm"
        }

    # Require pynicotine
    if not importlib.util.find_spec("pynicotine"):
        return _("""Can not find Nicotine+ modules.
Perhaps they're installed in a directory which is not
in an interpreter's module search path.
(there could be a version mismatch between
what version of python was used to build the Nicotine
binary package and what you try to run Nicotine+ with).""")

    return None


def version():
    try:
        import pynicotine.utils
        print(_("Nicotine+ version %s") % pynicotine.utils.version)

    except ImportError:
        print(_("Cannot find the pynicotine.utils module."))


def renameprocess(newname, debug=False):

    errors = []

    # Renaming ourselves for ps et al.
    try:
        import procname
        procname.setprocname(newname)
    except Exception:
        errors.append("Failed procname module")

    # Renaming ourselves for pkill et al.
    try:
        import ctypes
        # GNU/Linux style
        libc = ctypes.CDLL('libc.so.6')
        libc.prctl(15, ctypes.c_char_p(newname.encode('ascii')), 0, 0, 0)
    except Exception:
        errors.append("Failed GNU/Linux style")

    try:
        import dl
        # FreeBSD style
        libc = dl.open('/lib/libc.so.6')
        libc.call('setproctitle', newname + '\0')
    except Exception:
        errors.append("Failed FreeBSD style")

    if debug and errors:
        msg = [_("Errors occured while trying to change process name:")]
        for i in errors:
            msg.append("%s" % (i,))
        print('\n'.join(msg))


def rescanshares(config, data_dir):
    import queue

    from pynicotine.config import Config
    from pynicotine.logfacility import console
    from pynicotine.shares import Shares

    config = Config(config, data_dir)
    config.read_config()

    # Show normal log messages in console
    console.set_log_levels((0, 1))

    print(_("""WARNING! Rescanning shares will not work if Nicotine+ is already open. If you need to rescan
your shares this way, keep Nicotine+ closed until rescanning is done."""))

    queue = queue.Queue(0)
    shares = Shares(None, config, queue)

    shares.rescan_shares()

    if config.sections["transfers"]["enablebuddyshares"]:
        shares.rescan_buddy_shares()


def run():

    renameprocess('nicotine')

    import getopt
    import os.path
    try:
        opts, args = getopt.getopt(sys.argv[1:],
                                   "hc:p:tdrvswb:",
                                   [
                                       "help",
                                       "rescan",
                                       "config=",
                                       "plugins=",
                                       "profile",
                                       "enable-trayicon",
                                       "disable-trayicon",
                                       "version",
                                       "hidden",
                                       "bindip=",
                                       "port="
        ]
        )
    except getopt.GetoptError:
        # print help information and exit
        usage()
        sys.exit(2)

    config_dir, data_dir = get_user_directories()
    config = os.path.join(config_dir, 'config')
    plugins = os.path.join(data_dir, 'plugins')

    profile = False
    trayicon = True
    rescan = False
    hidden = False
    bindip = None
    port = None

    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()

        if o in ("-c", "--config"):
            config = a

        if o in ("-p", "--plugins"):
            plugins = a

        if o in ("-b", "--bindip"):
            bindip = a

        if o in ("-l", "--port"):
            port = a

        if o == "--profile":
            profile = True

        if o in ("-t", "--enable-trayicon"):
            trayicon = True

        if o in ("-d", "--disable-trayicon"):
            trayicon = False

        if o in ('-r', '--rescan'):
            """ Flag currently undocumented, since we can't rescan shares while Nicotine+ is open (shelve limitation) """
            rescan = True

        if o in ('-s', '--hidden'):
            hidden = True

        if o in ("-v", "--version"):
            version()
            sys.exit()

    result = checkenv()

    if result is None:

        # Setting gettext and locale
        apply_translation()

        if rescan:
            rescanshares(config, data_dir)
            return

        from pynicotine.gtkgui import frame
        app = frame.MainApp(data_dir, config, plugins, trayicon, hidden, bindip, port)

        if profile:

            import cProfile

            dumpfile = os.path.expanduser(config) + ".profile"
            profiler = cProfile.Profile()

            print(_("Starting using the profiler (saving dump to %s)") % dumpfile)

            profiler.enable()
            app.run(None)
            profiler.disable()

            profiler.dump_stats(dumpfile)
        else:
            app.run(None)
    else:
        print(result)


if __name__ == '__main__':
    try:
        run()
    except SystemExit:
        raise
    except Exception:
        import traceback
        traceback.print_exc()
