#!/usr/bin/env python

"""Compile a MyriaL program into a physical plan."""
import argparse
import os
import json
import sys

from raco.catalog import FromFileCatalog
import raco.myrial.interpreter as interpreter
import raco.myrial.parser as parser
from raco.fakedb import FakeDatabase
from raco import algebra
from raco.viz import operator_to_dot
from raco.myrial.exceptions import *


def print_pretty_plan(plan, indent=0):
    if isinstance(plan, algebra.DoWhile):
        children = plan.children()
        body = children[:-1]
        term = children[-1]

        spc = ' ' * indent
        print '%sDO' % spc
        for op in body:
            print_pretty_plan(op, indent + 4)
        print '%sWHILE' % spc
        print_pretty_plan(term, indent + 4)
    elif isinstance(plan, algebra.Sequence):
        print '%s%s' % (' ' * indent, plan.shortStr())
        for child in plan.children():
            print_pretty_plan(child, indent + 4)
    else:
        print '%s%s' % (' ' * indent, plan)


def parse_options(args):
    arg_parser = argparse.ArgumentParser()
    group = arg_parser.add_mutually_exclusive_group()
    group.add_argument('-p', dest='parse',
                       help="Generate AST (parse tree)", action='store_true')
    group.add_argument('-l', dest='logical',
                       help="Generate logical plan", action='store_true')
    group.add_argument('-d', dest='dot',
                       help="Generate dot output for logical plan",
                       action='store_true')
    group.add_argument('-j', dest='json',
                       help="Encode plan as JSON", action='store_true')
    group.add_argument('-r', dest='repr',
                       help="Encode plan as Python repr", action='store_true')
    group.add_argument('-f', dest='standalone', action='store_true',
                       help='Execute the program in standalone mode')
    arg_parser.add_argument('file',
                            help='File containing MyriaL source program')

    ns = arg_parser.parse_args(args)
    return ns


def main(args):
    opt = parse_options(args)

    # Search for a catalog definition file
    catalog_path = os.path.join(os.path.dirname(opt.file), 'catalog.py')
    if os.path.exists(catalog_path):
        catalog = FromFileCatalog.load_from_file(catalog_path)
    else:
        catalog = FromFileCatalog({})

    _parser = parser.Parser()
    processor = interpreter.StatementProcessor(catalog, True)

    with open(opt.file) as fh:
        try:
            statement_list = _parser.parse(fh.read())
        except MyrialCompileException as ex:
            print 'MyriaL parse error: %s' % ex
            return 1

        if opt.parse:
            print statement_list
        else:
            processor.evaluate(statement_list)
            if opt.logical:
                print_pretty_plan(processor.get_logical_plan())
            elif opt.dot:
                print operator_to_dot(processor.get_logical_plan())
            elif opt.json:
                print json.dumps(processor.get_json())
            elif opt.standalone:
                pp = processor.get_physical_plan()
                db = FakeDatabase()
                db.evaluate(pp)
            elif opt.repr:
                pp = processor.get_physical_plan()
                print repr(pp)
            else:
                print_pretty_plan(processor.get_physical_plan())

    return 0

if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))
