###########################################################################################
# Asteroid interpreter
#
# (c) University of Rhode Island
###########################################################################################

import os
import sys

from asteroid.globals import *
from asteroid.support import *
from asteroid.frontend import Parser
from asteroid.state import state, dump_trace
from asteroid.walk import walk

# the prologue file is expected to be in the 'modules' folder
prologue_name = 'prologue.ast'

# TODO: adjust the defaults
def interp(input_stream,
           input_name = "<input>",
           tree_dump=False,
           do_walk=True,
           symtab_dump=False,
           exceptions=False,
           redundancy=True,
           prologue=True,
           initialize_state = True):
    try:
        # initialize state
        if initialize_state:
            state.initialize(input_name)

        #lhh
        #print("path[0]: {}".format(sys.path[0]))
        #print("path[1]: {}".format(sys.path[1]))

        # initialize "check for useless clauses" flag
        state.eval_redundancy = redundancy

        # read in prologue
        if prologue:
            prologue_file = ''
            prologue_file_base = os.path.join('modules', prologue_name)
            module_path = os.path.join(os.path.split(os.path.abspath(__file__))[0], prologue_file_base)
            working_path = os.path.join(os.getcwd(), prologue_file_base)

            if os.path.isfile(module_path):
                prologue_file = module_path
            elif os.path.isfile(working_path):
                prologue_file = working_path
            else:
                raise ValueError("Asteroid prologue '{}' not found"
                                .format(prologue_file_base))

            with open(prologue_file) as f:
                state.modules.append(prologue_name)
                data = f.read()
                pparser = Parser(prologue_name)
                (LIST, pstmts) = pparser.parse(data)

        # build the AST
        parser = Parser(input_name)
        (LIST, istmts) = parser.parse(input_stream)
        if prologue:
            state.AST = ('list', pstmts + istmts)
        else:
            state.AST = ('list', istmts)

        # walk the AST
        if tree_dump:
            dump_AST(state.AST)
        if do_walk:
            walk(state.AST)
        if symtab_dump:
            state.symbol_table.dump()

    # Note: all exceptions generated by Asteroid should go through the handler
    #       for 'Exception' unless the exception needs special handling like
    #       'ThrowValue' or 'ReturnValue' etc.
    except ThrowValue as throw_val:
        dump_trace()
        # handle exceptions using the standard Error constructor
        module, lineno = state.lineinfo
        if throw_val.value[0] == 'apply' and throw_val.value[1][1] == 'Error':
            (APPLY, (ID, id), (APPLY, error_obj, rest)) = throw_val.value
            print("error: {}: {}: {}".format(module, lineno, term2string(error_obj)))
        else:
            print("error: {}: {}: unhandled Asteroid exception: {}"
                  .format(module, lineno, term2string(throw_val.value)))
        # needed for REPL
        if not exceptions:
            sys.exit(1)

    except ReturnValue as inst:
        dump_trace()
        module, lineno = state.lineinfo
        print("error: {}: {}: return statement used outside a function environment"
              .format(module, lineno))
        # needed for REPL
        if not exceptions:
            sys.exit(1)

    except  KeyboardInterrupt as e:
        dump_trace()
        print("error: keyboard interrupt")
        # needed for REPL
        if not exceptions:
            sys.exit(1)

    except Exception as e:
        if exceptions: # rethrow the exception so that you can see the full backtrace
            if symtab_dump:
                state.symbol_table.dump()
            raise e
        else:
            dump_trace()
            module, lineno = state.lineinfo
            print("error: {}: {}: {}".format(module, lineno, e))
            # needed for REPL
            if not exceptions:
                sys.exit(1)

    except BaseException as e:
        dump_trace()
        print("error: {}".format(e))
        # needed for REPL
        if not exceptions:
            sys.exit(1)
