#! /usr/bin/env python3

#
#  Copyright 2016 - 2025 gary@drinkingtea.net
#
#  This Source Code Form is subject to the terms of the Mozilla Public
#  License, v. 2.0. If a copy of the MPL was not distributed with this
#  file, You can obtain one at http://mozilla.org/MPL/2.0/.
#

import argparse
import os
import json
import pathlib
import sys


tool_name = 'file-to-cpp'


def write_txt_file(path: str, txt: str):
    with open(path, "w") as f:
        f.write(txt)
    print(f'Wrote {path}')


def file_to_hex(path: str, line_prefix: str) -> tuple[str, int]:
    try:
        with open(path, 'rb') as f:
            out = line_prefix
            data = f.read()
            i = 1
            for b in data:
                out += f"{b:#0{4}x},"
                out += '\n\t' if i % 10 == 0 else ' '
                i += 1
            return out[:-1], len(data)
    except FileNotFoundError:
        print(f'{tool_name}: included file not found: {path}', file=sys.stderr)
        sys.exit(1)


def file_to_cpp(path: str, cpp_name: str) -> tuple[str, str]:
    cpp = ''
    hpp = ''
    data, data_len = file_to_hex(path, "\t")
    cpp += f'\nstatic const ox::Array<uint8_t, {data_len}> {cpp_name}Data {{\n{data}\n}};\n'
    cpp += f'\nox::SpanView<uint8_t> {cpp_name}() noexcept {{ return {cpp_name}Data; }}\n'
    hpp += f'\n[[nodiscard]]\nox::SpanView<uint8_t> {cpp_name}() noexcept;\n'
    return cpp, hpp


def proc_rsrc_file(rsrc_path: str):# Open and read the JSON file
    with open(rsrc_path, 'r') as file:
        data = json.load(file)
    base_path = pathlib.Path(rsrc_path).parent.absolute()
    all_files = data['all_files'] if 'all_files' in data else None
    namespace = data['namespace'] if 'namespace' in data else ''
    if len(namespace) > 0:
        push_ns = f'namespace {namespace} {{\n'
        pop_ns = '\n}\n'
    else:
        push_ns = ''
        pop_ns = ''
    cpp = '// Generated\n\n#include <ox/std/array.hpp>\n'
    cpp += '#include <ox/std/span.hpp>\n\n'
    hpp = '// Generated\n\n#include <ox/std/span.hpp>\n\n'
    cpp += push_ns
    hpp += push_ns
    all_files_func_decl = ''
    all_files_func = ''
    if all_files is not None:
        all_files_func_decl += f'\n[[nodiscard]]\nox::Vector<ox::SpanView<uint8_t>> {all_files}() noexcept;\n'
        all_files_func += f'\nox::Vector<ox::SpanView<uint8_t>> {all_files}() noexcept {{\n\treturn {{\n'
    for f in data['files']:
        if 'path' not in f:
            print(f'{tool_name}: src file path missing', file=sys.stderr)
            sys.exit(1)
        path = f['path']
        if 'cpp_name' not in f:
            print(f'{tool_name}: var name missing', file=sys.stderr)
            sys.exit(1)
        cpp_name = f['cpp_name']
        c, h = file_to_cpp(os.path.join(base_path, path), cpp_name)
        cpp += c
        if len(h) > 0:
            hpp += h
        if all_files is not None:
            all_files_func += f'\t\t{cpp_name}(),\n'
    if all_files is not None:
        cpp += all_files_func + '\t};\n}\n'
        hpp += all_files_func_decl
    cpp += pop_ns
    hpp += pop_ns
    write_txt_file(os.path.join(base_path, data['cpp']), cpp)
    if 'hpp' in data:
        write_txt_file(os.path.join(base_path, data['hpp']), hpp)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument('--rsrc', help='path to file', required=True)
    args = parser.parse_args()
    proc_rsrc_file(args.rsrc)
    return 0


if __name__ == '__main__':
    try:
        err = main()
        sys.exit(err)
    except KeyboardInterrupt:
        sys.exit(1)
