/*
 * 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 common;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

public class Block {

    protected HashMap<String, Boolean> ctrlTypes = new HashMap<String, Boolean>() {
        {
            put("label", true);
            put("alloc", true);
        }
    };

    public static int uniq = 0;
    public LinkedList<Instruction> instructions = new LinkedList<>();
    protected String name;
    protected Code code;
    protected HashMap<String, Instruction> labels = new HashMap<>();
    protected HashMap<String, Boolean> dependences = new HashMap<>();
    protected DataLayout Context;
    public int BaseAddress = 0;
    public int CurrentAddress = 0;
    public int AddressIncrement = 1;
    public boolean HasCall;
    public int nonLabelCount;
    public int type;

    public static int TYPE_INSTRUCTIONS = 0;
    public static int TYPE_CTRL = 1;

    public Block(String name, Code c) {
        code = c;
        BaseAddress = c.Position();
        AddressIncrement = c.AddressIncrement;
        this.name = name;
        type = TYPE_INSTRUCTIONS;
        Context = new DataLayout(name);
    }

    public Block(String name) {
        this.name = name;
        type = TYPE_INSTRUCTIONS;
        Context = new DataLayout(name);
    }

    public int Type() {
        return type;
    }

    public void Type(int type) {
        this.type = type;
    }

    public LinkedList<Instruction> Instructions() {
        return this.instructions;
    }

    public boolean HasCall() {
        return HasCall;
    }

    public Block Add(int position, Instruction inst) {
        this.instructions.add(position, inst);
        return this;
    }

    public Block Add(Instruction nr) {
        this.instructions.add(nr);
        return this;
    }

    public Block Remove(Instruction instruction) throws Exception {
        this.instructions.remove(instruction);
        return this;
    }

    public String getName() {
        return this.name;
    }

    /**
     * Retorna o grupo de variaveiS de eScopo locaiS
     *
     * @return
     */
    public DataLayout Context() {
        return this.Context;
    }

    /**
     * Retorna o grupo de variaveiS de eScopo global
     *
     * @return
     */
    public DataLayout GlobalContext() {
        return this.code.GlobalContext();
    }

//    public DataLayout Context(DataLayout d) {
//        return this.Context.copy(d);
//    }
    public void Close() throws Exception {
        Update();
    }

    public void Update() throws Exception {
        CurrentAddress = 0;

        for (Instruction n : instructions) {
            if (ctrlTypes.containsKey(n.Get("type"))) {

                n.Set("reference.position", CurrentAddress)
                        .Set("global.reference.position", code.Position());

                if (n.eq("type", "label")) {
                    code.RegisterLabelAddress(n.Get("label"));
                }

            } else {

                n.Set("block.position", CurrentAddress)
                        .Set("block.position.origin", CurrentAddress)
                        .Set("global.position", code.PositionInc());
                CurrentAddress += AddressIncrement;
            }
        }
    }

    public int LastAddress() {
        return Instructions().size() - 1;
    }

    public Instruction RemoveLast() {
        return instructions.removeLast();
    }

    public Block ReplaceVar(String find, String replace) {
//        System.out.println("Replace " + find + " -> " + replace);
        for (Instruction inst : instructions) {
            for (String p : new String[]{"dst", "p1", "p2"}) {
                if (inst.eq(p, find)) {
                    inst.Set(p, replace);
                }
            }
        }
        return this;
    }

    public int CountInstructions() {
        return nonLabelCount;
    }

    public void AddDependence(String id) {
        dependences.put(id, true);
    }

    public ArrayList<String> GetDependences() {
        return new ArrayList<String>() {
            {
                for (Map.Entry<String, Boolean> entry : dependences.entrySet()) {
                    add(entry.getKey());
                }
            }
        };

    }

}
