#!/usr/bin/python2
# Copyright (c) 2012-2013, Itzik Kotler
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#
#     * Neither the name of the author nor the names of its contributors may
#       be used to endorse or promote products derived from this software without
#       specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import sys
import readline
import code
import codeop
import argparse
import logging
import os
import atexit
import runpy
import multiprocessing


try:

    import _preamble

except ImportError:

    sys.exc_clear()


import pythonect


# Pythonect Console

class PythonectCompile(codeop.Compile):

    def __init__(self):

        codeop.Compile.__init__(self)

    def __call__(self, source, filename, symbol):

        if source[-1] == '\\':
            return None

        return source.replace('\\\n', '')


class PythonectCommandCompiler(codeop.CommandCompiler):

    def __init__(self):

        codeop.CommandCompiler.__init__(self)

        self.compiler = PythonectCompile()


class PythonectInteractiveConsole(code.InteractiveConsole):

    def __init__(self, locals=None, histfile=os.path.expanduser("~/.pythonect_history")):

        code.InteractiveConsole.__init__(self, locals)

        self.compile = PythonectCommandCompiler()

        self.init_history(histfile)

    def init_history(self, histfile):

        try:

            readline.read_history_file(histfile)

        except IOError:

            pass

        atexit.register(self.save_history, histfile)

    def save_history(self, histfile):

        readline.write_history_file(histfile)

    # This is a cut & paste from /usr/lib/python2.7/code.py
    # Except we're not calling `exec` statement

    def runcode(self, code_):

        try:

            return_value = pythonect.eval(code_, {}, self.locals)

            # Meaningful Return Value?

            if return_value is not None:

                # String?

                if isinstance(return_value, basestring):

                    # Enclose in single quotes

                    return_value = "'" + return_value + "'"

                self.write(str(return_value) + '\n')

                # Keep return_value for further reference or reset to None?

                if return_value is False or return_value is True:

                    # Reset locals to None

                    self.locals['_'] = None

        except SystemExit:

            raise

        except:

            self.showtraceback()

        else:

            if code.softspace(sys.stdout, 0):

                print


def main():

    locals = {}

    verbose_levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]

    # Pythonect's Banner (for -V, --version purposes)

    banner = "Pythonect %s" % pythonect.__version__

    # Parse command-line arguments

    parser = argparse.ArgumentParser(sys.argv)

    parser.add_argument('script', metavar='file', nargs='?', type=argparse.FileType('rt'), help='program read from script file')
    parser.add_argument('arg', metavar='arg', nargs='*', help='arguments passed to program in sys.argv[1:]')
    parser.add_argument('--verbose', '-v', action='count', default=0)
    parser.add_argument('--version', '-V', action='version', version=banner)
    parser.add_argument('--interactive', '-i', action='store_true', default=False, help='inspect interactively after running script')
    parser.add_argument('--command', '-c', metavar='cmd', nargs='*', help='program passed in as string')
    parser.add_argument('--module', '-m', metavar='mod', action='store', help='run library module as a script')
    parser.add_argument('--max-threads-per-flow', '-mt', metavar='N', default=multiprocessing.cpu_count() + 1, type=int, action='store', help='max threads per flow')

    args = parser.parse_args()

    # Setup logging level

    logging.basicConfig(level=verbose_levels[args.verbose % 4], format="%(filename)10s:%(lineno)4d:%(message)s")

    # Adjust sys.argv, make sys.argv[1] as sys.argv[0], sys.argv[2] as sys.argv[1] and etc.

    sys.argv = sys.argv[1:]

    if not sys.argv:

        sys.argv = ['']

    # Module as script mode (i.e. python -m 'os')

    if args.module:

        locals = runpy.run_module(args.module)

    # Max threads per pipe

    locals.update(__MAX_THREADS_PER_FLOW__=args.max_threads_per_flow)

    # Command line mode (i.e. ./pythonect -c "'Hello world' -> print")

    if args.command:

        pythonect.eval(args.command[0], globals(), locals)

    # Script-mode (i.e. ./pythonect script or #!/usr/bin/env pythonect)

    if args.script:

        pythonect.eval(args.script.read().replace('\\\n', ''), globals_, locals_)

        args.script.close()

    if not args.module and not args.command and not args.script:

        args.interactive = True

    else:

        banner = ''

    # Interactive-mode (i.e. ./pythonect)

    if args.interactive:

        # Add current working directory to sys.path

        sys.path.insert(0, os.getcwd())

        # Pythonect's Banner (for -V, --version purposes)

        banner = """Python %s\n[Pythonect %s] on %s\nType "help", "copyright", "credits" or "license" for more information.""" % (sys.version.split('\n')[0], pythonect.__version__, sys.platform)

        PythonectInteractiveConsole(locals).interact(banner)

    return 0


# Entry Point

if __name__ == "__main__":

    try:

        sys.exit(main())

    except ValueError as e:

        pass
