"""
This script generates docs/plugin-meta.lua. It accepts no arguments

It assumes comments look like:
/**
 * Thing
 *
 * @lua@param thing boolean
 * @lua@returns boolean
 * @exposed name
 */
- Do not have any useful info on '/**' and '*/' lines.
- Class members are not allowed to have non-@command lines and commands different from @lua@field

Valid commands are:
1. @exposeenum [dotted.name.in_lua.last_part]
    Define a table with keys of the enum. Values behind those keys aren't
    written on purpose.
    This generates three lines:
     - An type alias of [last_part] to integer,
     - A type description that describes available values of the enum,
     - A global table definition for the num
2. @lua[@command]
    Writes [@command] to the file as a comment, usually this is @class, @param, @return, ...
    @lua@class and @lua@field have special treatment when it comes to generation of spacing new lines
3. @exposed [c2.name]
    Generates a function definition line from the last `@lua@param`s.

Non-command lines of comments are written with a space after '---'
"""
from pathlib import Path

BOILERPLATE = """
---@meta Chatterino2

-- This file is automatically generated from src/controllers/plugins/LuaAPI.hpp by the scripts/make_luals_meta.py script
-- This file is intended to be used with LuaLS (https://luals.github.io/).
-- Add the folder this file is in to "Lua.workspace.library".

c2 = {}
"""

repo_root = Path(__file__).parent.parent
lua_api_file = repo_root / "src" / "controllers" / "plugins" / "LuaAPI.hpp"
lua_meta = repo_root / "docs" / "plugin-meta.lua"

print("Reading from", lua_api_file.relative_to(repo_root))
print("Writing to", lua_meta.relative_to(repo_root))
with lua_api_file.open("r") as f:
    lines = f.read().splitlines()

# Are we in a doc comment?
comment: bool = False

# Last `@lua@param`s seen - for @exposed generation
last_params_names: list[str] = []
# Are we in a `@lua@class` definition? - makes newlines around @lua@class and @lua@field prettier
is_class = False

# The name of the next enum in lua world
expose_next_enum_as: str | None = None
# Name of the current enum in c++ world, used to generate internal typenames for
current_enum_name: str | None = None

with lua_meta.open("w") as out:
    out.write(BOILERPLATE[1:])  # skip the newline after triple quote

    for line in lines:
        line = line.strip()
        if line.startswith("enum class "):
            line = line.removeprefix("enum class ")
            temp = line.split(" ", 2)
            current_enum_name = temp[0]
            if not expose_next_enum_as:
                print(
                    f"Skipping enum {current_enum_name}, there wasn't a @exposeenum command"
                )
                current_enum_name = None
                continue
            current_enum_name = expose_next_enum_as.split(".", 1)[-1]
            out.write("---@alias " + current_enum_name + " integer\n")
            out.write("---@type { ")
            # temp[1] is '{'
            if len(temp) == 2:  # no values on this line
                continue
            line = temp[2]

        if current_enum_name is not None:
            for i, tok in enumerate(line.split(" ")):
                if tok == "};":
                    break
                entry = tok.removesuffix(",")
                if i != 0:
                    out.write(", ")
                out.write(entry + ": " + current_enum_name)
            out.write(" }\n" f"{expose_next_enum_as} = {{}}\n")
            print(f"Wrote enum {expose_next_enum_as} => {current_enum_name}")
            current_enum_name = None
            expose_next_enum_as = None
            continue

        if line.startswith("/**"):
            comment = True
            continue
        elif "*/" in line:
            comment = False
            if not is_class:
                out.write("\n")
            continue
        if not comment:
            continue
        line = line.replace("*", "", 1).lstrip()
        if line == "":
            out.write("---\n")
        elif line.startswith("@exposeenum "):
            expose_next_enum_as = line.split(" ", 1)[1]
        elif line.startswith("@exposed "):
            exp = line.replace("@exposed ", "", 1)
            params = ", ".join(last_params_names)
            out.write(f"function {exp}({params}) end\n")
            print(f"Wrote function {exp}(...)")
            last_params_names = []
        elif line.startswith("@lua"):
            command = line.replace("@lua", "", 1)
            if command.startswith("@param"):
                last_params_names.append(command.split(" ", 2)[1])
            elif command.startswith("@class"):
                print(f"Writing {command}")
                if is_class:
                    out.write("\n")
                is_class = True
            elif not command.startswith("@field"):
                is_class = False

            out.write("---" + command + "\n")
        else:
            if is_class:
                is_class = False
                out.write("\n")

            # note the space difference from the branch above
            out.write("--- " + line + "\n")