#!/usr/bin/env python
#
# Generate Rust code given Decimals and formatting strings
#

import sys
import textwrap
from decimal import *

# Decimal strings to the testing formats
INPUTS = {
    "1": [
        "{}",
        "{:.1}",
        "{:.4}",
        "{:4.1}",
        "{:>4.1}",
        "{:<4.1}",
        "{:+05.1}",
    ],
    "123456": [
        "{}",
        "{:+05.1}",
        "{:.1}",
        "{:.4}",
        "{:4.1}",
        "{:>4.3}",
        "{:>4.4}",
        "{:<4.1}",
    ],
    "9999999": [
        "{:.4}",
        "{:.8}",
    ],
    "19073.97235939614856": [
        "{}",
        "{:+05.7}",
        "{:.3}",
        "{:0.4}",
        "{:4.1}",
        "{:>8.3}",
        "{:>8.4}",
        "{:<8.1}",
    ],
    "491326e-12": [
        "{}",
        "{:+015.7}",
        "{:.3}",
        "{:0.4}",
        "{:4.1}",
        "{:>8.3}",
        "{:>8.4}",
        "{:<8.1}",
    ],
}


def main():
    from argparse import ArgumentParser, FileType

    parser = ArgumentParser(description="Create test modules for src/impl_fmt.rs")
    parser.add_argument("-o", "--output", nargs="?", default="-", type=FileType("w"))
    args = parser.parse_args()

    for dec_str, formats in INPUTS.items():
        src = make_test_module_src(dec_str, formats)
        args.output.write(src)

    return 0


def make_test_module_src(dec_str: Decimal, formats: list[str]) -> str:
    """
    Return Rust module source which tests given decimal against given formats
    """
    dec = Decimal(dec_str)
    mod_name = "dec_%s" % gen_name_from_dec(dec_str)

    formats_and_outputs = [
        (fmt, gen_name_from_fmt(fmt), fmt.format(dec)) for fmt in formats
    ]

    max_len_name_and_fmt = max(
        len(fmt) + len(name) for fmt, name, _ in formats_and_outputs
    )

    lines = []
    for fmt, name, value in formats_and_outputs:
        spacer = " " * (max_len_name_and_fmt - (len(fmt) + len(name)) + 2)
        lines.append(f'impl_case!(fmt_{name}:{spacer}"{fmt}" => "{value}");')

    body = textwrap.indent("\n".join(lines), "    ")
    text = textwrap.dedent(
        f"""
        mod {mod_name} {{
            use super::*;

            fn test_input() -> BigDecimal {{
                "{dec_str}".parse().unwrap()
            }}

        %s
        }}
        """
    )
    return text % body


def gen_name_from_dec(dec_str: str) -> str:
    """Given decimal input string return valid function/module name"""
    return "".join(map_fmt_char_to_name_char(c) for c in dec_str)


def gen_name_from_fmt(fmt: str) -> str:
    """Given decimal input string return valid function/module name"""
    if fmt == "{}":
        return "default"
    else:
        assert fmt.startswith("{:") and fmt.endswith("}")
        return "".join(map_fmt_char_to_name_char(c) for c in fmt[2:-1])


def map_fmt_char_to_name_char(c: str) -> str:
    """Turn special characters into identifiers"""
    match c:
        case ".":
            return "d"
        case "+":
            return "p"
        case "-":
            return "n"
        case "<":
            return "l"
        case ">":
            return "r"
        case ":":
            return "c"
        case _:
            return str(c)


if __name__ == "__main__":
    sys.exit(main())
