"""
A module for generating configuration data for struct fields.
"""

# built-in
from typing import Any

# internal
from ifgen.svd.model.peripheral import Cluster, Register, RegisterData

StructMap = dict[str, Any]
StructField = dict[str, Any]
DEFAULT_STRUCT = {
    "stream": False,
    "codec": False,
    "methods": False,
    "unit_test": False,
}


def handle_cluster(
    cluster: Cluster, structs: StructMap
) -> tuple[int, StructField]:
    """Handle a cluster element."""

    # Register a struct for this cluster. Should we use a namespace for this?
    cluster_struct: dict[str, Any] = cluster.handle_description()
    size, cluster_struct["fields"] = struct_fields(cluster.children, structs)
    cluster_struct["expected_size"] = size
    cluster_struct.update(DEFAULT_STRUCT)

    raw_name = cluster.name.replace("[%s]", "")

    cluster_name = cluster.raw_data.get(
        "headerStructName", f"{raw_name}_instance"
    )
    structs[cluster_name] = cluster_struct

    # This needs to be an array element somehow. Use a namespace?
    array_dim = int(cluster.raw_data.get("dim", 1))
    size *= array_dim
    result: StructField = {
        "name": raw_name,
        "type": cluster_name,
        "expected_size": size,
    }
    if array_dim > 1:
        result["array_length"] = array_dim

    cluster.handle_description(result)
    return size, result


def handle_register(register: Register) -> tuple[int, StructField]:
    """Handle a register entry."""

    array_dim = int(register.raw_data.get("dim", 1))

    size = register.size * array_dim
    data = {
        "name": register.name.replace("[%s]", ""),
        "type": register.c_type,
        "expected_size": size,
    }
    if array_dim > 1:
        data["array_length"] = array_dim

    # Add expected offset.

    access = register.access
    if access == "read-only":
        data["const"] = True

    notes = [access]

    register.handle_description(data, prefix=f"({', '.join(notes)}) ")
    return size, data


def struct_fields(
    registers: RegisterData, structs: StructMap, size: int = None
) -> tuple[int, list[StructField]]:
    """Generate data for struct fields."""

    fields = []

    if size is None:
        size = 0

    for item in registers:
        inst_size, field = (
            handle_cluster(item, structs)
            if isinstance(item, Cluster)
            else handle_register(item)
        )
        fields.append(field)
        size += inst_size

    return size, fields
