#=========================================================================
# YosysTranslator.py
#=========================================================================
# Author : Peitian Pan
# Date   : June 8, 2019
"""Provide yosys-compatible SystemVerilog translator."""

from pymtl3.passes.backends.generic import RTLIRTranslator
from pymtl3.passes.backends.sverilog.translation.SVTranslator import mk_SVTranslator

from .behavioral import YosysBehavioralTranslator as Yosys_BTranslator
from .structural import YosysStructuralTranslator as Yosys_STranslator

SVTranslator = mk_SVTranslator( RTLIRTranslator, Yosys_STranslator, Yosys_BTranslator )

class YosysTranslator( SVTranslator ):

  def set_header( s ):
      s.header = \
"""\
//-------------------------------------------------------------------------
// {name}.sv
//-------------------------------------------------------------------------
// This file is generated by PyMTL yosys-SystemVerilog translation pass.

"""

  def rtlir_tr_src_layout( s, hierarchy ):
    s.set_header()
    name = s._top_module_full_name
    ret = s.header.format( **locals() )

    # Add component sources
    ret += hierarchy.component_src
    return ret

  def rtlir_tr_component( s, behavioral, structural ):

    template =\
"""\
// PyMTL Component {component_name} Definition
// {optional_full_name}At {file_info}
module {module_name}
(
{ports});
{body}
endmodule
"""
    component_name = structural.component_name
    file_info = structural.component_file_info
    ports_template = "{port_decls}{ifc_decls}"
    full_name = structural.component_full_name
    module_name = structural.component_unique_name

    if full_name != module_name:
      optional_full_name = f"Full name: {full_name}\n// "
    else:
      optional_full_name = ""

    port_dct = structural.decl_ports
    structural.p_port_decls = port_dct["port_decls"]
    structural.p_wire_decls = port_dct["wire_decls"]
    structural.p_connections = port_dct["connections"]

    ifc_dct = structural.decl_ifcs
    structural.i_port_decls = ifc_dct["port_decls"]
    structural.i_wire_decls = ifc_dct["wire_decls"]
    structural.i_connections = ifc_dct["connections"]

    subcomp_dct = structural.decl_subcomps
    structural.c_port_decls = subcomp_dct["port_decls"]
    structural.c_wire_decls = subcomp_dct["wire_decls"]
    structural.c_connections = subcomp_dct["connections"]

    # Assemble ports and interfaces
    port_decls = s.get_pretty(structural, 'p_port_decls', False)
    ifc_decls = s.get_pretty(structural, 'i_port_decls', False)
    if port_decls or ifc_decls:
      if port_decls and ifc_decls:
        port_decls += ',\n'
      ifc_decls += '\n'
    ports = ports_template.format(**locals())

    # Assemble body of module definition

    # Begin with port_wires
    p_port_wires = s.get_pretty(structural, "p_wire_decls", False)
    i_port_wires = s.get_pretty(structural, "i_wire_decls", False)

    if p_port_wires or i_port_wires:
      if p_port_wires and i_port_wires:
        p_port_wires += "\n"
      i_port_wires += "\n"
    port_wires = p_port_wires + i_port_wires
    if port_wires:
      port_wires = \
          "  // Struct/Array ports in the form of wires\n" \
          + port_wires

    body = port_wires

    # Add wire declarations
    wire_decls = s.get_pretty(structural, "decl_wires")
    if wire_decls:
      wire_decls = "  // Wire declarations\n" + wire_decls
    if body and wire_decls:
      wire_decls = "\n" + wire_decls
    body += wire_decls

    # Add wires for sub-component struct/array ports
    subcomp_wires = s.get_pretty(structural, "c_wire_decls")
    if subcomp_wires:
      subcomp_wires = \
          "  // Struct/Array ports of sub-components in the form of wires\n" \
          + subcomp_wires
    if body and subcomp_wires:
      subcomp_wires = "\n" + subcomp_wires
    body += subcomp_wires

    # Add sub-component declarations
    subcomp_ports = s.get_pretty(structural, "c_port_decls")
    if subcomp_ports:
      subcomp_ports = "  // Sub-component declarations\n" + subcomp_ports
    if body and subcomp_ports:
      subcomp_ports = "\n" + subcomp_ports
    body += subcomp_ports

    # Add connections between wires and sub-component struct/array ports
    subcomp_conns = s.get_pretty(structural, "c_connections")
    if subcomp_conns:
      subcomp_conns = "  // Connect struct/array ports and their wire forms\n" \
                      + subcomp_conns
    if body and subcomp_conns:
      subcomp_conns = "\n" + subcomp_conns
    body += subcomp_conns

    # Add temporary wire definitions
    tmpvar_decls = s.get_pretty(behavioral, "decl_tmpvars")
    if tmpvar_decls:
      tmpvar_decls = "  // Temporary wire definitions\n" + tmpvar_decls
    if body and tmpvar_decls:
      tmpvar_decls = "\n" + tmpvar_decls
    body += tmpvar_decls

    # Add procedural blocks
    upblk_decls = s.get_pretty(behavioral, "upblk_decls")
    body += upblk_decls

    # Add connections
    p_conns = s.get_pretty(structural, "p_connections", False)
    i_conns = s.get_pretty(structural, "i_connections", False)

    if p_conns or i_conns:
      if p_conns and i_conns:
        p_conns += "\n"
      i_conns += "\n"
    port_connections = p_conns + i_conns
    connections = port_connections \
                + s.get_pretty(structural, "connections")
    if connections:
      connections = "  // Connections\n" + connections
    if (body and connections) or (not body and connections):
      connections = '\n' + connections
    body += connections

    s._top_module_name = structural.component_name
    s._top_module_full_name = module_name

    # Fill in the template and return
    return template.format( **locals() )
