package IntermediaryCode;

import API.Api;
import API.Functions;
import API.Types;
import API.Utils;
import API.Variables;
import ast.AbstractSyntaxTree;
import ast.Node;
import common.Block;
import common.Code;
import common.DataLayout;
import common.Instruction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Pattern;

/**
 *
 * @author Eugenio
 */
public final class IRGenerator implements IRGenInterface {

    protected int jb = 0;
    protected int incLabel = 0;
    protected int tmpVarCount = 1;
    protected boolean inicializado = false;
    protected boolean enableOtimization = true;
    protected AbstractSyntaxTree ast;
    protected Code code;
    protected Node calEndAtt;
    protected String label;
    protected String calEndVar;
    protected String calEndSize;
    protected String calEndTipo;
    protected Pattern ArrayPatternRegex = Pattern.compile(".*\\[.*\\]");
    protected Stack<Node> master = new Stack<>();
    protected ArrayList<String> line = new ArrayList<>();
    protected LinkedList<Boolean> branckInvert = new LinkedList<>();
    protected HashMap<String, String> arrayAcesso = new HashMap<>();
    protected HashMap<String, String> varGenMap = new HashMap<>();
    protected HashMap<String, String> funcGenMap = new HashMap<>();
    protected HashMap<String, Integer> systemCalls = new HashMap<>();
    protected ArrayList<String> currentMatrixvalues = new ArrayList<>();
    protected HashMap<String, ArrayList<String>> matrixValues = new HashMap<>();

    protected HashMap<String, String> ArrayShiftMap = new HashMap<>();

    protected Stack<String> loopScope = new Stack<String>() {
        {
            push("");
        }
    };

    protected LinkedHashMap<String, Long> power2 = new LinkedHashMap<String, Long>() {
        {
            for (long i = 0; i < 31; i++) {
                put(Long.toString(Long.valueOf(1 << i)), Long.valueOf(i));
            }
        }
    };
    public static Pattern AddressOperator = Pattern.compile("(\\&|\\*)");

    protected String[] primitives = Types.getPrimitiveTypesList();

    public IRGenerator() throws Exception {

    }

    public IRGenerator(AbstractSyntaxTree ast) throws Exception {
        Ast(ast);
    }

    protected int calc(ArrayList<Integer> data) {
        Integer op1, op2, operacao;
        switch (data.size()) {

            case 1:
                return data.get(0);
            case 3:
                op1 = data.remove(0);
                operacao = data.remove(0);
                op2 = data.remove(0);
                if (operacao == 1) {
                    return op1 - op2;
                    //                throw new Exception("Quantidade de entradas invalidas!" + data.size());
                } else {
                    return op1 + op2;
                }
            default:
                op1 = data.remove(0);
                operacao = data.remove(0);
                if (operacao == 1) {
                    return op1 - calc(data);
                    //                throw new Exception("Quantidade de entradas invalidas!" + data.size());
                } else {
                    return op1 + calc(data);
                }
        }
    }

    public void Ast(AbstractSyntaxTree ast) throws Exception {

        this.ast = ast;
        code = new Code("IR", ast);
        //Registra o bloco global

        code.OpenBlock("global");
        Block block = code.Block();
        // Seta o bloco como bloco de controle
        block.Type(Block.TYPE_CTRL);
        // Atribui o contexto do bloco ao contexto global
        code.GlobalContext(block.Context());
        // Encerra o bloco
        code.CloseBlock();

        code.ControlInstructions = "label,alloc".split(",");
        // Especifica o template engine
        code.Template(new IRTemplate());

    }

    @Override
    public Code Create(AbstractSyntaxTree ast) throws Exception {
        Ast(ast);

        Code code = run();
        // Executa todos os tratametos definidos apos geracao do codigo
        printVarMap();

        code.Print();

        return code;
    }

    protected Node visit(Node n) throws Exception {
        if (null == n || n.eq("visited", "true")) {
            return n;
        }
        n.Set("visited", "true");
//        System.out.println("Visit:" + n.Class());
        switch (n.Class()) {
            case "dec.const":
                gerarConstante(n);
                break;
            case "if.stmt":
                IfStmts(n);
                break;
            case "selector":
                Selector(n);
                break;
            case "dec.var":
//                Log.PrintInfo("TAC", new Instruction().Set("msg", "DECVAR." + n));
                if (n.find("exprs") != null) {
                    n.Set("class", "dec.var.short").Set("value", "dec.var.short");
                    genAssign(n);
                    break;
                }
            case "def.assign":
                genAssign(n);
                break;
            case "call":
                genCall(n);
                break;
            case "ID":
                genID(n);
                break;
            case "incdec":
                IncDecStmt(n);
                break;
            case "for.stmt":
                genFor(n);
                break;
            case "expr":
                _visitExpr(n);
                break;
            case "unary":
                unaryStmt(n);
                break;
            case "switch.stmt":
                switchStmt(n);
                break;
            case "test":
                test(n);
                break;
            case "return":
                visitChildrensFirst(n);
                break;
            default:
                visitRootFirst(n);
                break;
        }
        return n;
    }

    protected void genID(Node n) throws Exception {
//        System.out.println("GENID:" + n);
        n.Set("_return", CreateVar(Variables.Get(n.getText())));

    }

    protected void test(Node n) throws Exception {
        Node x = null;

        ArrayList<Node> chils = n.childrens();
        int size = chils.size();
        for (int i = 0; i < size; i++) {
            x = chils.get(i);
            visit(x);
        }

        n.Copy("_return", x);
    }

    protected void IncDecStmt(Node n) throws Exception {
        Instruction operacao = new Instruction();
        String dst = CreateVar(n.findByClass("selector"));
        operacao.Set("type", "assign")
                .Set("format", "assign")
                .Set("cat", "exp")
                .Set("_return", "exp")
                .Set("dst", dst)
                .Set("p1", dst)
                .Set("p2", "1")
                .Set("p1value", "" + Api.IsValue(dst))
                .Set("p2value", "true")
                .Set("op", n.findByClass("operator").eq("value", "++") ? "+" : "-");
        Add(operacao);
    }

    protected void Selector(Node n) throws Exception {
        String tmp;
        String shift = null;
        n.Set("copymode", "copy");

        switch (n.Get("subclass")) {
            case "operand":
                if (n.eq("classType", "value")) {
//                    n.Set("value", Api.Value(n.getText()));
                    n.Set("_return", Api.Value(n.getText()));
                } else {

                    String v = CreateVar(n);

                    if (n.eq("pointer", "*") && n.eq("access", "read")) {
                        String t = CreateTmpVar();
                        Copy(t, v, false);
                        v = t;
                    }

                    n.Set("_return", v);
                }
//                System.out.println("Seletor operand:" + n);
                break;
            case "index":
//                shift = n.childrens().Get(0).GetInt("value");
//                shift = n.childrens().Get(0).GetInt("value");

                shift = ArrayShift(n);

            case "selector":
                if (shift == null) {
                    shift = "" + Variables.Shift(n.getText());
                }

                tmp = CreateVar(n) + "[" + shift + "]";

                if (!n.eq("access", "write")) {
                    String t = CreateTmpVar();
                    Copy(t, tmp, false);
                    tmp = t;
                }

                n.Set("_return", tmp);
                break;

            case "arguments":
                break;
        }
    }

    protected ArrayList<Integer> ShiftPath(int size) {
        ArrayList<Integer> ret = new ArrayList<>();
        FindShiftPath(size, ret);
        return ret;
    }

    protected ArrayList<Integer> ShiftInterval(int size) {
        ArrayList<Integer> ret = new ArrayList<>();
        Integer op = 1, i = 1, after, before;
        //
        while (true) {
            after = 1 << (i++);
            if (after == size) {
                before = after;
                break;
            } else if (after > size) {
                before = after >> 1;
                break;
            }
        }
//        before = Math.abs(before - size);
//        after = Math.abs(after - size);
        if (Math.abs(before - size) <= Math.abs(after - size)) {
            // Se o valor anterios for menor que o posterior
            op = 0;
        }
//        else {
//            // Se o valor posterior for menor que o anterior
//        }
        ret.add(before);
        ret.add(after);
        ret.add(2, op);
//        System.out.println("Interval:{" + size + "}" + ret + "::" + op);
        return ret;
    }

    protected void FindShiftPath(int size, ArrayList<Integer> path) {
        size = Math.abs(size);
        ArrayList<Integer> shift = ShiftInterval(size);

        int op = shift.get(2), s1 = shift.get(op);

//        System.out.println("FindShiftPath:(" + power2.containsKey("" + s1) + "|" + size + "|" + s1 + ")\t\t" + shift);
//        System.out.println(power2);
        if (power2.containsKey("" + s1)) {
            path.add(s1);
            path.add(op);
        }

        if (size == 1) {
//            path.add(size); 
        }

//        System.out.println("Pathj:" + path);
        if (path.size() % 2 == 0 && Math.abs(size - s1) == 0) {
            path.remove(path.size() - 1);
            return;
        }

        FindShiftPath(size - s1, path);

//        int i = 1, after, before;
//        while (true) {
//            after = 1 << (i++);
//            if (after == size) {
//
//                break;
//            } else if (after > size) {
//                break;
//            }
//        }
//        before = after >> 1;
//
//        if (Math.abs(size - before) < Math.abs(after - size)) {
//            // Se o valor anterios for menor que o posterior
//            op = 1;
//            m4 = before;
//        } else {
//            // Se o valor posterior for menor que o anterior
//            op = 0;
//            m4 = after;
//        }
//        FindShiftPath(size, m4, op, path);
    }

    protected String ArrayShift(Node n) throws Exception {
        // Cria a chave para a ocorrencia de um acesso ao array, evitando recalcular o endereco
        String key = n.getText();
        for (Node node : n.childrens()) {
            key += "_" + node.getText();
        }

        if (!ArrayShiftMap.containsKey(key)) {

//            String shift;
            Node var = Variables.Get(n.getText());
//            System.out.println("INDEX shift:" + n + var);

            for (int i = 0, j = 1; j < var.childrens().size(); j++) {
                n.childrens().get(i++).Set("collNumber", var.childrens().get(j).getText());
            }

            String ret = CalcArrayAddress(n.childrens(), Types.Size(var.Get("type")));
            // Se o offset for calculado em um inteiro atribui o mesmo como shift
//            if (Utils.IsNumber(ret)) {
//                shift = ret;
//            } else {
////                 Se for um endereci faz a soma com o offset da varivel
////                __exp("+", ret, CreateAlias(n), ret);
//                shift = ret;
//            }
            ArrayShiftMap.put(key, ret);
        }

        return ArrayShiftMap.get(key);
    }

    protected String GenShiftLeftLogical(String var, int size, boolean mps, boolean last) throws Exception {
        ArrayList<Integer> path = ShiftPath(size);

        ArrayList<Integer> reverse = new ArrayList<>();
        for (int i = path.size() - 1; i >= 0; i--) {
            reverse.add(path.remove(i));
        }
//        System.out.println(">>>>>(var:" + var + ", size:" + size + ") arithpath:" + reverse);

        var = GSLL(var, reverse, mps, size, last);

        return var;
    }

    protected long pow2(long p) {
        return pow2("" + p);
    }

    protected long pow2(String p) {
        return power2.get(p);
    }

    protected String GSLL(String var, ArrayList<Integer> path, boolean mps, int size, boolean last) throws Exception {
        int op1, operacao, op2;
        String T1 = CreateTmpVar(),
                T2 = CreateTmpVar(),
                T3 = CreateTmpVar();

        switch (path.size()) {
            case 1:
                if (!mps) {
                    Copy(T1, var, false)
                            .Set("comment", "Copy value of index")
                            .Set("copy", "true");
                } else {
                    T1 = var;
                }
                if (!last) {
//                    System.out.println("GSLL:" + size + ":" + pow2(size));
                    __exp("<<", T1, "" + pow2(size), T2);
                    return T2;
                }
                return T1;
            case 3:
                op1 = path.remove(0);
                operacao = path.remove(0);
                op2 = path.remove(0);

//                String T4 = CreateTmpVar();
                if (!mps) {
                    Copy(T1, var, false)
                            .Set("comment", "Copy value of index")
                            .Set("copy", "true");
                }

                if (op1 != 1) {
                    __exp("<<", T1, "" + pow2(op1), T1);
                }
//                if (op2 == 1) {
//                } else {
//                    Copy(T3, var, false).Set("comment", "Copy value of index");
                if (op2 != 1) {

                    __exp("<<", T1, "" + (pow2(op2) - pow2(op1)), T2);
                    // deve realizar um novo deslocamento
                } else {
                    T2 = T1;
                }
                // Soma o deslocamento com o tamanho
                __exp("+", T1, T2, T1).Set("comment", "");
                return T1;
            default:

//                op1 = path.remove(0);
//                operacao = path.remove(0);
//
//                T2 = GSLL(var, path);
//
//                Copy(T1, var, false).Set("comment", "Copy value of index");
//
//                __exp("<<", T1, "" + pow2(op1), T1).Set("comment", "");
//                __exp("+", T1, T2, T1).Set("comment", "");
        }
        return var;
    }

    protected String CalcArrayAddress(ArrayList<Node> childrens, int Size) throws Exception {
//        System.out.println("CalcArrayAddressINIT(" + Size + "):");
        boolean onlyNumber = true;

        for (Node node : childrens) {
            if (!node.isNumber("value")) {
                onlyNumber = false;
                break;
            }
        }

        if (onlyNumber) {
            return "" + (CalcArrayAddressNumber(childrens, Size) * Size);

        } else {
//            System.out.println("CAA:" + childrens);

            String current = CalcArrayAddressAux(childrens, Size);

//            System.out.println("Size {" + current + "}:----------->" + Size);
// multiplica pelo tamanho
            return GenShiftLeftLogical(current, Size, true, false);
        }
    }

    protected Integer CalcArrayAddressNumber(ArrayList<Node> childrens, int Size) throws Exception {

        Node ind;
        ind = childrens.remove(0);
        Integer value = ind.GetInt("value");
        // ultimo elemento da idexacao
        if (childrens.size() == 0) {
            return value;
        }

        Integer collNumber = ind.GetInt("collNumber"),
                ret = CalcArrayAddressNumber(childrens, Size);

        return ((value * collNumber) + ret);
    }

    protected String CalcArrayAddressAux(ArrayList<Node> childrens, int Size) throws Exception {
//        boolean multPerSize = true;
        int collNumber;
        Node ind, nextIndex;
        ind = childrens.remove(0);
//        System.out.println("CalcArrayAddressAux:" + childrens.size() + ":" + Size + "\n" + ind);
        String current, varname;

        if (ind.eq("class", "expr")) {
            varname = CreateTmpVar();
//            varname = CreateVar(
//                    //                    new Node(CreateAlias("TMP"))
//                    new Node(CreateAlias("T"))
//                            .Set("array", "false")
//                            .Set("type", "int32")
//                            .Set("var.tmp", "true")
//            );

            Node p1 = visit(ind.childrens().get(0)),
                    p2 = visit(ind.childrens().get(1));

            __exp(ind.getText(), p1.Get("_return"), p2.Get("_return"), varname);

        } else {
            varname = CreateVar(ind);
        }
//        if (ind)
        // ultimo elemento da idexacao
        if (childrens.size() == 0) {

            if (Utils.IsNumber(ind.getText())) {
                return "" + ind.GetInt("value");
            } else {
                return GenShiftLeftLogical(varname, Size, false, true);
            }
        }

        collNumber = ind.GetInt("collNumber");

        nextIndex = childrens.get(0);

        if (ind.isNumber("value") && nextIndex.isNumber("value")) {
//                System.out.println("CalcArray:" + ind.GetInt("value") + ":" + ind.GetInt("collNumber") + ":" + Integer.parseInt(ret));
            current = "" + (ind.GetInt("value") * collNumber);
        } else {

            current = GenShiftLeftLogical(varname, collNumber, false, false);
        }
        String ret = CalcArrayAddressAux(childrens, Size);

        __exp("+u", current, ret, current).Set("comment", "colls shift");

        return current;
    }

    /**
     * ProceSSa um node:<br>
     * Ordem:<br>
     * ->Primeiro filhoS;<br>
     * ->No principal;<br>
     *
     * @param n
     * @throws Exception
     */
    protected void visitChildrensFirst(Node n) throws Exception {
//        System.out.println("Visit:" + n.getText());

        for (Node n1 : n.childrens()) {
//            System.out.println("Visit:" + n1.getText());
            visit(n1);
        }
//        System.out.println("Process:" + n.getText());
        processarNode(n);
    }

    /**
     * ProceSSa um node:<br>
     * Ordem:<br>
     * ->Primeiro filho;<br>
     * ->No principal;<br>
     * ->Segundo filho;<br>
     *
     * @param n
     * @throws Exception
     */
    protected void visitNoMeio(Node n) throws Exception {
        visit(n.getFilho(0));
        processarNode(n);
        visit(n.getFilho(1));
    }

    /**
     * ProceSSa um node:<br>
     * Ordem:<br>
     * ->No princial;<br>
     * ->DepoiS oS filhoS
     *
     * @param n
     * @throws Exception
     */
    protected void visitRootFirst(Node n) throws Exception {
        processarNode(n);
        for (Node n1 : n.childrens()) {
            visit(n1);
        }
    }

    /**
     * Aloca endereço no inicio da memoria para conStanteS ApenaS tipoS<br>
     * [inteiro, booleano e char]
     *
     * @param n
     * @throws Exception
     */
    protected void gerarConstante(Node n) throws Exception {

//        System.out.println("Gerando constant:" + n.childrens().Get(0) + n.childrens().Get(1));
//        CreateAlias(n.Get(label, label));
//        getLabel("C", n);
//        if (n.closest("block", "class") != null) {
//            code.registerVar(n.Get("_return"), 1, DataLayout.TYPE_VAR);
//        }
    }

    /**
     * Executa a extração fo block principal
     *
     * @throws Exception
     */
    public boolean main() throws Exception {
        Node main = ast.getMain();
        if (main != null) {
//        main.Set("_return", Threads.getId("main"));
            Node stmts = main.find("stmts");

            if (stmts.childrens().size() > 0 && code.OpenBlock("main")) {
//                basicBlock.push("main");
//                basicBlockCount.push(0);

                PushLabel("main", "block");
                visit(stmts);

                PushLabel("main-end", "block");

//                basicBlock.pop();
//                basicBlockCount.pop();
                code.CloseBlock();
                return true;
            }
        }
        return false;
    }

    public Code run() throws Exception {
        if (!inicializado) {
            inicializado = true;
            if (!main()) {
                //Se a função main não possui statments gera o codigo para as outras funcoes
                ArrayList<Node> functions = ast.getRoot().findAll("dec.func", "class", 0);
                for (Node n : functions) {
//                    System.out.println("Gen Func:" + funcGenMap + n);
//                    genFunction("main." + n.getText(), n);
                    genFunction(n.getText(), n);
                }
            }
        }
        // Atualiza as posivos de todas as instruções e labels
        code.UpdatePositions();

        return code;
    }

    // Atualiza as posivos de todas as instruções e labels
    protected void UpdatePositions() {
        Block b;
        // Reseta a posicao inicial;
        code.PosicaoLabel = 0;
        for (Map.Entry<String, Block> x : code.Blocks().entrySet()) {
            b = x.getValue();
            b.CurrentAddress = 0;

            for (Instruction n : b.Instructions()) {
//            System.out.println("Update->:" + CurrentAddress + ":" + code.Position());
                switch (n.Get("type")) {
                    case "label":
                        n.Set("reference.position", b.CurrentAddress)
                                .Set("global.reference.position", code.Position());
                        code.RegisterLabelAddress(n.Get("label"));
                    case "alloc":
                        break;
                    default:
                        n.Set("block.position", b.CurrentAddress)
                                .Set("block.position.origin", b.CurrentAddress++)
                                .Set("global.position", code.PositionInc());
                }
            }
        }
    }

    /**
     * ProceSSa If (ElSe'Set)*
     *
     * @param n
     * @throws Exception
     */
    protected void IfStmts(Node n) throws Exception {
        String end = gerarLabel(), next;
        int size = n.childrens().size(), last = size - 1, current = 0;
        boolean ifChain = size > 1;

        //(If)+ (else)?
        next = ifChain ? gerarLabel() : end;

        n.Set("next", next).Set("end", end);

        for (Node stmt : n.childrens()) {
            switch (stmt.Class()) {
                case "if.case.stmt":
                    IfCaseStmt(n, stmt, ifChain, (current == last));
                    current++;
                    break;
                // Else case
                case "stmts":
//                    String next = gerarLabel();
//                    n.Set("next", next);
                    visit(stmt.Set("next", next));
                    // Cria um PushLabel final 
//                    PushLabel(n.Get("end"), "branch");
//                    PushLabel(end);
                    break;
            }
        }
        // Cria um PushLabel final 
//        System.out.println("Label end:" + n.Get("end"));
        PushLabel(n.Get("end"), "branch");
    }

    /**
     * ProceSSa If, eSSe metodo deve Ser chamado pelo metodo IfStmtS
     *
     * @param n
     * @param _if
     * @param ifChain
     * @throws Exception
     */
    protected void IfCaseStmt(Node n, Node _if, boolean ifChain, boolean last) throws Exception {
        Node test = _if.first();
        String testType = "jb";

        if (test.childrens().size() > 1) {
            visit(test.childrens().get(0));
            test = test.last();
        } else {
            test = test.first();
        }

        Node stmts = _if.find("stmts");

        // Se o teste falha deve saltar o bloco 
        n.Set("start", gerarLabel());

        test.Set("test.type", testType).Copy("next,start,end", n);

        Branch(test, 0);
        // marca com um label o inicio do bloco do if
        PushLabel(n.Get("start"), "branch");

        // gera as instruções do bloco
        visit(stmts);

        // se for uma cadeia de condiçoes com ou sem else
        if (ifChain) {
            if (!last) {
                // Salta os outros testes
                _goto(n.Get("end"));
            }
            //Adiciona o label de proximo elemento da cadeia de if's e else
            PushLabel(n.Get("next"), "branch");
            //Atualiza o ponteiro para o proximo elemento
            n.Set("next", gerarLabel());
        }
    }

    /**
     * Executa tratamento de um no eSpecifico, Selecionado pelo atributo claSS
     *
     * @param n
     * @throws Exception
     */
    protected void processarNode(Node n) throws Exception {
        switch (n.Class()) {
            case "return":
                genReturn(n);
                break;
            case "ctrl.stmt":
                ctrlStmt(n);
                break;
            case "label.stmt":
                PushLabel(n.getText(), "user");
                break;
        }
    }

    protected void _visitExpr(Node n) throws Exception {
        visit(n.childrens().get(0));
        visit(n.childrens().get(1));

//        System.out.println("VisitExpr:" + n);
        switch (n.Get("subclass")) {
            case "arith":// ( + | - | * | / ) integer
                genExpArith(n);
                break;
        }
    }

    protected void BranchAnd(Node n, int index) throws Exception {
//        int pos = 0;
//         if (n.parent == null) {
//            System.out.println("BranchAnd:" + n);
//        }
//        OrTestAndOccurrence++;
        Instruction instruction = null;
        ArrayList<Node> x = n.childrens();
        Node op1 = x.get(0), op2 = x.get(1);
        String type = n.Get("test.type"),
                step = gerarLabel(),
                attrs = "next,end,inc,test.type,skip.test";

        n.Set("skip.test", step);

        op1.Copy(attrs, n);
        op2.Copy(attrs, n);

        instruction = Branch(op1, 1);

        if (instruction != null) {
            switch (type) {
                case "jtb":
                    instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));

                    if (n.parent != null && n.parent.eq("value", "||")) {
                        if (index == 1) {
                            UpdateLabel(instruction, n.parent.Get("skip.test"));
                        } else if (index == 2) {
                            UpdateLabel(instruction, n.parent.Get("end"));
                        }
//                    } else if (n.parent.eq("value", "&&")) {

                    } else {
                        UpdateLabel(instruction, n.Get("end"));
                    }
                    break;
                case "jb":
//                    if (n.parent != null) {
                    if (n.parent != null && n.parent.eq("value", "||")) {
                        if (index <= 1) {
                            UpdateLabel(instruction, n.parent.Get("skip.test"));

                        } else if (index == 2) {
//                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                            UpdateLabel(instruction, n.Get("next"));
                        }
                    }
//                    } else {

//                    }
            }
        }
//        System.out.println("instruction Branch 1:" + instruction + "\n" + op1);

//        if (index == 0 || (op1.in("value", new String[]{"||", "&&"}) && !n.eq("value", op1.Get("value")))) {
        // Registra o label para o saldo dos testes do primeiro parametro
        PushLabel(step, "branch");
//        }
        instruction = Branch(op2, 2);
        if (instruction != null) {
            switch (type) {
                case "jtb":
                    if (n.parent != null && n.parent.eq("value", "||")) {
                        if (index == 1) { // teste 1 do parametro 2 com pai ||
                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                            UpdateLabel(instruction, n.Get("end"));
                        } else if (index == 2) {  // teste 2 do parametro 2
                            UpdateLabel(instruction, n.Get("next"));
                        }
//                    } else if (n.parent.eq("value", "&&")) {
//                        if (index == 1) { // teste 1 do parametro 2 com pai && 
////                            throw new Exception("// teste 1 do parametro 2 com pai &&");
//
//                        } else if (index == 2) {  // teste 2 do parametro 2 com pai && 
//                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
//                            UpdateLabel(instruction, n.Get("end"));
//                        }
                    } else {
                        if (index == 1) {  // teste 1 do parametro 2 geral
                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                            UpdateLabel(instruction, n.Get("end"));
                        } else if (index == 2) {
//                            throw new Exception("teste 2 do parametro 2 geral");
                        }
                    }
                    break;
                case "jb":

//                    System.out.println("I(jb:and:2)[" + index + "]\n" + instruction + "\n" + n);
//                    if (n.parent != null) {
                    if (n.parent != null && n.parent.eq("value", "||")) {
                        if (index <= 1) {
//                            UpdateLabel(instruction, n.parent.Get("skip.test"));
                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                            UpdateLabel(instruction, n.Get("start"));

                        } else if (index == 2) {
//                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                            UpdateLabel(instruction, n.Get("next"));

                        }
                    }
//                    } else {

//                    }//                    } else {
//                    }
            }
        }
    }

    public void UpdateLabel(Instruction I, String label) {
        String copy = I.Get("label");
        if (!copy.equals("")) {
            code.RemoveLabelReference(copy);
        }
        I.Set("label", label);
        code.AddLabelReference(label);
    }

    protected void BranchOr(Node n, int index) throws Exception {
//        if (n.parent == null) {
//            System.out.println("BranchOr:" + n);
//        }
        //        int pos = 0;
//        OrTestAndOccurrence++;
        Instruction instruction = null;
        ArrayList<Node> x = n.childrens();
        Node op1 = x.get(0), op2 = x.get(1);
        String type = n.Get("test.type"),
                step = gerarLabel(),
                attrs = "next,start,end,inc,test.type,skip.test";

        n.Set("skip.test", step);

        op1.Copy(attrs, n);
        op2.Copy(attrs, n);

//        System.out.println("BranchOr:" + n.parent);
        // Executa o lado esquerdo do ||
        instruction = Branch(op1, 1);

        if (instruction != null) {
            switch (type) {
                case "jtb":
                    if (n.parent != null && n.parent.eq("value", "&&")) {
                        if (index == 1) {
                            UpdateLabel(instruction, n.parent.Get("skip.test"));
                        }
                    }
                    break;
                case "jb":
//                    System.out.println("I(jb:1)[" + index + "]\n" + instruction + "\n" + n);

//                    System.out.println("Entrei no jb ||" + (jb++) + instruction + n.parent);
//                    if (n.parent != null) {
                    if (n.parent != null && n.parent.eq("value", "||")) {
//                        if (index == 2) {
                        instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                        UpdateLabel(instruction, n.Get("start"));
//                        }
                    } else if (n.parent != null && n.parent.eq("value", "&&")) {
                        if (index <= 1) {
                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                            UpdateLabel(instruction, n.parent.Get("skip.test"));
                        } else if (index == 2) {
                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                            UpdateLabel(instruction, n.parent.Get("start"));
                        }
//                        }
                    } else {
                        instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                        if (index <= 1) {
                            UpdateLabel(instruction, n.Get("start"));
                        } else if (index == 2) {

                        }
                    }
            }
        }
//        System.out.println("instruction Branch 1:" + instruction + "\n" + op1);
        // Registra o label para o saldo dos testes do primeiro parametro
        PushLabel(step, "branch");

        // Executa o lado direito do ||
        instruction = Branch(op2, 2);
        if (instruction != null) {
            switch (type) {
                case "jtb":
                    if (n.parent != null && n.parent.eq("value", "&&")) {
                        if (index == 1) {
                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                            UpdateLabel(instruction, n.parent.Get("end"));
                        }
                    }
                    break;
                case "jb":
//                    if (n.parent != null) {
                    if (n.parent != null && n.parent.eq("value", "||")) {
                        if (index <= 1) {

                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                            UpdateLabel(instruction, n.Get("start"));

                        } else if (index == 2) {
//
//                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
                            UpdateLabel(instruction, n.Get("next"));
                        }
                    } else if (n.parent != null && n.parent.eq("value", "&&")) {
                        if (index == 1) {
//                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
//                            UpdateLabel(instruction, n.parent.Get("start"));
                        } else if (index == 2) {
//                            instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
//                            UpdateLabel(instruction, n.parent.Get("start"));
                        }
//                        }
                    } else {
//                        instruction.Set("op", Utils.ComplementOperation(instruction.Get("op")));
//                        UpdateLabel(instruction, n.Get("next"));
                    }
            }
        }
    }

    protected Instruction BranchTest(Node n, int index) throws Exception {
        ArrayList<Node> x = n.childrens();
        String type = n.Get("test.type");
        boolean invert = false;
        String op, p0, p1;

        switch (type) {
            case "jb":
                invert = true;
                break;
            case "jtb":
                break;
            default:
                throw new Exception("test.type '".concat(type).concat("' not suported. Values {jtb,jb}"));
        }

        visit(n);

        // Resultado do test class ou acesso a um id ou bool
        if (!n.eq("class", "expr")) {
            op = "==";
            p0 = n.Get("_return");
            p1 = "1";
        } else { //(<, <=, >, >=, ==, !=)
            op = n.getText();
            p0 = visit(x.get(0)).Get("_return");
            p1 = visit(x.get(1)).Get("_return");
        }

        // Inverte o teste e salta o bloco
        if (invert) {
            op = Utils.ComplementOperation(op);
        }
//        return Branch(op, p0, p1, next);
        Instruction r1 = new Instruction().Set("label", n.Get("next"));

        Boolean p0IsValue = Api.IsValue(p0), p1IsValue = Api.IsValue(p1);

        if (p0IsValue && p1IsValue) {

            // Se a expressão for resolvivel e o resultado é positivo
            // A instrução de saldo é adicionada 
            if (Utils.ResolveExprBool(op, p0, p1)) {
                Add(r1.Set("type", "jump").Set("format", "jump"));
            }

        } else {

            r1.Set("format", "branch")
                    .Set("type", "branch")
                    .Set("op", op)
                    .Set("p1", p0)
                    .Set("p2", p1)
                    .Set("p1value", "" + p0IsValue)
                    .Set("p2value", "" + Api.IsValue(p1));
            Add(r1);
        }
//        System.out.println("Add branch:" + branckInvert.peek() + branckInvert + ">>" + (!branckInvert.isEmpty() && branckInvert.peek()) + "\n" + r1);
        return r1;
    }

    protected Instruction Store(String attr) throws Exception {
        return Add(new Instruction()
                .Set("type", "store")
                .Set("format", "memory")
                .Set("p1", attr));
    }

    protected Instruction Load(String attr) throws Exception {
        return Add(new Instruction()
                .Set("type", "load")
                .Set("format", "memory")
                .Set("p1", attr));
    }

    protected Instruction Add(Instruction i) throws Exception {

//        System.out.println("ADD:" + i);
        String loop = loopScope.peek(), type, paramValue;

        boolean temporary,
                ignoreLoadStore = !i.in("type", new String[]{"jump", "goto", "label"});

        // Inseri a carga dos parametros
        if (ignoreLoadStore) {
            for (String param : new String[]{"p1", "p2"}) {
                if (!i.Has(param)) {
                    continue;
                }

                temporary = i.contem(param, "_T");
                paramValue = i.Get(param);

                if (!temporary && i.eq(param + "value", "false")) {
                    Load(paramValue);
                }

                if (i.isNumber(param)) {
                    continue;
                }

                if (temporary) {
                    type = "int32";
                } else {
//                    System.out.println("ADD:TYPEOF:" + paramValue + ":" + varGenMap.get(paramValue));
                    type = Variables.TypeOf(varGenMap.get(paramValue));
                }

//                System.out.println(param + ".type = " + type);
                i.Set(param + ".type", type);
            }
        }

        // Adiciona a instrução
        code.Add(i.Set("inloop", loop));

//        if (i.Has("dst") && !i.contem("dst", "_T")) {
        // Se existe um destino e ele não for temporario
        // persiste o valor copiado para o destino
        if (i.Has("dst") && !i.Get("dst").split("\\[")[0].contains("_T")) {
//        if (i.Has("dst") && !i.Get("dst").matches("^_T\\d+(\\[\\w+\\])?")) {

//            String dst = Utils.clearName(i.Get("dst"));
//            System.out.println("TYPE OF1:" + dst);
//            System.out.println("TYPE OF2:" + varGenMap.get(dst));
//            System.out.println("TYPE OF3" + Variables.TypeOf(varGenMap.get(dst)));
            if (!i.eq("dst.indexed", "true")) {
//            } else {
                Store(i.Get("dst"));
            } else {
//                //Se o destino for indexdo sempre é um byte
                i.Set("dst.type", "byte");
//                i.Set("dst.type", "byte");
            }
        }

        return i;
    }

    protected Instruction Branch(Node n, int index) throws Exception {
        Instruction instruction = null;

//        System.out.println("test:" + n);
//        ArrayList<Node> childrens = n.parent.childrens();
//        if (childrens.size() > 1) {
////            visit(childrens.Get(0));
//            Node c = n;
//            n = childrens.Get(1);
//            n.Copy("test.type,next,start,end", c);
//        }
//        System.out.println("n:" + n);
        switch (n.getText()) {
            case "&&":
                BranchAnd(n, index);
                break;
            case "||":
                BranchOr(n, index);
                break;
            default:
                instruction = BranchTest(n, index);
        }
        return instruction;
    }

    /**
     * Cria uma linha de Salto para PuShLabel eSpecificado no parametro.
     *
     * @param label
     * @throws Exception
     */
    protected void _goto(String label) throws Exception {
        Instruction r = new Instruction();
        r.Set("label", label)
                .Set("type", "jump")
                .Set("format", "jump")
                .Set("locker", getIdMaster());
        Add(r);

    }

    /*Inicio das avaliacoes booleanas*/
    /**
     * Gera um linha de inStrução de retorno no block de codigo
     *
     * @param n
     * @throws Exception
     */
    protected void genReturn(Node n) throws Exception {

        Instruction nreg;
        ArrayList<Node> returns = n.find("exprs").childrens();
        for (Node ret : returns) {
//            System.out.println("Return p:" + ret);
            nreg = new Instruction()
                    .Set("type", "push_return")
                    .Set("format", "push_return")
                    .Set("p1value", "" + Api.IsValue(ret.getText()))
                    .Set("p1", ret.Get("_return"));
            nreg.Set("locker", getIdMaster());
            Add(nreg);
        }

        Add(new Instruction()
                .Set("type", "return")
                .Set("format", "return")
                .Set("p1", returns.size())
                .Set("p1value", "true")
                .Set("locker", getIdMaster()));

    }

    /**
     * Gera todoS oS SaltoS de um loop for
     *
     * @param n
     * @throws Exception
     */
    protected void genFor(Node n) throws Exception {
        String attrs = "next,end,inc,test";

        loopScope.push(n.Get("scope"));

        setMaster(n);

        String next = gerarLabel(),
                //                next = gerarLabel(),
                end = gerarLabel(),
                inc = gerarLabel(),
                testLabel = gerarLabel();

        n.Set("next", next)
                .Set("end", end)
                .Set("inc", inc)
                .Set("test", testLabel);

        Node stmt = n.find("stmts");

        stmt.Copy(attrs, n);

        boolean hasTest = false;
        Node t = n.find("test"), test = null;

        if (t.childrens().size() > 0) {
            hasTest = true;
            test = t.first();
            test.Copy(attrs, n).Set("test.type", "jtb");
        }

        /*Inicializacoes e atribuicoes*/
        visit(n.find("initialization"));
        if (hasTest) {
            /*Salta para o teste do laco*/
            _goto(testLabel);
        }

        /*Marca o inicio do bloco do laco*/
        PushLabel(next, "branch");

        /*Executa o bloco do for*/
        visit(stmt);

        /*Label que marca os incrementos dos laco*/
        PushLabel(inc, "branch");

        /*Executa os incrementos*/
        visit(n.find("increment"));
        // test.type = ((JumpToBlock in LOOP) | (JumpBlock in IF || SWITCH))
        // test.type = JumpToBlock 
//        test.Set("test.type", "for").Set("next", next).Set("end", end).Set("or", next);
//        System.out.println("Teste:" + test);
        if (hasTest) {
            /*Label que marca o teste do laco*/
            PushLabel(testLabel, "branch");

            Branch(test, 0);
        } else {
            _goto(next);
        }

        PushLabel(end, "branch");

        setNullMaster();

        loopScope.pop();
    }
//    protected void genFor(Node n) throws Exception {
//        setMaster(n);
//        String start = gerarLabel(), next = gerarLabel();
//
//        n.Set("next", next);
//        n.Set("start", start);
//
//        Node stmt = n.find("stmts");
//        stmt.Set("next", start);
//        /*Inicializacoes e atribuicoes*/
//        visit(n.find("initialization"));
//        // Label que marca o inicio do for
//        PushLabel(start, "branch");
//
//        Node test = n.find("test").first();
////        if (test.hasChildrens())
////        test.Set("true", gerarLabel());
////        test.Set("false", n.Get("next"));
////        test.Set("true", next);
////        test.Set("false", n.Get("next"));
//        test.Set("and", next);
//        test.Set("next", next);
//        test.Set("or", gerarLabel());
//
//        Branch(test);
//
//        if (test.Has("or")) {
//            PushLabel(test.Get("or"), "branch");
//        }
//
////        PushLabel(test.Get("true"), "branch");
//        /*Executa o bloco do for*/
//        visit(stmt);
//        /*Executa os incrementos*/
//        visit(n.find("increment"));
//        /*Salta para inicio do laço*/
//        _goto(start);
//        /*Marca o endereço final para sair do laço*/
//        PushLabel(test.Get("next"), "branch");
//
//        setNullMaster();
//    }

    protected void genExpArith(Node n) throws Exception {
        Node op1 = n.childrens.get(0);
        Node op2 = n.childrens.get(1);

        String _op = n.getText();
//        System.out.println("Gen genExpArith:" + _op);
//
//        

//        // se os dois valores são
//        if (op1.eq("type", "value") && op2.eq("type", "value")) {
//
//            n.Set("_return", resolveExpr(_op, op1.Get("value"), op2.Get("value")));
//        }
//        if (op1.IsNumber("value") && op2.IsNumber("value")) {
//
//        } else {
        // Inverte a ordem dos operandos se o segundo for seletor e o primeiro for valor
        if (op1.isNumber("value") && !op2.isNumber("value")) {
            Node aux = op1;
            op1 = op2;
            op2 = aux;
        }

//        System.out.println("GerarArit" + op1 + "-" + op2);
        String _p1 = op1.Get("_return");
        String _p2 = op2.Get("_return");

        String _return = CreateTmpVar();

        n.Set("_return", _return);

        Instruction instruction = __exp(_op, _p1, _p2, _return);

        if (instruction.eq("type", "copy")) {

            n.Set("_p1", instruction.Get("p1")).Set("value", instruction.Get("p1"));
        } else {
            n.Set("_op", _op).Set("_p1", _p1).Set("_p2", _p2);
        }
    }

    /**
     * Para cima verificado <==>
     */
    protected void genArray(Node n) {

        if (n.eq("raiz", "t")) {
            currentMatrixvalues = new ArrayList<String>();
            matrixValues.put(n.Get("id"), currentMatrixvalues);
            for (Node c : n.childrens()) {
                genArray(c);
            }
        } else if (n.eq("terminal", "t")) {
            currentMatrixvalues.add(n.getText());
        } else {
            for (Node c : n.childrens()) {
                genArray(c);
            }
        }
    }

    protected ArrayList<String> initValues(Node n) {
        ArrayList<String> values = new ArrayList<>();
        switch (n.Get("class")) {
            case "_array":
                values = matrixValues.get(n.Get("id"));
                break;
            /*valores e constantes*/
            default:
                String _return = n.Get("_return");
                if (_return.matches("^_.*")) {
                    _return = "0";
                }
                values.add(_return);
        }
        return values;
    }

    /**
     * Gera a chamada para o metodo de deSalocacao de memoria
     *
     * @param n
     * @throws Exception
     */
    protected void genDelete(Node n) throws Exception {
        for (Node address : n.childrens()) {
            visit(address);
            __genParam(address.Get("_return"));
            __gerarCall(Functions.FREE, "1", getIdMaster());
        }
    }

    /**
     * Gera a Switch caSe
     *
     * @param n
     */
    protected void switchStmt(Node n) throws Exception {
        /*Label que sera consultado pelo break para sair do case*/

        String start,
                next,
                end = gerarLabel(),
                dst = null;

        n.Set("next", end);

        Node test = n.find("test");

        // Verifica se existe entrada para selecao
        if (test.childrens().size() > 0) {
            test = test.first();
            visit(test);
            dst = test.Get("_return");
        }

//        test;
        ArrayList<Node> cases = n.findAll("def.case", "class", 1);

        int current = 0, last = cases.size() - 1;
        for (Node _case : cases) {

            start = gerarLabel();
            next = gerarLabel();

            _case.Set("start", start)
                    .Set("_input", dst)
                    .Set("next", next)
                    .Set("end", end)
                    .Set("test.type", "jb");

            genCase(_case, current == last);
            current++;
        }

        /*Marca o endereço final para sair do switch*/
        PushLabel(end, "branch");
    }

    /**
     * Gera oS caSeS de um Switch
     *
     * @param n
     */
    protected void genCase(Node n, Boolean last) throws Exception {
        ArrayList<Node> exprs;
        Node test;
//        System.out.println("GEN_CASE:" + n);

//        String start = n.Has("start") ? n.Get("start") : gerarLabel();
//        String next = gerarLabel(), end;
//
////        System.out.println("GenCase:" + start + ":" + next);
        ArrayList<Node> itens = n.childrens();
        Node stmts = n.find("stmts");
//        Node or;
//  
        if (itens.size() >= 2) { // Tem teste
            test = n.first();
            String input = n.Get("_input"),
                    start = n.Get("start"),
                    end = n.Get("end"),
                    next = n.Get("next");

//
            if (input != null) {
                exprs = ExprsEq(test, new Node(input).Set("_return", input));
            } else {
                exprs = test.childrens();
            }

//            // Atualiza o PushLabel de sucesso das exprs
            for (Node expr : exprs) {
                expr.Copy("start,next,end,test.type", n);
            }
//
            Branch(OR(exprs).Set("next", next), 0);

            /*Se nenhum dos casos for compativel salta para o proximo*/

 /*Marca o PushLabel inicial do caso*/
            PushLabel(start, "branch");
            /*Visita o bloco do case*/
            visit(stmts);
//            System.out.println("Gen case :" + last);
            if (!last) {
                /*Salta para o proximo case ou para o fim do switchcase*/
                _goto(end);
            }
            /*Label do inicio do proximo case*/
            PushLabel(next, "branch");

        } else {  // Default statiment
            visit(stmts);
        }

//        String start = n.Has("start") ? n.Get("start") : gerarLabel();
//        String next = gerarLabel(), end;
//
////        System.out.println("GenCase:" + start + ":" + next);
//        ArrayList<Node> itens = n.childrens();
//        Node stmts = n.find("stmts");
//        Node or;
//
//        if (itens.size() >= 2) { // Tem teste
//            String input = n.Get("_input");
//            Node test = n.first();
//            ArrayList<Node> exprs;
//
//            if (input != null) {
//                exprs = ExprsEq(test, new Node(input).Set("_return", input));
//            } else {
//                exprs = test.childrens();
//            }
//
//            /*Seleciona o endereco de saida se é no proximo case ou no fim do switch*/
//            if (stmts.last().eq("value", "fallthrough")) {
//                end = gerarLabel();
//                n.Set("next", end);
//            } else {
//                end = n.Get("end");
//            }
//
//            // Atualiza o PushLabel de sucesso das exprs
//            for (Node expr : exprs) {
//                expr.Set("true", !last ? next : end);
////                expr.Set("false", next);
////                expr.Set("false", start);
//            }
//
//            Branch(OR(exprs).Set("next", next), 0);
//
//            /*Se nenhum dos casos for compativel salta para o proximo*/
////            _goto(!last ? next : end);
//            /*Marca o PushLabel inicial do caso*/
//            PushLabel(start, "branch");
//            /*Visita o bloco do case*/
//            visit(stmts);
//
//            if (!last) {
//                /*Salta para o proximo case ou para o fim do switchcase*/
//                _goto(end);
//                /*Label do inicio do proximo case*/
//                PushLabel(next, "branch");
//            }
//        } else {  // Default statiment
//            visit(stmts);
//        }
    }

    protected Node NodeBoolExpr(String op, Node e1, Node e2) {
        return new Node(op)
                .Set("type", "bool")
                .Set("class", "expr")
                .Set("subclass", "bool")
                .addFilho(e1)
                .addFilho(e2);
    }

    protected ArrayList<Node> ExprsEq(Node n, Node input) throws Exception {
        ArrayList<Node> list = new ArrayList<>();
        switch (n.Class()) {
            case "exprs":
                for (Node e : n.childrens()) {
                    list.add(NodeBoolExpr("==", input, e));
                }
                break;
            default:
                list.add(NodeBoolExpr("==", input, n));
        }
        return list;
    }

    protected Node OR(ArrayList<Node> n) throws Exception {
        switch (n.size()) {
            case 0:
                return null;
            case 1:
                return n.get(0);
            default:
                Node x = n.get(0);
                n.remove(x);
                return NodeBoolExpr("||", x, OR(n)).Copy("start,next,end,test.type", x);
        }
    }

    protected int qtdByIndex(Node n) throws Exception {
        Node indexes = n.find("indexes");
        if (indexes == null) {
            return 1;
        } else {
            int sum = 0;
            for (Node index : indexes.childrens()) {
                sum += index.GetInt("_return");
            }
            return sum;
        }

    }

    /**
     * Gera inStrução de atribuicao
     *
     * @param n
     * @throws Exception
     */
    protected void genAssign(Node n) throws Exception {

//        System.out.println("GENAssign :" + n);
        ArrayList<Node> localExpr = new ArrayList<>();

        ArrayList<String> localAttribs = new ArrayList<>(),
                //                copymode = new ArrayList<>(),
                //                operators = new ArrayList<>(),
                returns;
        ArrayList<Boolean> copyaddress = new ArrayList<>();
        String p1,
                modify = n.Get("modify"),
                declare = "" + n.getText().equals("dec.var.short");

//        System.out.println("genAssign:" + n.childrens().Get(0).childrens().Get(0));
        Node dsts = n.childrens().get(0);

        if (n.in("value", new String[]{"dec.var.short", "def.assign"})) {

            for (Node attrib : n.childrens().get(1).childrens()) {
//                System.out.println(">n:" + attrib);
                visit(attrib);
//                System.out.println("n:" + attrib);

//                System.out.println("genAssign:" + attrib.Get("class") + ":" + attrib.Get("value"));
                if (attrib.eq("class", "call")) {

                    returns = attrib.getList("returns");

                    if (returns != null) {
                        for (String _return : returns) {
                            localAttribs.add(_return);
                            copyaddress.add(attrib.eq("subclass", "address"));
                        }
                    }
                } else if (attrib.eq("class", "index")) {

//                    System.out.println("Attr <- :" + attrib);
                } else if (attrib.eq("class", "literal")) {
//                    System.out.println("literal<- :" + attrib);
                    switch (attrib.Get("subclass")) {
                        case "array":
                            for (Node element : attrib.find("exprs").childrens()) {
                                visit(element);
                                copyaddress.add(element.eq("subclass", "address"));
                                localAttribs.add(element.Get("_return"));
                            }
                            break;
                    }

                } else {
//                    System.out.println("Assign:" + attrib);
                    copyaddress.add(attrib.eq("subclass", "address"));
                    localAttribs.add(attrib.Get("_return"));
                }
                localExpr.add(attrib);
            }
        }

//        System.out.println("Copy::" + localAttribs);
//        System.out.println("Lista de atribuicoes locais:\n" + dsts + "\n>>\n" + n);
//        System.out.println("Lista de atribuicoes locais:\n" + dsts + "\n>>\n" + localAttribs);
        Node attrib;
        // Executa a copia dos valores
        for (int dstPosition = 0, i = 0; i < localAttribs.size(); dstPosition++) {

            Node d = dsts.childrens().get(dstPosition);
            d.Set("declare", declare);
            visit(d);

//            System.out.println("D:" + d);
            if (d.eq("class", "unary") && d.eq("dst.pointer", "*")) {
                p1 = localAttribs.get(i);
                Add(new Instruction()
                        .Set("format", "pointer_assign")
                        .Set("type", "pointer_assign")
                        .Set("dst", d.childrens().get(1).Get("_return"))
                        .Set("p1", p1)
                        .Set("p1value", "" + Api.IsValue(p1))
                        .Set("dst.pointer", "*")
                        .Set("locker", getIdMaster()));
                i++;
                // Salva o valor em memoria
            } else if (d.eq("array", "true")) {
                for (int j = 0; j < d.GetInt("array_size"); j++, i++) {
//                    System.out.println("ARRAY:" + d.Get("_return") + "[" + j + "]" + " -- " + localAttribs.Get(i));

                    Copy(d.Get("_return") + "[" + j + "]", localAttribs.get(i), copyaddress.get(i));
                }
            } else {
                // Copia valor para endereco
//                System.out.println("Attrib[" + modify + ":" + (modify.equals("")) + "]:" + d.Get("_return") + "<" + localAttribs.Get(i));

                if (!modify.equals("")) {
                    __exp(modify, d.Get("_return"), localAttribs.get(i), d.Get("_return")).Set("comment", "");

                } else {
                    attrib = localExpr.get(i);

//                    if (attrib.eq("class", "expr")) {
////                        System.out.println("Attrib:" + attrib);
//                        __exp(attrib.Get("_op"), attrib.Get("_p1"), attrib.Get("_p2"), d.Get("_return")).Set("comment", "");
//                    } else {
                    Copy(d.Get("_return"), localAttribs.get(i), copyaddress.get(i));
//                    }
                }
                i++;
            }
        }
    }

    protected void __genParam(String param) throws Exception {
        Instruction r = new Instruction();
        r.Set("format", "push_param")
                .Set("type", "push_param")
                .Set("p1", param)
                .Set("p1value", "" + Api.IsValue(param))
                .Set("locker", getIdMaster());
        Add(r);
    }

    protected void genFunction(String id, Node func) throws Exception {
        if (!funcGenMap.containsKey(id)) {
            funcGenMap.put(id, "true");

            Node block = func.find("stmts");

//            basicBlock.push(id);
//            basicBlockCount.push(0);
            code.OpenBlock(id);
            /*Cria a PushLabel inicial da funcao*/
            PushLabel(id, "block");

            /*Declara as variaveis de parametro*/
            genPopParams(func);
            /*Declara as variaveis de retorno caso sejam nomeadas*/
//            visit(func.find("def::return"));

//            System.out.println(Variables.printScope(func.Get("scope")));

            /*Processa o bloco de instrucoes*/
            visit(block);
            /*Cria a PushLabel final da funcao*/
            PushLabel(id + "-end", "block");
            code.CloseBlock();
//            basicBlock.pop();
//            basicBlockCount.pop();
        }
    }

    protected void genCall(Node n) throws Exception {
        int argsNum = 0;
        // Se for metodo carrega o contexto no primeiro parametro

//        System.out.println("GenCall:" + n);
        if (!n.eq("ctx", "")) {
            Node var = Variables.Get(n.Get("ctx"));
//            System.out.println("");
//            __genParam("&" + CreateAlias(var));
//            Add(new Instruction()
//                    .Set("", PushLabel));
//            CreateAlias(var)
            __genParam(CreateVar(var));
            argsNum++;
        }

        // Gera os argumentos
        Node args = n.find("arguments").find("exprs");
        if (args != null) {
            for (Node param : args.childrens()) {
                // Processa o argumento para obter o _return
                visit(param);
                // Cria a instrução de parametro
                __genParam(param.Get("_return"));
            }
            argsNum += args.getChildrenCount();
        }

//        System.out.println("GetMETHOD" + n.getText());
        Node def = Functions.Get(n.getText());
        String id = def.getText();
//        System.out.println("GenCall:" + id + "\n" + def);
        genFunction(id, def);
        code.Block().AddDependence(id);
        // Cria a instrução de chamada
        Instruction r = __gerarCall(id, "" + argsNum, getIdMaster(), def.find("dec.return"));

//        System.out.println("CALL -> " + r.getList("returns"));
        n.addList("returns", r.getList("returns"));

    }

    protected String UnaryResolve(String op, String value) throws Exception {
        switch (op) {
            case "-":
                return "-" + value;
            case "!":
                return "" + (value.equalsIgnoreCase("false") || value.equals("0") ? "1" : "0");
        }
        throw new Exception(String.format("Invalid [%s]%s ", op, value));
    }

    /*Fim dos laços*/
 /*Inicio das operacoes aritimeticas*/
    /**
     * Unary pode Ser uma expreSSão do tipo <br>
     * (&|*)addreSS - operacao de ponteiroS <br>
     * (-) addreSS|value - negação aritimetica <br>
     * (!) addreSS|value - negação booleana
     *
     * @param n
     * @throws Exception
     */
    protected void unaryStmt(Node n) throws Exception {

        String op = n.childrens().get(0).getText().trim();
        Node value = n.childrens().get(1);
        visit(value);

        String _ret,
                _val = value.Get("_return");
        // Se for escrita verifica se é escrita no ponteir ou no lugar referenciado

        if (Api.IsValue(_val)) {
            _ret = UnaryResolve(op, _val);

//        } else {
        } else if (n.eq("access", "write")) {
            _ret = "ERR";
            n.Set("dst.pointer", op);
        } else {

//            System.out.println("N:[" + n.Get("id") + "]");
            _ret = CreateTmpVar();
//            System.out.println("Unary:" + n.childrens().Get(1));
            String type;
            if (AddressOperator.matcher(op).matches()) {
                type = "pointer_assign";
            } else {
                type = "unary";
            }

            Instruction r = new Instruction()
                    .Set("type", type)
                    .Set("format", type)
                    .Set("dst", _ret)
                    .Set("p1", _val)
                    .Set("op", op)
                    .Set("copymode", "unary")
                    .Set("p1value", "" + Api.IsValue(_val))
                    .Set("locker", getIdMaster());
            Add(r);
//            System.out.println("N:" + n);
//            throw new Exception("Unary Stmt not defined");
        }

        n.Set("_return", _ret);
//        System.out.println("UnaryStmt:[" + _val + ":" + op + "]" + n);
    }

    protected Instruction __exp(String op, String p1, String p2, String ret) throws Exception {

        boolean p1value = Api.IsValue(p1),
                p2value = Api.IsValue(p2);

        String unsigned = "false";

        if (op.contains("u")) {
            op = op.replace("u", "");
            unsigned = "true";
        }

        Instruction r = new Instruction();

        if (p1value && p2value) {
            r.Set("type", "copy")
                    .Set("format", "copy")
                    .Set("p1value", "true")
                    .Set("p1", "" + Utils.ResolveExpr(op, p1, p2));

        } else {
            r.Set("type", "assign")
                    .Set("format", "assign")
                    .Set("cat", "exp")
                    .Set("p1", p1)
                    .Set("p2", p2)
                    .Set("copymode", "assign")
                    .Set("p1value", "" + p1value)
                    .Set("p2value", "" + p2value);
//            System.out.println("__EXP:" + r);
        }
        r.Set("dst", ret)
                .Set("op", op)
                .Set("op.unsigned", unsigned)
                .Set("locker", getIdMaster());

        return Add(r);
    }

    /**
     * Gera Salto para inStruçõeS de continue e break;
     *
     * @param n
     * @throws Exception
     */
    protected void ctrlStmt(Node n) throws Exception {
        String go;
        Node ctn = n.closest("for.stmt,switch.stmt", "class");

        switch (n.getText()) {
            case "continue":
                go = ctn.Get(ctn.eq("class", "for.stmt") ? "inc" : "end");
                break;
            case "break":
                go = ctn.Get("end");
                break;
            case "goto":
                go = n.Get("label");
                break;
            default:
                throw new Exception("Undefined ctrl stmt '" + n.getText() + "'.");
        }
        _goto(go);
    }

    /**
     * Executa a extração do codigo baSeado na aSt, e retorna um objeto
     * CodigoTreSEnderecoS contendo oS blockS em Formato de treS endereçoS.
     *
     * @return
     * @throws Exception
     */
    public Code getCodigoDeTresEnderecos() throws Exception {
        return run();
    }

    /**
     * Ativa ou deSativa a otimizacao
     *
     * @param lc
     */
    public void setOtimizacao(boolean lc) {
        code.setOtimizacao(lc);
    }

    protected void setMaster(Node n) {
        master.push(n);
    }

    protected Node getMaster() {
        return master.peek();
    }

    protected void setNullMaster() {
        master.pop();
    }

    protected int getIdMaster() throws Exception {
        if (master.empty()) {
            return 0;
        }
        return master.peek().GetInt("id");
    }

    /**
     * Adiciona aS declaraçõeS bem como carregaoS parametroS e referenciaS
     *
     * @param n
     * @throws Exception
     */
    protected void genPopParams(Node func) throws Exception {
        int reg = 0;
        String destino, atribuido, id;
        /**
         * Se for method declara e atribui o endereco a variavel
         */
        Node receive = func.find("receive.type");
        if (receive != null) {
//            atribuido = "_A" + (reg++);
//            destino = clearPointer(atribuido, CreateAlias(receive.childrens().Get(0)));
            destino = CreateVar(receive.childrens().get(0));
            // copy_pointer?
//            Copy(Api.clearID(destino), __popParam().Get("_return"), false);
            __popParam(Api.clearID(destino));
        }

        for (Node argument : func.find("arguments").childrens()) {
            for (Node arg : argument.find("ids").childrens()) {
                /**
                 * Gera uma instrução de copia do registrador a{i},i = [0-4],
                 * para o endereço de memoria dos parametros
                 */
                //                atribuido = "_Ra" + (reg++);
                //                atribuido = "_A" + (reg++);
                //                System.out.println("PopArg:" + arg);
                //                destino = clearPointer(atribuido, CreateAlias(arg));
                // copy_pointer?
//                id = Api.clearID(CreateAlias(arg));
//                Copy(id, __popParam(id).Get("_return"), false);
                __popParam(Api.clearID(CreateVar(arg)));
            }
        }
    }

    protected Instruction __popParam(String varname) throws Exception {
//        String varname = CreateTmpVar();
        return Add(new Instruction()
                .Set("type", "pop_param")
                .Set("format", "pop_param")
                .Set("p1", varname)
                .Set("p1value", "false")
                .Set("_return", varname)
                .Set("locker", getIdMaster()));
    }

//    protected void inicializarVariaveis() throws Exception {
////        System.out.println("INICIALIZAR VARIAVEIS:");
//
//        for (Node container : ast.getRoot().childrens()) {
//            for (Node constant : container.findAll("dec::const,dec::var", "class", 1)) {
//                visit(constant);
//            }
//        }
//    }
    protected Instruction Copy(String dst, String src, boolean address) throws Exception {
        String type;

        Instruction inst = new Instruction();
        // Identifica que o valor copiado é um endereco ou valor
        inst.Set("format", "copy")
                .Set("src.address", address)
                .Set("dst", dst)
                .Set("p1", src)
                .Set("p1value", Api.IsValue(src) + "")
                .Set("locker", getIdMaster());

        Boolean dstIndexed = ArrayPatternRegex.matcher(dst).matches(),
                srcIndexed = ArrayPatternRegex.matcher(src).matches();

        if (dstIndexed || srcIndexed) {

            type = "indexed_assign";

            inst.Set("src_indexed", srcIndexed);
            inst.Set("dst.indexed", dstIndexed);

            if (srcIndexed) {
                inst.Set("p1.indice", Api.getIndice(src));
                inst.Set("indice", Api.getIndice(src));
            }

            if (dstIndexed) {
                inst.Set("dst.indice", Api.getIndice(dst));
            }

        } else {
            type = "copy";
        }

        return Add(inst.Set("type", type));
    }

    /**
     * Gera um PuShLabel unico
     *
     * @return
     */
    protected String gerarLabel() {
//        return code.genLabel();
        return code.getCurrentBlockName() + "+_i" + (incLabel++);
    }

    /**
     * Cria uma linha de PuShLabel no block de codigo corrente
     *
     * @param label String
     * @throws Exception
     */
    protected Instruction PushLabel(String label, String type) throws Exception {
        Instruction r = new Instruction()
                .Set("label", label)
                .Set("type", "label")
                .Set("label_type", type)
                .Set("format", "label")
                .Set("locker", getIdMaster());

        return Add(r);
    }

    /**
     * Gera um nome de variavel unico,
     *
     * @param pre prefixo da variavel
     * @param n
     * @return
     */
    protected String CreateAlias(String pre) {
        return "_" + pre + (tmpVarCount++);
    }

    protected String CreateTmpVar() {
        return CreateAlias("T");
    }

    protected String CreateVar(Node n) throws Exception {
//        System.out.println("Create var " + n);
        String id = n.getText(), g;

        if (!varGenMap.containsKey(id)) {
            g = SetNewVar(id, n);

            varGenMap.put(id, g);
            if (varGenMap.containsKey(g)) {
                if (id.length() < varGenMap.get(g).length()) {
                    varGenMap.put(g, id);
                }
            } else {
                varGenMap.put(g, id);
            }
        }

        return varGenMap.get(id);
    }

    protected String SetNewVar(String id, Node n) throws Exception {
        Node var = Variables.Get(id);

        String uniqPath, alias, prefix;
        Integer elementCount;

        if (var == null) {
            throw new Exception(String.format("Var %s não definida", id));
        }

        // Se for uma constante retorna o valor
        if (var.eq("constant", "true")) {
            return var.Get("default.value");
        }
        uniqPath = var.Get("scope") + "." + var.Get("name");

//        System.out.println("$$$$$$$$$$$$4 VarName:" + uniqPath + "\n\n" + var);
        if (var.Has("alias")) {
            // Se a variavel possui um alias definido
            alias = var.Get("alias");
        } else if (varGenMap.containsKey(uniqPath)) {

            // Se a variavel possui um alias definido no mapa de variaveis
            alias = varGenMap.get(uniqPath);
        } else {
            // Cria um novo alias para a variavel

            if (var.eq("global", "true")) {
                prefix = "G";
            } else {
                prefix = "V";
            }

            alias = CreateAlias(prefix);

            varGenMap.put(uniqPath, alias);
            if (varGenMap.containsKey(alias)) {
                if (uniqPath.length() < varGenMap.get(alias).length()) {
                    varGenMap.put(alias, uniqPath);
                }
            } else {
                varGenMap.put(alias, uniqPath);
            }

            var.Set("alias", alias);
//                    .Set("size", Types.Size(var.Get("type")));

            n.Copy("pointer,alias", var);

//            vn = var.Get("scope");
            elementCount = 1;
//            System.out.println("var:" + var);
//            System.out.println("gerarVariavelSetVAR:" + var);
            // Se for um array considera os filhos como index

            if (var.eq("array", "true") && var.childrens().size() > 0) {
                for (Node node : var.childrens()) {
                    elementCount *= (node.eq("constant", "true")
                            ? node.GetInt("default.value")
                            : node.GetInt("value"));
                }
            }

            Alloc(var, elementCount);
//            System.out.println(">>>>>>>>>gerarVariavelSet(" + alias + "):" + var);

//            if (!isConstant || !var.in("type", primitives)) {
//                // Adiciona variaveis no frame global ou da funcao
//            } else {
//                alias = var.Get("default.value");
//            }
        }
        return alias;
    }
//
//    protected void DefineAliasInScope(String id, String alias, String scope) {
//
//        String tmp = id.replace(scope, ""),
//                path = scope + "",
//                ;
//
//
////        Matcher matcher = Pattern.compile(Variables.VARNAME).matcher(id);
////        if (matcher.find()) {
////            varname = matcher.group();
////        }
////        System.out.println("DefineAliasInScope:\n " + id + ":\n " + scope + "\n: " + tmp + "\n: '" + varname + "'");
////
////        matcher = Pattern.compile(Variables.SCOPE_LEVE).matcher(tmp);
////
////        while (matcher.find()) {
////            scope += matcher.group(0);
////            System.out.println("parh:" + scope);
////            path = scope;
////        }
//    }

    protected void Alloc(Node var, Integer numElements) throws Exception {
        DataLayout ctx;
        Instruction alloc;
        String alias = var.Get("alias"), type = var.Get("type");
        Boolean global = alias.contains("_G");

        if (global) {
            ctx = code.GlobalContext();
        } else {
            ctx = code.Block().Context();
        }

        if (!ctx.contains(alias)) {

//            System.out.println("Alloc(" + code.Block().getName() + "):" + alias + " > Numero:" + numElements);
            alloc = new Instruction()
                    .Set("type", "alloc")
                    .Set("format", "alloc")
                    .Set("p1", alias)
                    .Set("p1.type", type)
                    .Set("numElements", numElements)
                    .Set("locker", getIdMaster());

            if (global) {
                // Adiciona a alocacao no bloco global
                code.Blocks().get("global").Add(alloc);
            } else {
                // Adiciona a alocacao no bloco atual
                code.Add(1, alloc);
            }

            ctx.Add(alias, type, numElements);
        }
    }

    /**
     * Imprime o codigo de treS endereçoS
     *
     * @return
     */
    @Override
    public String toString() {

        return //                printVarMap()
                code.toString();
    }

    public String printVarMap() {
        StringBuilder s = new StringBuilder();

        for (Map.Entry<String, String> e : varGenMap.entrySet()) {
            s.append(e.getKey() + " : " + e.getValue() + "\n");
        }

        System.out.println("Var Map:\n" + s.toString());
        return varGenMap.toString();
    }

    protected Instruction __gerarCall(String funcname, String numparam, int idMaster, Node returns) throws Exception {
        Instruction call = __gerarCall(funcname, numparam, idMaster);

        // Atribuição dos enderecos de retorn
        if (returns != null) {
            ArrayList<String> rts = new ArrayList<>();
            String t;
            for (Node r : returns.childrens()) {
                t = CreateTmpVar();
                rts.add(t);
                Add(new Instruction()
                        .Set("type", "pop_return")
                        .Set("format", "pop_return")
                        .Set("p1value", "false")
                        .Set("p1", t));
            }

            call.addList("returns", rts);
        }

        return call;
    }

    /**
     * Gera inStrução de call
     *
     * @param funcname
     * @param numparam
     * @param idMaster
     * @param variavelRetorno
     * @throws Exception
     */
    protected Instruction __gerarCall(String funcname, String numparam, int idMaster) throws Exception {
        Instruction r = new Instruction();
        r.Set("format", "call")
                .Set("type", "call")
                .Set("funcname", funcname)
                .Set("numParams", numparam).Set("locker", idMaster);
        Add(r);
        return r;
    }

}
