# vim: tabstop=4 shiftwidth=4 softtabstop=4
# -*- coding: utf-8 -*-
#


import string
import re
from ftg.flow_table import FlowTable
from ftg.flow_priority import FlowPriority
from ftg.flow_entry import FlowEntry
from ftg.formatter.base_formatter import BaseFormatter
from ftg.utils.common import CommonUtils


class GraphvizTBFormatter(BaseFormatter):
    MATCH_FORMAT = '"m${table_no}_${priority_no}_${entry_no}" [ shape="diamond", fillcolor="#ddddff", peripheries=1, label="${match_exp}" ]'
    INSTRUCTION_OUTPUT_FORMAT = '"l${table_no}_${priority_no}_${entry_no}_${instruction_no}" [ shape="record", fillcolor="#ffcccc", peripheries=1, label="{${actions}}" ]'
    INSTRUCTION_CONTROLLER_FORMAT = '"l${table_no}_${priority_no}_${entry_no}_${instruction_no}" [ shape="record", fillcolor="#ffffcc", peripheries=1, label="{${actions}}" ]'
    INSTRUCTION_RESUBMIT_FORMAT = '"l${table_no}_${priority_no}_${entry_no}_${instruction_no}" [ style="dashed", shape="record", fillcolor="#ffffff", color="#0000ff", peripheries=1, label="{${actions}}" ]'
    INSTRUCTION_DROP_FORMAT = '"l${table_no}_${priority_no}_${entry_no}_${instruction_no}" [ shape="record", fillcolor="#cccccc", color="#ff0000", peripheries=1, label="{${actions}}" ]'
    INSTRUCTION_OTHER_FORMAT = '"l${table_no}_${priority_no}_${entry_no}_${instruction_no}" [ shape="record", fillcolor="#ffffff", peripheries=1, label="{${actions}}" ]'
    MATCH_TO_INSTRUCTION_FORMAT = '"m${table_no}_${priority_no}_${entry_no}" -> "l${table_no}_${priority_no}_${entry_no}_${instruction_no}" [ color="#0000CC", fontcolor="#663333", label="YES" ]'
    START_TO_PRIORITY_FORMAT = '"start${table_no}" -> "m${table_no}_${priority_no}_${entry_no}" [ lhead=cluster_t${table_no}_p${priority_no}, color="#000000", fontcolor="#663333", label="" ]'
    PRIORITY_TO_PRIORITY_FORMAT = '"l${table_no}_${old_priority_no}_${old_entry_no}_${old_instruction_no}" -> "m${table_no}_${priority_no}_${entry_no}" [ ltail=cluster_t${table_no}_p${old_priority_no}, lhead=cluster_t${table_no}_p${priority_no}, color="#000000", fontcolor="#663333", label="NO" ]'
    PRIORITY_TO_END_FORMAT = '"l${table_no}_${priority_no}_${entry_no}_${instruction_no}" -> "end${table_no}" [ ltail=cluster_t${table_no}_p${priority_no}, color="#000000", fontcolor="#663333", label="NO" ]'
    BLOCK_TO_BLOCK_FORMAT = '"l${table_no}_${priority_no}_${old_entry_no}_${instruction_no}" -> "m${table_no}_${priority_no}_${entry_no}" [ ltail=cluster_t${table_no}_p${priority_no}_b${old_block_no}, lhead=cluster_t${table_no}_p${priority_no}_b${block_no}, color="transparent", label="" ]'
    TABLE_TO_TABLE_FORMAT = '"end${old_table_no}" -> "start${table_no}" [ ltail=cluster_t${old_table_no}, lhead=cluster_t${table_no}, color="#000000", fontcolor="#663333", label="" ]'
    TABLE_TO_TABLE_FORMAT_INVISIBLE = '"end${old_table_no}" -> "start${table_no}" [ ltail=cluster_t${old_table_no}, lhead=cluster_t${table_no}, color ="transparent", label="" ]'
    INSTRUCTION_TO_RESUBMIT_FORMAT = '"l${table_no}_${priority_no}_${entry_no}_${instruction_no}" -> "resubmit${table_no}" [ dirType="none", arrowhead=none, style="dotted", color="#0000CC", fontcolor="#663333", label = "" ]'
    INSTRUCTION_TO_RESUBMIT_TABLE_FORMAT = '"l${table_no}_${priority_no}_${entry_no}_${instruction_no}" -> "resubmit_from${table_no}_to${resubmit_table_no}" [ dirType="none", arrowhead=none, style="dashed", color="#0000CC", fontcolor="#663333", label = "" ];'
    
    RESUBMIT_TO_START_FORMAT = '"resubmit${table_no}" -> "start${table_no}" [ style="dotted", color="#0000CC", fontcolor="#663333", label = "" ]'
    RESUBMIT_TO_TABLE_FORMAT = '"l${table_no}_${priority_no}_${entry_no}_${instruction_no}" -> "resubmit_to_t${resubmit_table_no}" [ dirType="none", arrowhead=none, style="dotted", color="#0000CC", fontcolor="#663333", label = "" ]'

    START_FORMAT = '"start${table_no}" [ style="rounded", fillcolor="#ddddff", peripheries=1, label="Match START" ]'
    END_FORMAT = '"end${table_no}" [ style="rounded", fillcolor="#ddddff", peripheries=1, label="Match END" ]'
    RESUBMIT_FORMAT = '"resubmit${table_no}" [ style="rounded,dotted", color="#0000cc", fillcolor="#ddddff", peripheries=1, label="Resubmit" ]'
    RESUBMIT_TABLE_FORMAT = '"resubmit_from${table_no}_to${resubmit_table_no}" [ style="rounded", color="#0000cc", fillcolor="#ddddff", peripheries=1, label="Table ${resubmit_table_no}" ]'

    INDENT_IN_GRAPH = '  '
    INDENT_IN_TABLE = '    '
    INDENT_IN_PRIORITY = '      '
    INDENT_IN_PRIORITY_BLOCK = '        '

    RANK_MIN_FORMAT = '{rank = min; "Level_1"; "%s"}'
    RANK_MAX_FORMAT = '{rank = max; "Level_10000"; "%s"}'
    RANK_SAME_FORMAT = '{rank = same; "%s"; "%s"}'
    GROUP_FORMAT = '"${entry_name}" [ group = ${group_no} ]'

    formatter_name = "graphviz-tb"

# [ br0 ] { fill: #ddddff; border: double; bordercolor: blue; } -- { label: "eth5   "; }

    HEADER_FORMAT = '''
digraph GRAPH_0 {

edge [ dirType="forward", arrowtail=none, fontsize=10, fontname="Arial" ];
ratio = "auto"
graph [ label="", rankdir=TB, fontsize=22, fontname="Arial", concentrate=true, compound=true ];
node [
    fontsize=12,
    fillcolor=white,
    style=filled,
    shape=box ];

"Level_1" -> "Level_2" -> "Level_3" -> "Level_4" -> "Level_5" -> "Level_8000" -> "Level_10000" [color ="transparent"];
"Level_1" [color = "transparent", label="", fixedsize = true, width = 0.1, height = 0.1, fontsize=2];
"Level_2" [color = "transparent", label="", fixedsize = true, width = 0.1, height = 0.1, fontsize=2];
"Level_3" [color = "transparent", label="", fixedsize = true, width = 0.1, height = 0.1, fontsize=2];
"Level_4" [color = "transparent", label="", fixedsize = true, width = 0.1, height = 0.1, fontsize=2];
"Level_5" [color = "transparent", label="", fixedsize = true, width = 0.1, height = 0.1, fontsize=2];
"Level_8000" [color = "transparent", label="", fixedsize = true, width = 0.1, height = 0.1, fontsize=2];
"Level_10000" [color = "transparent", label="", fixedsize = true, width = 0.1, height = 0.1, fontsize=2];

    '''

    FOOTER_FORMAT = '}'

    TABLE_START = """
  subgraph cluster_t${table_no} {
    label="Table ${table_no}";
    color="#000000";
    style=dashed;
    penwidth=2;
    """
    
    TABLE_END = '  }'

    PRIORITY_START = """
    subgraph cluster_t${table_no}_p${priority_no} {
      label="Priority ${priority_no}";
      labeljust = "l";
      color="#cc0000";
      style=dashed;
      penwidth=2;
    """

    PRIORITY_END = '    }'

    PRIORITY_BLOCK_START = """
      subgraph cluster_t${table_no}_p${priority_no}_b${block_no} {
        label="";
        color = "transparent";
    """
    
    PRIORITY_BLOCK_END = '      }'

    SUMMARY_START = """
"summary" [ shape=plaintext, label=<
<TABLE BGCOLOR="gray" CELLBORDER="0">
<TR><TD COLSPAN="3">Summary</TD></TR>
<TR><TD COLSPAN="2" BGCOLOR="lightgray">Number of Tables</TD><TD BGCOLOR="lightgray">${number_of_tables}</TD></TR>
<TR><TD COLSPAN="2" BGCOLOR="lightgray">Number of Flows</TD><TD BGCOLOR="lightgray">${number_of_flows}</TD></TR>
<TR><TD COLSPAN="3" BGCOLOR="gray">
<TABLE CELLBORDER="0">
  <TR><TD>Table NO.</TD><TD>Priorities</TD><TD>Flows</TD></TR>
"""

    SUMMARY_END = """
</TABLE>
</TD></TR>
</TABLE>
> ]
"""

    SUMMARY_TABLE_FORMAT = '<TR><TD BGCOLOR="lightgray">${table_no}</TD><TD BGCOLOR="lightgray">${number_of_priorities}</TD><TD BGCOLOR="lightgray">${number_of_flows}</TD></TR>'
    SUMMARY_TABLE2_FORMAT = '<TR><TD>${table_no}</TD><TD BGCOLOR="lightgray">${number_of_priorities}</TD><TD BGCOLOR="lightgray">${number_of_entries}</TD></TR>'

    def __init__(self, util=CommonUtils(), title=""):
        self._tables = []
        self.util = util
        self._title = title
        self._groupid = 1
        self.entries_per_block = 10
        self.resubmit_loop = False
        self.resubmit_edges = []
        self.resubmit_tables = []
        self.ranks_in_table = []
        self.output_summary = False
        self._initialize_templates()
        self.util.debug_out(self.formatter_name + " formatter initialized")

    def set_entries_per_block(self, entries_per_block):
        self.entries_per_block = entries_per_block
    
    def _initialize_templates(self):
        self.start_template = string.Template(self.START_FORMAT)
        self.end_template = string.Template(self.END_FORMAT)
        self.table_start_template = string.Template(self.TABLE_START)
        self.priority_start_template = string.Template(self.PRIORITY_START)
        self.priority_block_start_template = string.Template(self.PRIORITY_BLOCK_START)
        self.match_template = string.Template(self.MATCH_FORMAT)
        self.instruction_output_template = string.Template(self.INSTRUCTION_OUTPUT_FORMAT)
        self.instruction_controller_template = string.Template(self.INSTRUCTION_CONTROLLER_FORMAT)
        self.instruction_resubmit_template = string.Template(self.INSTRUCTION_RESUBMIT_FORMAT)
        self.instruction_drop_template = string.Template(self.INSTRUCTION_DROP_FORMAT)
        self.instruction_other_template = string.Template(self.INSTRUCTION_OTHER_FORMAT)
        self.match_to_instruction_template = string.Template(self.MATCH_TO_INSTRUCTION_FORMAT)
        self.start_to_priority_template = string.Template(self.START_TO_PRIORITY_FORMAT)
        self.priority_to_end_template = string.Template(self.PRIORITY_TO_END_FORMAT)
        self.table_to_table_template = string.Template(self.TABLE_TO_TABLE_FORMAT)
        self.table_to_table_invisible_template = string.Template(self.TABLE_TO_TABLE_FORMAT_INVISIBLE)
        self.resubmit_template = string.Template(self.RESUBMIT_FORMAT)
        self.resubmit_table_template = string.Template(self.RESUBMIT_TABLE_FORMAT)
        self.block_to_block_template = string.Template(self.BLOCK_TO_BLOCK_FORMAT)
        self.priority_to_priority_template = string.Template(self.PRIORITY_TO_PRIORITY_FORMAT)
        self.instruction_to_resubmit_template = string.Template(self.INSTRUCTION_TO_RESUBMIT_FORMAT)
        self.instruction_to_resubmit_table_template = string.Template(self.INSTRUCTION_TO_RESUBMIT_TABLE_FORMAT)
        self.resubmit_to_start_template = string.Template(self.RESUBMIT_TO_START_FORMAT)
        self.resubmit_to_table_template = string.Template(self.RESUBMIT_TO_TABLE_FORMAT)
        self.group_template = string.Template(self.GROUP_FORMAT)
        self.summary_start_template = string.Template(self.SUMMARY_START)
        self.summary_end_template = string.Template(self.SUMMARY_END)
        self.summary_table_template = string.Template(self.SUMMARY_TABLE_FORMAT)
        self.summary_table2_template = string.Template(self.SUMMARY_TABLE2_FORMAT)
        
    def format(self):
        formatted_flow = []
        formatted_flow.append(self.get_header())

        n_table_in_switch = 0
        n_entry_in_priority = 0
        n_entry_in_old_priority = 0
        n_priority_in_table = 0
        b_no = 0
        old_t_no = 0
        old_p_no = 0

        tables = self._tables.keys()
        for t_no in sorted(tables, key=lambda x: int(x)):
            if self.output_target_tables != []:
                if not t_no in self.output_target_tables:
                    continue
                    
            n_priority_in_table = 0
            self.resubmit_loop = False
            self.resubmit_edges = []
            self.resubmit_tables = []
            self.ranks_in_table = []
            formatted_flow.extend( [
                self._format_table_start(t_no),
                self._format_start(t_no)])
            priorities = self._tables[t_no].get_priorities()
            priorities = sorted(priorities, key=lambda x: int(x), reverse=True)
            max_priority = priorities[0]
            formatted_flow.append(
                self._format_start_to_priority(t_no, max_priority))
            for p_no in priorities:
                formatted_flow.append(self._format_priority_start(t_no, p_no))
                n_entry_in_priority = 0
                b_no = 0
                self.ranks_in_table.append(
                    self._generate_match_name(t_no, p_no, n_entry_in_priority))
                for ent in self._tables[t_no].get_priority(p_no).get_flow_entries():
                    if n_entry_in_priority == 0:
                        formatted_flow.append(
                            self._format_priority_block_start(t_no, p_no, b_no))
                        b_no += 1
                    elif (n_entry_in_priority % self.entries_per_block) == 0:
                        formatted_flow.extend( [
                            self._format_priority_block_end(),
                            self._format_block_to_block(t_no, p_no, b_no, n_entry_in_priority, self.entries_per_block),
                            self._format_priority_block_start(t_no, p_no, b_no) ])
                        b_no += 1
                    formatted_flow.append(
                        self._format_entry(t_no, p_no, n_entry_in_priority, ent.match_exp, ent.instructions))
                    n_entry_in_priority += 1
                self.ranks_in_table.append(
                    self._generate_instruction_name(t_no, p_no, n_entry_in_priority - 1, "0"))

                formatted_flow.extend( [
                    self._format_priority_block_end(),
                    self._format_priority_end() ])
                n_priority_in_table += 1
                if n_priority_in_table <= len(priorities) and n_priority_in_table != 1:
                    formatted_flow.append(self._format_priority_to_priority(
                        t_no, p_no, n_entry_in_old_priority, self.entries_per_block, old_p_no))
                old_p_no = p_no
                n_entry_in_old_priority = n_entry_in_priority

            formatted_flow.extend( [
                self._format_end(t_no),
                self._format_priority_to_end(t_no, p_no, n_entry_in_priority - 1),
                self._format_resubmit_loop(t_no),
                self._format_resubmit_edges(),
                self._format_resubmit_tables(),
                self._rank_priorities(t_no),
                self._rank_min("start" + t_no),
                self._rank_max("end" + t_no),
                self._format_table_end() ])
            n_table_in_switch += 1
            if n_table_in_switch <= len(tables) and n_table_in_switch != 1:
                formatted_flow.append(
                    self._format_table_to_table(t_no, old_t_no))
            old_t_no = t_no

        formatted_flow.append(self._format_summary())
        formatted_flow.append(self.get_footer())
        return formatted_flow

    def _generate_match_name(self, table_no, priority_no, entry_no):
        return "m%s_%s_%s" % (table_no, priority_no, entry_no)

    def _generate_instruction_name(self, table_no, priority_no, entry_no, instruction_no):
        return "l%s_%s_%s_%s" % (table_no, priority_no, entry_no, instruction_no)

    def _format_start(self, table_no):
        return self.INDENT_IN_TABLE + self.start_template.substitute({"table_no":table_no})

    def _format_end(self, table_no):
        return self.INDENT_IN_TABLE + self.end_template.substitute({"table_no":table_no})

    def _format_summary(self):
        format_summary = []
        format_summary_tables = []
        number_of_all_table_flows = 0
        
        if self.output_summary:
            tables = self._tables.keys()
            for table_no in sorted(tables, key=lambda x: int(x)):
                priorities = self._tables[table_no].get_priorities()
                number_of_priorities = 0
                number_of_flows = 0
                for priority_no in sorted(priorities, key=lambda x: int(x), reverse=True):
                    number_of_priorities += 1
                    number_of_flows += self._tables[table_no].get_priority(priority_no).get_number_of_flow_entries()
                    self.util.debug_out("table_no:" + table_no + " priority_no:" + priority_no \
                        + " flows:" + str(self._tables[table_no].get_priority(priority_no).get_number_of_flow_entries()))
                
                format_summary_tables.append(self._format_summary_table(table_no, number_of_priorities, number_of_flows))
                number_of_all_table_flows += number_of_flows
                
            format_summary.append(self._format_summary_start(len(tables), number_of_all_table_flows))
            format_summary.extend(format_summary_tables)
            format_summary.append(self._format_summary_end())
            return "".join(format_summary)
        else:
            return ""

    def _format_summary_start(self, number_of_tables, number_of_flows):
        return self.summary_start_template.substitute({"number_of_tables":number_of_tables, "number_of_flows":number_of_flows})

    def _format_summary_end(self):
        return self.summary_end_template.substitute({})

    def _format_summary_table(self, table_no, number_of_priorities, number_of_flows):
        return "\n" + self.summary_table_template.substitute({"table_no":table_no, "number_of_priorities":number_of_priorities, "number_of_flows":number_of_flows})

    def _format_summary_table2(self, table_no, number_of_priorities, number_of_flows):
        return "\n" + self.summary_table2_template.substitute({"table_no":table_no, "number_of_priorities":number_of_priorities, "number_of_flows":number_of_flows})

        
    def _format_table_start(self, table_no):
        return self.table_start_template.substitute({"table_no":table_no})

    def _format_table_end(self):
        return self.TABLE_END

    def _format_priority_start(self, table_no, priority_no):
        return self.priority_start_template.substitute({"table_no":table_no, "priority_no":priority_no})

    def _format_priority_end(self):
        return self.PRIORITY_END

    def _format_priority_block_start(self, table_no, priority_no, block_no):
        return self.priority_block_start_template.substitute({"table_no":table_no, "priority_no":priority_no, "block_no":block_no})

    def _format_priority_block_end(self):
        return self.PRIORITY_BLOCK_END

    def _format_match(self, table_no, priority_no, entry_no, match_exp):
        return self.match_template.substitute({"table_no":table_no, "priority_no":priority_no, \
            "entry_no":entry_no, "match_exp":match_exp})
    
    def _format_instruction_output(self, table_no, priority_no, entry_no, instruction_no, actions):
        return self.instruction_output_template.substitute({"table_no":table_no, "priority_no":priority_no, \
            "entry_no":entry_no, "instruction_no":instruction_no, "actions":actions})

    def _format_instruction_controller(self, table_no, priority_no, entry_no, instruction_no, actions):
        return self.instruction_controller_template.substitute({"table_no":table_no, "priority_no":priority_no, \
            "entry_no":entry_no, "instruction_no":instruction_no, "actions":actions})

    def _format_instruction_resubmit(self, table_no, priority_no, entry_no, instruction_no, actions):
        return self.instruction_resubmit_template.substitute({"table_no":table_no, "priority_no":priority_no, \
            "entry_no":entry_no, "instruction_no":instruction_no, "actions":actions})

    def _format_instruction_drop(self, table_no, priority_no, entry_no, instruction_no, actions):
        return self.instruction_drop_template.substitute({"table_no":table_no, "priority_no":priority_no, \
            "entry_no":entry_no, "instruction_no":instruction_no, "actions":actions})

    def _format_instruction_other(self, table_no, priority_no, entry_no, instruction_no, actions):
        return self.instruction_other_template.substitute({"table_no":table_no, "priority_no":priority_no, \
            "entry_no":entry_no, "instruction_no":instruction_no, "actions":actions})
    
    def _format_entry(self, table_no, priority_no, entry_no, match_exp, instructions, counter=""):
        formatted_match_exp = self._format_match_exp(match_exp)
        formatted_str = self.INDENT_IN_PRIORITY_BLOCK + \
            self._format_match(table_no, priority_no, entry_no, formatted_match_exp) + \
            "\n" + self.INDENT_IN_PRIORITY_BLOCK + \
            self._format_group(self._generate_match_name(table_no, priority_no, entry_no), entry_no)
            
        instruction_no = 0
        self.util.debug_out("TARGET INSTRUCTION: " + str(instructions))
        instruction_set = self._split_instruction(instructions)
        for actions in instruction_set:
            if re.match(r'.* output:.*', actions) is not None or re.match(r'.* ALL.*', actions) is not None or re.match(r'.* NORMAL.*', actions) is not None or re.match(r'.* LOCAL.*', actions) is not None:
                formatted_str += "\n" + self.INDENT_IN_PRIORITY_BLOCK + \
                    self._format_instruction_output(table_no, priority_no, entry_no, instruction_no, actions)
            elif re.match(r'.* CONTROLLER:.*', actions) is not None:
                formatted_str += "\n" + self.INDENT_IN_PRIORITY_BLOCK + \
                    self._format_instruction_controller(table_no, priority_no, entry_no, instruction_no, actions)
            elif re.match(r'.* drop', actions) is not None:
                formatted_str += "\n" + self.INDENT_IN_PRIORITY_BLOCK + \
                    self._format_instruction_drop(table_no, priority_no, entry_no, instruction_no, actions)
            elif re.match(r'.* resubmit:\d+', actions) is not None or re.match(r'.* resubmit\(\d+,%s' % table_no, actions) is not None:
                formatted_str += "\n" + self.INDENT_IN_PRIORITY_BLOCK + \
                    self._format_instruction_resubmit(table_no, priority_no, entry_no, instruction_no, actions)
                self.resubmit_edges.append("\n" + self.INDENT_IN_TABLE \
                    + self._format_instruction_to_resubmit(table_no, priority_no, entry_no, instruction_no))
                self.resubmit_loop = True
            elif self.util.d_push(re.match(r'.* resubmit\(\d*,(\d+)\)', actions)) is not None or self.util.d_push(re.match(r'.* goto_table:(\d+)', actions)) is not None:
                match = self.util.d_pop()
                resubmit_table_no = match.group(1)
                formatted_str += "\n" + self.INDENT_IN_PRIORITY_BLOCK + \
                    self._format_instruction_resubmit(table_no, priority_no, entry_no, instruction_no, actions)
                self.resubmit_tables.append([table_no, resubmit_table_no])
                self.resubmit_edges.append("\n" + self.INDENT_IN_TABLE \
                    + self._format_instruction_to_resubmit_table(\
                        table_no, priority_no, entry_no, instruction_no, resubmit_table_no))
            else:
                formatted_str += "\n" + self.INDENT_IN_PRIORITY_BLOCK + \
                    self._format_instruction_other(table_no, priority_no, entry_no, instruction_no, actions)

            formatted_str += "\n" + self.INDENT_IN_PRIORITY_BLOCK + \
                self._format_match_to_instruction(
                    table_no, priority_no, entry_no, instruction_no)
            formatted_str += "\n" + self.INDENT_IN_PRIORITY_BLOCK + \
                self._format_group(self._generate_instruction_name(table_no, priority_no, entry_no, instruction_no), entry_no)
            instruction_no += 1
        return formatted_str

    def _format_match_exp(self, match_exp):
        formatted_match_exp = ""
        formatted_match_exp = re.sub(r',', ',\\\\n', match_exp)
        return formatted_match_exp

    def _format_match_to_instruction(self, table_no, priority_no, entry_no, instruction_no):
        return self.match_to_instruction_template.substitute( \
            {"table_no":table_no,"priority_no":priority_no, "entry_no":entry_no, \
            "instruction_no":instruction_no})

    def _format_instruction_to_resubmit(self, table_no, priority_no, entry_no, instruction_no):
        if self.output_resubmit_loop:
            return self.instruction_to_resubmit_template.substitute( \
                {"table_no":table_no, "priority_no":priority_no, "entry_no":entry_no, \
                "instruction_no":instruction_no})
        else:
            return ""

    def _format_instruction_to_resubmit_table(self, table_no, priority_no, entry_no, instruction_no, resubmit_table_no):
        return self.instruction_to_resubmit_table_template.substitute( \
            {"table_no":table_no, "priority_no":priority_no, "entry_no":entry_no, \
            "instruction_no":instruction_no, "resubmit_table_no":resubmit_table_no})

    def _format_block_to_block(self, table_no, priority_no, block_no, entry_no, entries_per_block):
        return self.INDENT_IN_PRIORITY + self.block_to_block_template.substitute( \
            {"table_no":table_no, "priority_no":priority_no, "entry_no":entry_no, \
            "old_entry_no":entry_no - (entries_per_block / 2), "instruction_no":"0",\
            "block_no":block_no, "old_block_no":block_no -1})

    def _format_priority_to_priority(self, table_no, priority_no, entry_no, entries_per_block, old_priority_no):
#         if entry_no > (entries_per_block / 2) and entry_no > entries_per_block:
#             return self.INDENT_IN_TABLE + self.PRIORITY_TO_PRIORITY_FORMAT % \
#                 (table_no, old_priority_no, entry_no - (entries_per_block / 2), "0",\
#                 table_no, priority_no, "0",\
#                 table_no, old_priority_no, table_no, priority_no)
#         else:
        return self.INDENT_IN_TABLE + self.priority_to_priority_template.substitute( \
            {"table_no":table_no, "priority_no":priority_no, "entry_no":"0",\
            "old_priority_no":old_priority_no, "old_entry_no":entry_no - 1, "old_instruction_no":"0"})

    def _format_start_to_priority(self, table_no, priority_no):
        return self.INDENT_IN_TABLE + self.start_to_priority_template.substitute({"table_no":table_no, "priority_no":priority_no, "entry_no":"0"})

    def _format_priority_to_end(self, table_no, priority_no, entry_no):
        return self.INDENT_IN_TABLE + self.priority_to_end_template.substitute({"table_no":table_no, "priority_no":priority_no, "entry_no":entry_no, "instruction_no":"0"})

    def _format_resubmit_table(self, table_no, resubmit_table_no):
        return self.INDENT_IN_TABLE + self.resubmit_table_template.substitute({"table_no":table_no, "resubmit_table_no":resubmit_table_no})

    def _format_resubmit_loop(self, table_no):
        if self.output_resubmit_loop:
            if self.resubmit_loop:
                return self.INDENT_IN_TABLE + self.resubmit_template.substitute({"table_no":table_no}) \
                    + "\n" + self.INDENT_IN_TABLE + self.resubmit_to_start_template.substitute({"table_no":table_no})
            else:
                return ""
        else:
            return ""

    def _format_resubmit_edges(self):
        if len(self.resubmit_edges) == 0:
            return ""
        else:
            return "".join(self.resubmit_edges)

    def _format_resubmit_tables(self):
        format_resubmit_tables = []
        resubmit_table_sets = []

        if len(self.resubmit_tables) == 0:
            return ""
#        for table_no, resubmit_table in list(set(self.resubmit_tables)):
        for resubmit_table_set in self.resubmit_tables:
            if not resubmit_table_set in resubmit_table_sets:
                resubmit_table_sets.append(resubmit_table_set)

        for table_no, resubmit_table in resubmit_table_sets:
            if resubmit_table is not None:
                format_resubmit_tables.append(self._format_resubmit_table(table_no, resubmit_table))
        return "\n".join(format_resubmit_tables)

    def _format_table_to_table(self, table_no, old_table_no):
        if self.output_target_tables != []:
            return self.INDENT_IN_GRAPH + self.table_to_table_invisible_template.substitute({"table_no":table_no, "old_table_no":old_table_no})
        else:
            return self.INDENT_IN_GRAPH + self.table_to_table_template.substitute({"table_no":table_no, "old_table_no":old_table_no})
        
    def _format_group(self, entry_name, group_no):
        return self.group_template.substitute({"entry_name":entry_name, "group_no":group_no})

    def _rank_priorities(self, table_no):
        rank_priority = []
        rank_priority.append(self.INDENT_IN_TABLE + '"start%s" -> "%s"' % (table_no, self.ranks_in_table[0]))
        for entry in range(1, len(self.ranks_in_table)):
            rank_priority.append(' -> "%s"' % (self.ranks_in_table[entry]))
        rank_priority.append(' -> "end%s" [color ="transparent"]' % (table_no))
        return "".join(rank_priority)

    def _rank_min(self, rank1):
        return self.INDENT_IN_TABLE + self.RANK_MIN_FORMAT % (rank1)

    def _rank_max(self, rank1):
        return self.INDENT_IN_TABLE + self.RANK_MAX_FORMAT % (rank1)

    def _rank_same(self, rank1, rank2):
        return self.INDENT_IN_TABLE + self.RANK_SAME_FORMAT % (rank1, rank2)

    
    def _split_instruction(self, instructions):
        instruction_set = [""]
        instruction_no = 0
        action_no = 0
        f_no = 0
        for inst in instructions:
            inst = re.sub(r'>', '\\>', inst)
            self.util.debug_out("i_no:  " + str(instruction_no) + ": " + inst)
            instruction_set[instruction_no] += "<f" + str(
                f_no) + "> " + inst + "|"
            if inst.startswith("resubmit") or inst.startswith("goto_table:") or inst.startswith("output:") or inst.startswith("ALL") or inst.startswith("LOCAL") or inst.startswith("NORMAL") or inst.startswith("CONTROLLER:"):
                if action_no < (len(instructions) - 1):
                    instruction_no += 1
                    instruction_set.append("")
                    f_no = 0
            else:
                f_no += 1
            action_no += 1

        for i in range(len(instruction_set)):
            instruction_set[i] = re.sub(r'\|$', '', instruction_set[i])
            self.util.debug_out("INSTRUCTION PARSED: " + instruction_set[i])
        return instruction_set
