#!/usr/bin/python
#
# Copyright 2015 Michael Sparks
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from pyxie.parsing.lexer import build_lexer
from pyxie.parsing.grammar import parse
from pyxie.model.transform import ast_to_cst
from pyxie.model.pynode import jdump
from pyxie.codegen.simple_cpp import C_Program, source, makefile_tmpl, reset_parser
import pyxie.codegen.simple_cpp

import os
import pprint
import time

import sys

testdir = "test-data"
testprogs_dir = os.path.join(testdir, "progs")

def get_test_programs(program_suffix):
    testprogs = [x for x in os.listdir(testprogs_dir) ]
    testprogs = [x for x in testprogs if x.endswith(program_suffix) ]
    return testprogs

def parse_file(somefile):
    lexer = build_lexer()
    reset_parser()
    data = open(somefile).read()
    AST = parse(data, lexer)
    return AST

def parse_testfile(testprogs_dir, testfile, debug=False):
    print "_______________________________________________________________"
    print "PARSING", os.path.join(testprogs_dir,testfile)
    print
    AST = parse_file(os.path.join(testprogs_dir,testfile))
    if debug:
        pprint.pprint(AST)
        JAST = jdump(AST)
        pprint.pprint(JAST)
    return AST


def generate_code(cname, AST, debug=False):
    CST = ast_to_cst(cname, AST)
    if debug:
        print "CST:", pprint.pformat(CST)
    program = C_Program.fromjson(CST)
    program.generate()
    return pyxie.codegen.simple_cpp.source[:]

def compile_testfile(testprogs_dir, testfile):
    # The compiled c name is the filename with the suffix removed
    p = testfile.rfind(".")
    cname = testfile[:p]

    AST = parse_testfile(testprogs_dir, testfile, debug=True)

    print "_______________________________________________________________"
    print "COMPILING", os.path.join(testprogs_dir,testfile)
    print

    c_code = generate_code(cname, AST, debug=True)


    # Create Work directory
    allresults_dir = os.path.join(testdir, "genprogs")
    thisresult_dir = os.path.join(allresults_dir, testfile)

    try:
        os.mkdir(allresults_dir)
    except OSError as e:
        pass # Can raise an exception if the directory exists, which is fine

    try:
        os.mkdir(thisresult_dir)
    except OSError as e:
       if e.errno != 17: # Directory exists
           raise


    build_program(c_code, thisresult_dir, cname)

def build_program(source, work_dir, name):
    print "_______________________________________________________________"
    print "BUILDING", work_dir
    print
    print "Program to build:"
    pprint.pprint(source, width=200)
    print

    f = open(os.path.join(work_dir,name+".c"), "w")
    for line in source:
        f.write(line)
        f.write("\n")
    f.close()

    makefile = makefile_tmpl % {"filename": name }
    f = open(os.path.join(work_dir,"Makefile"), "w")
    f.write(makefile)
    f.close()

    os.chdir(work_dir)
    os.system("make")
    print
    print "Done!"
    print

def parsing_tests():
    # Run parsing tests. These are in the "progs" test directory, and are in
    # filenames ending ".p"

    rootdir = os.getcwd()
    testprogs = get_test_programs(".p")

    for testfile in testprogs:
        AST = parse_testfile(testprogs_dir, testfile)
        pprint.pprint(AST)

        JAST = jdump(AST)
        pprint.pprint(JAST)

        os.chdir(rootdir)

def compilation_tests():
    # ast_to_cst
    # Compilation Tests
    rootdir = os.getcwd()
    testprogs = get_test_programs(".pyxie")

    for testfile in testprogs:
        compile_testfile(testprogs_dir, testfile)
        os.chdir(rootdir)

    print "COMPILING DONE", testprogs

def show_help():
    print """\npyxie -- A little python compiler\nUsage:\n
    pyxie -- show runtime arguments
    pyxie --test run-tests -- Run all tests
    pyxie --test parse-tests -- Just run parse tests
    pyxie --test compile-tests -- Just run compile tests
    pyxie --test parse filename -- Parses a given test given a certain filename
    pyxie parse filename -- parses the given filename, outputs result to console
    pyxie compile path/to/filename.suffix -- compiles the given file to path/to/filename
    pyxie compile path/to/filename.suffix  path/to/other/filename -- compiles the given file to the destination filename
"""

def main():
    if len(sys.argv) > 1:
        if sys.argv[1] == "--test":
            if sys.argv[2] == "run-tests":
                parsing_tests()
                compilation_tests()
                return

            if sys.argv[2] == "parse-tests":
                parsing_tests()
                return

            if sys.argv[2] == "compile-tests":
                compilation_tests()
                return

            if sys.argv[2] == "parse" and len(sys.argv) == 4:
                filename = sys.argv[3]
                AST = parse_testfile(testprogs_dir, filename)
                pprint.pprint(AST)
                pprint.pprint(AST.__json__())
                return

            if sys.argv[2] == "compile" and len(sys.argv) == 4:
                filename = sys.argv[3]
                compile_testfile(testprogs_dir, filename)
                return

        if sys.argv[1] == "parse" and len(sys.argv) == 3:
            filename = sys.argv[2]
            AST = parse_file(filename)
            pprint.pprint(AST)
            pprint.pprint(AST.__json__())
            return

        if sys.argv[1] == "compile" and len(sys.argv) == 3 or len(sys.argv) == 4:

            rootdir = os.getcwd()
            filename = sys.argv[2]
            base_dir = filename[:filename.rfind("/")]
            base_filename = filename[filename.rfind("/")+1:]
            cname = base_filename[:base_filename.rfind(".")]

            if len(sys.argv) == 4:
                result_filename = sys.argv[3]
            else:
                result_filename = os.path.join(base_dir, cname)

            print "COMPILING", filename
            print "IN", base_dir
            print "SOURCEFILE", base_filename
            print "cname", cname
            print "result_filename", result_filename

            AST = parse_file(filename)
            c_code = generate_code(cname, AST, debug=True)

            build_dir = os.path.join(base_dir, "build-"+str(int(time.time())))
            try:
                os.mkdir(build_dir)
            except OSError as e:
               if e.errno != 17: # Directory exists
                   raise

            build_program(c_code, build_dir, cname)

            os.chdir(rootdir)

            print "BUILD DIR", build_dir
            print "RESULT FILE", os.path.join(build_dir, cname)
            print "DEST FILE", result_filename
            print "CWD", os.getcwd()

            os.rename(os.path.join(build_dir, cname),result_filename)
            clean = True
            if clean:
                for filename in os.listdir(build_dir):
                    os.unlink(os.path.join(build_dir,filename))
                os.removedirs(build_dir)
            return

    show_help()


if __name__ == "__main__":
    main()

