"""Global yap_ipython app to support test running.

We must start our own ipython object and heavily muck with it so that all the
modifications yap_ipython makes to system behavior don't send the doctest machinery
into a fit.  This code should be considered a gross hack, but it gets the job
done.
"""

# Copyright (c) yap_ipython Development Team.
# Distributed under the terms of the Modified BSD License.

import builtins as builtin_mod
import sys
import types
import warnings

from . import tools

from yap_ipython.core import page
from yap_ipython.utils import io
from yap_ipython.terminal.interactiveshell import TerminalInteractiveShell


class StreamProxy(io.IOStream):
    """Proxy for sys.stdout/err.  This will request the stream *at call time*
    allowing for nose's Capture plugin's redirection of sys.stdout/err.

    Parameters
    ----------
    name : str
        The name of the stream. This will be requested anew at every call
    """

    def __init__(self, name):
        warnings.warn("StreamProxy is deprecated and unused as of yap_ipython 5", DeprecationWarning,
            stacklevel=2,
        )
        self.name=name

    @property
    def stream(self):
        return getattr(sys, self.name)

    def flush(self):
        self.stream.flush()


def get_ipython():
    # This will get replaced by the real thing once we start yap_ipython below
    return start_ipython()


# A couple of methods to override those in the running yap_ipython to interact
# better with doctest (doctest captures on raw stdout, so we need to direct
# various types of output there otherwise it will miss them).

def xsys(self, cmd):
    """Replace the default system call with a capturing one for doctest.
    """
    # We use getoutput, but we need to strip it because pexpect captures
    # the trailing newline differently from commands.getoutput
    print(self.getoutput(cmd, split=False, depth=1).rstrip(), end='', file=sys.stdout)
    sys.stdout.flush()


def _showtraceback(self, etype, evalue, stb):
    """Print the traceback purely on stdout for doctest to capture it.
    """
    print(self.InteractiveTB.stb2text(stb), file=sys.stdout)


def start_ipython():
    """Start a global yap_ipython shell, which we need for yap_ipython-specific syntax.
    """
    global get_ipython

    # This function should only ever run once!
    if hasattr(start_ipython, 'already_called'):
        return
    start_ipython.already_called = True

    # Store certain global objects that yap_ipython modifies
    _displayhook = sys.displayhook
    _excepthook = sys.excepthook
    _main = sys.modules.get('__main__')

    # Create custom argv and namespaces for our yap_ipython to be test-friendly
    config = tools.default_config()
    config.TerminalInteractiveShell.simple_prompt = True

    # Create and initialize our test-friendly yap_ipython instance.
    shell = TerminalInteractiveShell.instance(config=config,
                                              )

    # A few more tweaks needed for playing nicely with doctests...

    # remove history file
    shell.tempfiles.append(config.HistoryManager.hist_file)

    # These traps are normally only active for interactive use, set them
    # permanently since we'll be mocking interactive sessions.
    shell.builtin_trap.activate()

    # Modify the yap_ipython system call with one that uses getoutput, so that we
    # can capture subcommands and print them to Python's stdout, otherwise the
    # doctest machinery would miss them.
    shell.system = types.MethodType(xsys, shell)

    shell._showtraceback = types.MethodType(_showtraceback, shell)

    # yap_ipython is ready, now clean up some global state...

    # Deactivate the various python system hooks added by ipython for
    # interactive convenience so we don't confuse the doctest system
    sys.modules['__main__'] = _main
    sys.displayhook = _displayhook
    sys.excepthook = _excepthook

    # So that ipython magics and aliases can be doctested (they work by making
    # a call into a global _ip object).  Also make the top-level get_ipython
    # now return this without recursively calling here again.
    _ip = shell
    get_ipython = _ip.get_ipython
    builtin_mod._ip = _ip
    builtin_mod.get_ipython = get_ipython

    # Override paging, so we don't require user interaction during the tests.
    def nopage(strng, start=0, screen_lines=0, pager_cmd=None):
        if isinstance(strng, dict):
           strng = strng.get('text/plain', '')
        print(strng)
    
    page.orig_page = page.pager_page
    page.pager_page = nopage

    return _ip