#! /usr/bin/env python

"""Python shell for SymPy.

   This is just a normal Python shell  (ipython shell if you have the
   IPython package installed) that executes the following commands so
   that you don't have to:

       >>> from __future__ import division
       >>> from sympy import *
       >>> x, y, z = symbols("xyz")
       >>> k, m, n = symbols("kmn", integer=True)
       >>> f = Function('f')

   So starting 'isympy' is equivalent to starting Python (or IPython)
   and executing the above commands by hand.  It is intended for easy
   and quick experimentation with SymPy.

   COMMAND LINE OPTIONS
   --------------------

   -c CONSOLE, --console=CONSOLE

     Use the specified python or ipython shell (lower case) as console
     backend instead of the default one  (ipython if present or python
     otherwise), eg. isympy -c python

   -q, --quiet

     Print only Python's and SymPy's versions to stdout at startup.

   -- IPython's options

     Additionally  you can pass command line options directly to IPython
     interpreter (standard Python shell is not supported).  However  you
     need to add '--' separator between two types of options, eg. to run
     SymPy without startup banner and colors, issue:

        isympy -q -- -colors NoColor

"""

import os, sys

# common SymPy's code for IPython and Python shells

div_code = "from __future__ import division"
sym_code = "from sympy.interactive import *"

# hook in-tree SymPy into Python path, if possible

isympy_dir = os.path.dirname(__file__)         # bin/isympy
sympy_top  = os.path.split(isympy_dir)[0]      # ../
sympy_dir  = os.path.join(sympy_top, 'sympy')  # ../sympy/

if os.path.isdir(sympy_dir):
    sys.path.insert(0, sympy_top)

# some longer messages

long_message = """\
These commands were executed:
>>> from __future__ import division
>>> from sympy import *
>>> x, y, z = symbols('xyz')
>>> k, m, n = symbols('kmn', integer=True)
>>> f = Function("f")

Documentation can be found at http://sympy.org/
"""

no_ipython = """\
Couldn't locate IPython. Having IPython installed is greatly recommended.
See http://ipython.scipy.org for more details.  If you use Debian/Ubuntu,
just install the 'ipython' package and start isympy again.
"""

def init_ipython(*args, **kwargs):
    import IPython.Shell

    ip = IPython.Shell.make_IPython(kwargs.get('argv'))

    from sympy import pretty

    def result_display(self, arg):
        """Pretty-printer display hook.

           Called for displaying pretty results to the user. Using this
           handler not only SymPy's  expression can be printed but also
           Python's lists, tuples and dictionaries.

           This function was adapted from:

             ipython/IPython/hooks.py:155

        """
        if self.rc.pprint:
            out = pretty(arg)

            if '\n' in out:
                print

            print out
        else:
            print repr(arg)

    ip.set_hook('result_display', result_display)

    ip.runcode(ip.compile(div_code, '<input>', 'single'))
    ip.runcode(ip.compile(sym_code, '<input>', 'single'))

    ip.interact(kwargs.get('message'))
    sys.exit('Exiting ...')

def init_python(*args, **kwargs):
    import code

    class HistoryConsole(code.InteractiveConsole):
        def __init__(self, *args, **kwargs):
            code.InteractiveConsole.__init__(self)

            history = kwargs.get('history', '~/.sympy-history')
            history = os.path.expanduser(history)

            try:
                import readline, atexit

                readline.parse_and_bind('tab: complete')

                if hasattr(readline, 'read_history_file'):
                    try:
                        readline.read_history_file(history)
                    except IOError:
                        pass

                    def save_history(history):
                        readline.write_history_file(history)

                    atexit.register(save_history, history)
            except ImportError:
                pass

    from sympy import pprint
    sys.displayhook = pprint

    console = HistoryConsole(*args, **kwargs)

    console.runcode(console.compile(div_code, '<input>', 'single'))
    console.runcode(console.compile(sym_code, '<input>', 'single'))

    console.interact(kwargs.get('message'))
    sys.exit('Exiting ...')

def main():
    from sympy import __version__ as sympy_version
    py_version = "%d.%d.%d" % sys.version_info[:3]

    usage = 'usage: isympy [options] -- [ipython options]'

    key_options = ('dest', 'action', 'default', 'help')

    isympy_options = (
        # parametric options
        ('-c', '--console', 'console', 'store', None,
            'select an interactive console: python | ipython'),
        # boolean options
        ('-q', '--quiet', 'quiet', 'store_true', False,
            'print only version information at startup'),
    )

    from optparse import OptionParser

    parser = OptionParser(usage, version=sympy_version)

    for option in isympy_options:
        parser.add_option(*option[0:2],
            **dict(zip(key_options, option[2:])))

    (options, args) = parser.parse_args()

    message = "Python %s console for SymPy %s" \
        % (py_version, sympy_version)

    if os.getenv('SYMPY_USE_CACHE') == 'no':
        message += ' (cache: off)\n'
    else:
        message += '\n'

    if not options.quiet:
        message += '\n' + long_message

    args = {
        'message' : message,
        'argv'    : args,
    }

    shells = {
        'ipython' : init_ipython,
        'python'  : init_python
    }

    try:
        shells[options.console](**args)
    except KeyError:
        try:
            init_ipython(**args)
        except ImportError:
            print no_ipython
            init_python(**args)

if __name__ == "__main__":
    main()
