"""Utility and helper methods for tmuxp.

tmuxp.shell
~~~~~~~~~~~

"""
import logging
import os

logger = logging.getLogger(__name__)


def has_ipython():
    try:
        from IPython import start_ipython  # NOQA F841
    except ImportError:
        try:
            from IPython.Shell import IPShell  # NOQA F841
        except ImportError:
            return False

    return True


def has_ptpython():
    try:
        from ptpython.repl import embed, run_config  # NOQA F841
    except ImportError:
        try:
            from prompt_toolkit.contrib.repl import embed, run_config  # NOQA F841
        except ImportError:
            return False

    return True


def has_ptipython():
    try:
        from ptpython.ipython import embed  # NOQA F841
        from ptpython.repl import run_config  # NOQA F841
    except ImportError:
        try:
            from prompt_toolkit.contrib.ipython import embed  # NOQA F841
            from prompt_toolkit.contrib.repl import run_config  # NOQA F841
        except ImportError:
            return False

    return True


def has_bpython():
    try:
        from bpython import embed  # NOQA F841
    except ImportError:
        return False
    return True


def detect_best_shell():
    if has_ptipython():
        return "ptipython"
    elif has_ptpython():
        return "ptpython"
    elif has_ipython():
        return "ipython"
    elif has_bpython():
        return "bpython"
    return "code"


def get_bpython(options, extra_args=None):
    if extra_args is None:
        extra_args = {}

    from bpython import embed  # NOQA F841

    def launch_bpython():
        imported_objects = get_launch_args(**options)
        kwargs = {}
        if extra_args:
            kwargs["args"] = extra_args
        embed(imported_objects, **kwargs)

    return launch_bpython


def get_ipython_arguments():
    ipython_args = "IPYTHON_ARGUMENTS"
    return os.environ.get(ipython_args, "").split()


def get_ipython(options, **extra_args):
    try:
        from IPython import start_ipython

        def launch_ipython():
            imported_objects = get_launch_args(**options)
            ipython_arguments = extra_args or get_ipython_arguments()
            start_ipython(argv=ipython_arguments, user_ns=imported_objects)

        return launch_ipython
    except ImportError:
        # IPython < 0.11
        # Explicitly pass an empty list as arguments, because otherwise
        # IPython would use sys.argv from this script.
        # Notebook not supported for IPython < 0.11.
        from IPython.Shell import IPShell

        def launch_ipython():
            imported_objects = get_launch_args(**options)
            shell = IPShell(argv=[], user_ns=imported_objects)
            shell.mainloop()

        return launch_ipython


def get_ptpython(options, vi_mode=False):
    try:
        from ptpython.repl import embed, run_config
    except ImportError:
        from prompt_toolkit.contrib.repl import embed, run_config

    def launch_ptpython():
        imported_objects = get_launch_args(**options)
        history_filename = os.path.expanduser("~/.ptpython_history")
        embed(
            globals=imported_objects,
            history_filename=history_filename,
            vi_mode=vi_mode,
            configure=run_config,
        )

    return launch_ptpython


def get_ptipython(options, vi_mode=False):
    """Based on django-extensions

    Run renamed to launch, get_imported_objects renamed to get_launch_args
    """
    try:
        from ptpython.ipython import embed
        from ptpython.repl import run_config
    except ImportError:
        # prompt_toolkit < v0.27
        from prompt_toolkit.contrib.ipython import embed
        from prompt_toolkit.contrib.repl import run_config

    def launch_ptipython():
        imported_objects = get_launch_args(**options)
        history_filename = os.path.expanduser("~/.ptpython_history")
        embed(
            user_ns=imported_objects,
            history_filename=history_filename,
            vi_mode=vi_mode,
            configure=run_config,
        )

    return launch_ptipython


def get_launch_args(**kwargs):
    import libtmux

    return {
        "libtmux": libtmux,
        "Server": libtmux.Server,
        "Session": libtmux.Session,
        "Window": libtmux.Window,
        "Pane": libtmux.Pane,
        "server": kwargs.get("server"),
        "session": kwargs.get("session"),
        "window": kwargs.get("window"),
        "pane": kwargs.get("pane"),
    }


def get_code(use_pythonrc, imported_objects):
    import code

    try:
        # Try activating rlcompleter, because it's handy.
        import readline
    except ImportError:
        pass
    else:
        # We don't have to wrap the following import in a 'try', because
        # we already know 'readline' was imported successfully.
        import rlcompleter

        readline.set_completer(rlcompleter.Completer(imported_objects).complete)
        # Enable tab completion on systems using libedit (e.g. macOS).
        # These lines are copied from Lib/site.py on Python 3.4.
        readline_doc = getattr(readline, "__doc__", "")
        if readline_doc is not None and "libedit" in readline_doc:
            readline.parse_and_bind("bind ^I rl_complete")
        else:
            readline.parse_and_bind("tab:complete")

    # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system
    # conventions and get $PYTHONSTARTUP first then .pythonrc.py.
    if use_pythonrc:
        for pythonrc in {
            os.environ.get("PYTHONSTARTUP"),
            os.path.expanduser("~/.pythonrc.py"),
        }:
            if not pythonrc:
                continue
            if not os.path.isfile(pythonrc):
                continue
            with open(pythonrc) as handle:
                pythonrc_code = handle.read()
            # Match the behavior of the cpython shell where an error in
            # PYTHONSTARTUP prints an exception and continues.
            exec(compile(pythonrc_code, pythonrc, "exec"), imported_objects)

    def launch_code():
        code.interact(local=imported_objects)

    return launch_code


def launch(shell="best", use_pythonrc=False, use_vi_mode=False, **kwargs):
    # Also allowing passing shell='code' to force using code.interact
    imported_objects = get_launch_args(**kwargs)

    if shell == "best":
        shell = detect_best_shell()

    if shell == "ptipython":
        launch = get_ptipython(options=kwargs, vi_mode=use_vi_mode)
    elif shell == "ptpython":
        launch = get_ptpython(options=kwargs, vi_mode=use_vi_mode)
    elif shell == "ipython":
        launch = get_ipython(options=kwargs)
    elif shell == "bpython":
        launch = get_bpython(options=kwargs)
    else:
        launch = get_code(use_pythonrc=use_pythonrc, imported_objects=imported_objects)

    launch()
