import argparse
import os
import pathlib

from util import reduced_build_config_parser
from util.ort_format_model.operator_type_usage_processors import GloballyAllowedTypesOpTypeImplFilter


def generate_docs(output_file, required_ops, op_type_impl_filter):
    with open(output_file, "w") as out:
        out.write("# ONNX Runtime Mobile Pre-Built Package Operator and Type Support\n\n")

        # Description
        out.write("## Supported operators and types\n\n")
        out.write(
            "The supported operators and types are based on what is required to support float32 and quantized "
            "versions of popular models. The full list of input models used to determine this list is available "
            "[here](https://github.com/microsoft/onnxruntime/blob/main/tools/ci_build/github/android/mobile_package"
            ".required_operators.readme.txt)"
        )
        out.write("\n\n")

        # Globally supported types
        out.write("## Supported data input types\n\n")
        assert op_type_impl_filter.__class__ is GloballyAllowedTypesOpTypeImplFilter
        global_types = op_type_impl_filter.global_type_list()
        for type in sorted(global_types):
            out.write(f"  - {type}\n")
        out.write("\n")
        out.write("NOTE: Operators used to manipulate dimensions and indices will support int32 and int64.\n\n")

        domain_op_opsets = []
        for domain in sorted(required_ops.keys()):
            op_opsets = {}
            domain_op_opsets.append((domain, op_opsets))
            for opset in sorted(required_ops[domain].keys()):
                str_opset = str(opset)
                for op in required_ops[domain][opset]:
                    op_with_domain = f"{domain}:{op}"
                    if op_with_domain not in op_opsets:
                        op_opsets[op_with_domain] = []

                    op_opsets[op_with_domain].append(str_opset)

        out.write("## Supported Operators\n\n")
        out.write("|Operator|Opsets|\n")
        out.write("|--------|------|\n")
        for domain, op_opsets in domain_op_opsets:
            out.write(f"|**{domain}**||\n")
            for op in sorted(op_opsets.keys()):
                out.write("|{}|{}|\n".format(op, ", ".join(op_opsets[op])))
            out.write("|||\n")


def main():
    script_dir = os.path.dirname(os.path.realpath(__file__))

    parser = argparse.ArgumentParser(
        description="ONNX Runtime Mobile Pre-Built Package Operator and Type Support Documentation Generator",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )

    default_config_path = pathlib.Path(
        os.path.join(script_dir, "../ci_build/github/android/mobile_package.required_operators.config")
    ).resolve()

    default_output_path = pathlib.Path(
        os.path.join(script_dir, "../../docs/ORTMobilePackageOperatorTypeSupport.md")
    ).resolve()

    parser.add_argument(
        "--config_path",
        help="Path to build configuration used to generate package.",
        required=False,
        type=pathlib.Path,
        default=default_config_path,
    )

    parser.add_argument(
        "--output_path",
        help="output markdown file path",
        required=False,
        type=pathlib.Path,
        default=default_output_path,
    )

    args = parser.parse_args()
    config_file = args.config_path.resolve(strict=True)  # must exist so strict=True
    output_path = args.output_path.resolve()

    enable_type_reduction = True
    required_ops, op_type_impl_filter = reduced_build_config_parser.parse_config(config_file, enable_type_reduction)
    generate_docs(output_path, required_ops, op_type_impl_filter)


if __name__ == "__main__":
    main()
