from visitor import Visitor
from compiler import main as do_compile
import os

def empty(s):
    return None

class ModelVisitor(Visitor):
    def __init__(self, args):
        Visitor.__init__(self, args)
        self.constructors = []
        self.free_id = 0
        self.name_maps = {}
        self.current_model = None
        self.current_element = None
        self.includes = []

    def dump(self):
        return self.constructors

    def __getattr__(self, attr):
        if attr.startswith("visit_"):
            return empty
        else:
            raise AttributeError()

    def visit_start(self, tree):
        for t in tree.get_tail():
            self.visit(t)

    def visit_include_files(self, tree):
        self.includes.append(tree.get_children("STRVALUE")[0].get_text())

    def visit_import(self, tree):
        url = tree.get_children("MV_URL")[0]
        target = tree.get_children("MODEL_ID")[0]
        self.constructors.extend(["import_node", url.get_text(), target.get_text()])

    def visit_export(self, tree):
        url = tree.get_children("MV_URL")[0]
        target = tree.get_children("MODEL_ID")[0]
        self.constructors.extend(["export_node", target.get_text(), url.get_text()])

    def visit_model(self, tree):
        children = tree.get_children("MODEL_ID")
        model_type = children[0].get_text()
        model_name = children[1].get_text()
        self.constructors.extend(["instantiate_model", model_type, model_name])
        self.constructors.extend(["define_inheritance", model_name, "Inheritance"])
        self.current_model = model_name
        for element in tree.get_children("model_element"):
            self.visit(element)

    def visit_model_element(self, tree):
        children = tree.get_children("MODEL_ID")
        element_type = children[0].get_text()
        if len(children) == 2 or len(children) == 4:
            element_name = children[1].get_text()
        else:
            element_name = "__%s" % self.free_id
            self.free_id += 1

        if len(children) > 2:
            # So we have a source and target; but aren't sure which is which, because the name is optional!
            source_name = children[-2].get_text()
            target_name = children[-1].get_text()
            self.constructors.extend(["instantiate_link", self.current_model, element_type, element_name, source_name, target_name])
        else:
            self.constructors.extend(["instantiate_node", self.current_model, element_type, element_name])
        self.current_element = element_name

        if tree.get_children("inheritance"):
            self.visit(tree.get_children("inheritance")[0])
            
        for attr in tree.get_children("model_attribute"):
            self.visit(attr)

    def visit_inheritance(self, tree):
        for token in tree.get_children("MODEL_ID"):
            superclass = token.get_text()
            self.constructors.extend(["instantiate_link", self.current_model, "Inheritance", "%s_inherits_from_%s" % (self.current_element, superclass), self.current_element, superclass])

    def visit_model_attribute(self, tree):
        children = tree.get_children("MODEL_ID")
        is_definition = bool(tree.get_children("COLON"))
        is_constraint = bool(tree.get_children("DOLLAR"))
        is_assign = bool(tree.get_children("model_attr_instance"))

        if is_definition:
            attr_name = children[0].get_text()
            attr_type = children[1].get_text()
            self.constructors.extend(["instantiate_link", self.current_model, "Association", self.current_element + "_" + attr_name, self.current_element, attr_type])
            full_attribute_name = self.current_element + "_" + attr_name
            self.constructors.extend(["instantiate_attribute", self.current_model, full_attribute_name, "name", attr_name])
            if is_assign:
                # There are also some attributes to set!
                old_element = self.current_element
                self.current_element = full_attribute_name
                for f in tree.get_children("model_attr_instance"):
                    self.visit(f)
                self.current_element = old_element
        elif is_assign:
            self.visit(tree.get_children("model_attr_instance")[0])
        elif is_constraint:
            constraint = tree.get_children("ANYTHING_EXCEPT_DOLLAR")[0].get_text()
            whitespaces = len(constraint) - len(constraint.lstrip())
            constraint = "\n".join(["\t" + line[whitespaces-1:].replace("    ", "\t") for line in constraint.split("\n") if len(line.strip()) != 0])
            constraint = "".join(["include %s\n" % i for i in self.includes]) + \
                         "String function constraint(model : Element, name : String):\n" + \
                         "\tElement self\n" + \
                         '\tself = model["model"][name]\n' + \
                         constraint + "\n"
            with open(".constraint.alc", 'w') as f:
                f.write(constraint)
                f.flush()
            directory = os.path.realpath(__file__).rsplit(os.sep, 1)[0]
            compiled = do_compile(".constraint.alc", directory + "/../grammars/actionlanguage.g", "CS")
            self.constructors.extend(["add_constraint", self.current_model, self.current_element] + compiled)

    def visit_model_attr_instance(self, tree):
        children = tree.get_children("MODEL_ID")
        attr_name = children[0].get_text()
        attr_value = tree.get_children("value")[0].get_tail()[0]
        if attr_value.head == "STRVALUE":
            attr_value = attr_value.get_text()[1:-1]
        elif attr_value.head == "TRUE":
            attr_value = True
        elif attr_value.head == "FALSE":
            attr_value = False
        elif attr_value.head == "DEC_NUMBER":
            attr_value = int(attr_value.get_text())
        elif attr_value.head == "FLOAT_NUMBER":
            attr_value = float(attr_value.get_text())
        else:
            raise Exception(attr_value.head)
        self.constructors.extend(["instantiate_attribute", self.current_model, self.current_element, attr_name, attr_value])
