"""
CAP, CodeGeneration, CFG linearization to a list of statements
"""

from typing import List, Set

from Lib.Statement import (
    Statement, AbsoluteJump, ConditionalJump
)
from Lib.Terminator import (Return, BranchingTerminator)
from Lib.CFG import Block


def ordered_blocks_list(cfg) -> List[Block]:
    """
    Compute a list of blocks with optimized ordering for linearization.
    """
    # TODO (Lab4b, Extension)
    return cfg.get_blocks()


def linearize(cfg) -> List[Statement]:
    """
    Linearize the given control flow graph as a list of instructions.
    """
    l: List[Statement] = []  # Linearized CFG
    blocks: List[Block] = ordered_blocks_list(cfg)
    
    labdict = {}
    # We make the label dictionary
    for j,block in enumerate(blocks):
        labdict[block.get_label()] = block
   
    while blocks != []:
        block = blocks[0]
        if(l != [] and isinstance(l[-1],AbsoluteJump)):
            ll : AbsoluteJump = l[-1]
            # We try to find the next jump according to that.
            try:
                # If we find the instruction
                b = labdict[ll.label]
                if b in blocks:
                    # We remove the jump instruction and we select the block to use
                    l.pop()
                    block = b
            except KeyError:
                pass
                
        # We remove the block we used
        blocks.remove(block)
            
        # 1. Add the label of the block to the linearization
        l.append(block.get_label())
        # 2. Add the body of the block to the linearization
        l.extend(block.get_body())
        # 3. Add the terminator of the block to the linearization
        match block.get_terminator():
            case BranchingTerminator() as j:
                l.append(ConditionalJump(j.cond, j.op1, j.op2, j.label_then))
                l.append(AbsoluteJump(j.label_else))
            case AbsoluteJump() as j:
                l.append(AbsoluteJump(j.label))
            case Return():
                l.append(AbsoluteJump(cfg.get_end()))
    return l
