#! /usr/bin/env python3
#
# This script pretty-prints timing/utilization reports produced by nextpnr.
# You can use it in your build process like this:
#
#   nextpnr-ecp5 ... --report report.json
#   ./print-report.py < report.json
#
# License: CC0 <https://creativecommons.org/publicdomain/zero/1.0>

import json, sys
from typing import Any, Dict

RED, GREEN, CYAN = "\x1b[31m", "\x1b[32m", "\x1b[36m"
BOLD, RESET = "\x1b[1m", "\x1b[0m"


def print_header(title: str) -> None:
    print(BOLD + GREEN + f"==== {title} ".ljust(60, "=") + RESET)


def print_frequency_report(frequencies: Dict[str, Any]) -> None:
    if not frequencies:
        return

    frequencies = {
        name: (value["achieved"], value["constraint"])
        for name, value in frequencies.items()
    }

    name_width = max(len(name) for name in frequencies.keys())

    print_header("Frequency Report")

    for name, (achieved, constraint) in frequencies.items():
        name_text = CYAN + (name + ":").ljust(name_width + 1) + RESET
        pass_fail_text = (
            f"{GREEN}PASS{RESET}" if achieved >= constraint else f"{RED}FAIL{RESET}"
        )
        print(
            f"  {name_text} {achieved:5.1f} MHz"
            f" ({pass_fail_text} at {constraint:5.1f} MHz)"
        )

    print()


def print_device_utilization(utilization: Dict[str, Any]) -> None:
    utilization = {
        name: (value["used"], value["available"])
        for name, value in utilization.items()
        if value["used"] > 0
    }

    percentages = {
        name: f"{value[0] / value[1] * 100:5.1f}" for name, value in utilization.items()
    }

    name_width = max(len(name) for name in utilization.keys())
    percent_width = max(len(percent) for percent in percentages.values())

    print_header("Device Utilization")

    for name, (used, available) in utilization.items():
        name_text = CYAN + (name + ":").ljust(name_width + 1) + RESET
        percent_text = f"{percentages[name].rjust(percent_width)}%"
        print(
            f"  {name_text} {str(used).rjust(5)} / {str(available).rjust(5)}",
            f"({GREEN if used <= available else RED}{percent_text}{RESET})",
        )

    print()


def print_report(report: Dict[str, Any]) -> None:
    print_frequency_report(report.get("fmax"))
    print_device_utilization(report.get("utilization"))


if __name__ == "__main__":
    print()
    print_report(json.load(sys.stdin))