def analyze_gcode(self, gcode_analyzer):
        self.layers = [PrimeTowerLayerInfo(prime_tower=self)]

        t_start = time.time()

        # Active tool
        current_tool = None  # Tool Change Info
        layer_info = self.layers[-1]  # Layer Info
        for token in gcode_analyzer.analyze_state():
            # Check if AFTER_LAYER_CHANGE label
            if token.type == Token.PARAMS and token.label == 'AFTER_LAYER_CHANGE':
                current_layer, current_layer_z = token.param[0], token.param[1]
                previous_layer_z = 0.0
                # This is because will put first tool before the AFTER_LAYER_CHANGE-BEFORE_LAYER_CHANGE block
                if current_layer != 0:
                    previous_layer_z = layer_info.layer_z
                    layer_info = PrimeTowerLayerInfo(prime_tower=self)
                    self.layers.append(layer_info)

                # Update the value
                layer_info.layer_num = current_layer
                layer_info.layer_z = current_layer_z
                layer_info.layer_height = current_layer_z - previous_layer_z

                # Update the values
                layer_info.layer_start = token

                # If current tool is not none
                if current_tool is not None:
                    self.layers[-1].tools_sequence = [current_tool]
                else:
                    self.layers[-1].tools_sequence = []

                continue

            # Check if BEFORE_LAYER_CHANGE label
            if token.type == Token.PARAMS and token.label == 'BEFORE_LAYER_CHANGE':
                # Mark the last layer end as the token before BEFORE_LAYER_CHANGE
                layer_info.layer_end = token

                # Validate the height
                toolset = [
                    tool_change_info.tool_id
                    for tool_change_info in layer_info.tools_sequence
                ]
                toolset_max_layer_height = conf.max_layer_height(toolset)

                # Layer height higher then max for the toolset (shouldn't happen!)
                if round(layer_info.layer_height,
                         5) > toolset_max_layer_height:
                    raise PrimeTowerException(
                        "Input layer #{layer_num} height {layer_height:0.4f} higher then max allowed for the toolset {tools}"
                        .format(layer_num=layer_info.layer_num,
                                layer_height=layer_info.layer_height,
                                tools=','.join([
                                    'T' + str(tool_id) for tool_id in toolset
                                ])))
                continue

            # Check if Tool change
            if token.type == Token.TOOLCHANGE:
                if token.next_tool != -1:
                    current_tool = ToolChangeInfo(tool_change=token)
                    logger.debug(
                        "PrimeTower - Added tool T{tool_id} to layer #{layer_num}"
                        .format(tool_id=token.next_tool,
                                layer_num=self.layers[-1].layer_num))

                    layer_info.tool_change_seq.append(current_tool)
                    layer_info.tools_sequence.append(current_tool)
                continue

            # Beginning to Tool block
            if token.type == Token.PARAMS and token.label == 'TOOL_BLOCK_START':
                tool_id = token.param[0]
                if tool_id != -1:
                    if tool_id != current_tool.tool_id:
                        raise ToolChangeException(
                            message=
                            "Tool id {tool_id} from TOOL_BLOCK_START doesn't match last active tool in layer"
                            .format(tool_id=tool_id),
                            tool_id=tool_id)
                    current_tool.block_start = token
                continue

            # End of Tool block
            if token.type == Token.PARAMS and token.label == 'TOOL_BLOCK_END':
                tool_id = token.param[0]
                if tool_id != -1:
                    if tool_id != current_tool.tool_id:
                        raise ToolChangeException(
                            message=
                            "Tool id {tool_id} from TOOL_BLOCK_END doesn't match last active tool in layer"
                            .format(tool_id=tool_id),
                            tool_id=tool_id)
                    current_tool.block_end = token
                continue

        # Generate the active/idle/disabled list
        #-----------------------------------------------------------
        self.analyze_tool_status()

        # Calc band and brim info
        self.generate_pillar_bands()

        t_end = time.time()
        logger.info(
            "PrimeTower: analysis done [elapsed: {elapsed:0.2f}s]".format(
                elapsed=t_end - t_start))

        return True
    def analyze_gcode(self, gcode_analyzer):
        t_start = time.time()

        # Generates the list of tool_activations per tool
        if conf.DEBUG:
            print("(DEBUG) TempController: Generating tool activation sequence per tool...")

        # Go over the tokens to generate the Tool Change Info 
        # Calculate the runtimes in the process
        print("TempController - Estimating the gcode runtimes")

        # Current tool head
        current_tool = None

        # Go over all of the tokens
        for token in gcode_analyzer.analyze_state():
            # Find the location of ;; TC_TEMP_INITIALIZE
            if token.type == Token.PARAMS and token.label == 'TC_TEMP_INITIALIZE':
                self.temp_header = token

            # Find the location of ;; TC_TEMP_SHUTDOWN
            if token.type == Token.PARAMS and token.label == 'TC_TEMP_SHUTDOWN':
                self.temp_footer = token

            # Remove the existing tokens for temp managment
            if token.type == Token.GCODE and token.gcode == 'M109':
                print("TempController: Removed an existing M109 gcode")
                gcode_analyzer.tokens.remove_node(token)

            # Setup the tool changes
            if token.type == Token.TOOLCHANGE:
                if token.state_post.tool_selected != None:
                    current_tool = ToolChangeInfo(tool_change = token)
                    if current_tool.tool_id not in self.tool_activation_seq:
                        self.tool_activation_seq[current_tool.tool_id] = []
                    self.tool_activation_seq[current_tool.tool_id].append(current_tool)
                continue

            # Beginning to Tool block
            if token.type == Token.PARAMS and token.label == 'TOOL_BLOCK_START':
                tool_id = token.param[0]
                if tool_id != -1:
                    if tool_id != current_tool.tool_id:
                        raise ToolChangeException("Tool id {tool_id} from TOOL_BLOCK_START doesn't match last active tool in layer".format(tool_id = tool_id))
                    current_tool.block_start = token
                continue

            # End of Tool block
            if token.type == Token.PARAMS and token.label == 'TOOL_BLOCK_END':
                tool_id = token.param[0]
                if tool_id != -1:
                    if tool_id != current_tool.tool_id:
                        raise ToolChangeException("Tool id {tool_id} from TOOL_BLOCK_END doesn't match last active tool in layer".format(tool_id = tool_id))
                    current_tool.block_end = token
                continue

        if self.temp_header is None:
            raise ConfException("TempController: Did not found TC_TEMP_INITIALIZE parameter in the GCode, slicer has not been configured correctly...")
        if self.temp_footer is None:
            raise ConfException("TempController: Did not found TC_TEMP_SHUTDOWN parameter in the GCode, slicer has not been configured correctly...")

        t_end = time.time()
        if conf.PERF_INFO:
            print("TempController: analysis done [elapsed: {elapsed:0.2f}s]".format(elapsed = t_end - t_start))