/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package API;

import common.Block;
import common.Code;
import common.Instruction;
import common.IvannosysTargetArch;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author EUGENIO CARVALHO
 */
public abstract class TargetGen implements IvannosysTargetArch {

    protected Code target;
    protected Code IR;

    public TargetGen(String id) {
        BaseGenInit(id);
    }

    public void BaseGenInit(String id) {
        target = new Code(id);
    }

    public Code getTarget() {
        return target;
    }

    public void setTarget(Code target) {
        this.target = target;
    }

    public Code getIR() {
        return IR;
    }

//    public void setTac(Code IR) {
//        this.IR = IR;
//    }
    public IvannosysTargetArch SetTAC(Code tac) {
        IR = tac;
        target.GData().copy(tac.GData());
        target.Parent = IR;
        return this;
    }

    protected Instruction Add(Instruction inst) {
        target.Block().Add(inst);
        return inst;
    }

    @Override
    public IvannosysTargetArch Format() {
        try {
            String code = target.getCodeStream();
            System.out.println("Target[" + target.Name() + "]:\n" + code);

        } catch (Exception ex) {
            Logger.getLogger(TargetGen.class.getName()).log(Level.SEVERE, null, ex);
        }
        return this;
    }

    @Override
    public IvannosysTargetArch Compile() {
        try {
            // Traduz o codigo intermediario para o alvo
            Translate();

        } catch (Exception ex) {
            Logger.getLogger(TargetGen.class.getName()).log(Level.SEVERE, null, ex);
        }
        return this;
    }

    protected void Translate() throws Exception {
        String targetID = target.Name(), id;
        target.Parent = IR;

        CodeProcessor.Trigger(target, targetID, "BeforeTranslate");

        for (Map.Entry<String, Block> b : IR.getBlocks().entrySet()) {
            id = b.getKey();

            IR.Use(id);

            target.OpenBlock(id);

            CodeProcessor.Trigger(target, targetID, "BeforeTranslateBlock");

            Prolog(id);

            TranslateBlock(b.getValue());

            Epilog(id);

            target.CloseBlock();

            target.UpdatePositions();

            CodeProcessor.Trigger(target, targetID, "AfterTranslateBlock");
        }

        CodeProcessor.Trigger(target, targetID, "AfterTranslate");
    }

    protected void TranslateBlock(Block b) throws Exception {
//        System.out.println("Translate block:" + b.getName() + "::" + b.Instructions().size());

        for (Instruction inst : b.Instructions()) {
//            System.out.println("Translate: " + inst.G("global.position") + "::read["
//                    + inst.G("reg.dst.load")
//                    + "|"
//                    + inst.G("reg.p1.load")
//                    + "|"
//                    + inst.G("reg.p2.load")
//                    + "] >> write["
//                    + inst.G("reg.dst.store")
//                    + "|"
//                    + inst.G("reg.p1.store")
//                    + "|"
//                    + inst.G("reg.p2.store")
//                    + "]"
//                    + inst.G("type")
//            );
//            
            switch (inst.G("type")) {
                case "label":
                    if (!inst.G("label").equals(getTarget().Block().getName())) {
                        TranslateLabel(inst);
                    }
                    break;
                case "assign":
                    TranslateAssign(inst);
                    break;
                case "unary":
                    TranslateUnary(inst);
                    break;
                case "copy":
                    TranslateCopy(inst);
                    break;
                case "jump":
                    TranslateJump(inst);
                    break;
                case "call":
                    TranslateCall(inst);
                    break;
                case "return":
                    TranslateReturn(inst);
                    break;
                case "push_param":
                    TranslatePushParam(inst);
                    break;
                case "push_return":
                    TranslatePushReturn(inst);
                    break;
                case "pop_return":
                    TranslatePopReturn(inst);
                    break;
                case "pop_param":
                    TranslatePopParam(inst);
                    break;
                case "branch":
                    TranslateBranch(inst);
                    break;
                case "indexed_assign":
                    TranslateIndexedAssignment(inst);
                    break;
                case "pointer_assign":
                    TranslatePointerAssignment(inst);
                    break;
                case "store":
                    TranslateStore(inst);
                    break;
                case "load":
                    TranslateLoad(inst);
                    break;
                default:
                    throw new Exception(String.format("TAC instruction type '%s' not defined", inst.G("type")));
            }
        }
    }

}
