#!/usr/bin/env python3
"""Convert DragonX ui.json / ui-dark.json / ui-light.json to TOML format.

Usage:
    python3 scripts/json2toml.py res/themes/ui.json res/themes/ui.toml
    python3 scripts/json2toml.py res/themes/ui-dark.json res/themes/ui-dark.toml
    python3 scripts/json2toml.py res/themes/ui-light.json res/themes/ui-light.toml
    python3 scripts/json2toml.py --all   # converts all three
"""

import json
import sys
import os
import re
from collections import OrderedDict

# Keys that need quoting in TOML because they contain special chars
def needs_quoting(key):
    # TOML bare keys: [A-Za-z0-9_-]+
    return not re.match(r'^[A-Za-z0-9_-]+$', key)

def quote_key(key):
    if needs_quoting(key):
        return f'"{key}"'
    return key

def format_value(val):
    """Format a Python value as a TOML value string."""
    if isinstance(val, bool):
        return "true" if val else "false"
    elif isinstance(val, int):
        return str(val)
    elif isinstance(val, float):
        # Ensure floats always have a decimal point
        s = repr(val)
        if '.' not in s and 'e' not in s and 'E' not in s:
            s += '.0'
        return s
    elif isinstance(val, str):
        # Escape backslashes and quotes
        escaped = val.replace('\\', '\\\\').replace('"', '\\"')
        return f'"{escaped}"'
    elif isinstance(val, list):
        parts = [format_value(v) for v in val]
        return f'[{", ".join(parts)}]'
    else:
        return repr(val)

def is_simple_leaf(obj):
    """Check if an object is a simple leaf that can be an inline table.
    Simple leafs: {"size": X}, {"color": "..."}, {"height": X}, or small
    objects with only primitive values and no nested objects."""
    if not isinstance(obj, dict):
        return False
    for v in obj.values():
        if isinstance(v, (dict, list)):
            return False
    # Keep objects with many keys as sections (threshold: 6 keys)
    return len(obj) <= 6

def is_array_of_objects(val):
    """Check if val is an array of objects (needs [[array.of.tables]])."""
    return isinstance(val, list) and all(isinstance(v, dict) for v in val) and len(val) > 0

def write_inline_table(obj):
    """Write a dict as a TOML inline table."""
    parts = []
    for k, v in obj.items():
        parts.append(f'{quote_key(k)} = {format_value(v)}')
    return '{ ' + ', '.join(parts) + ' }'

def emit_toml(data, lines, prefix='', depth=0):
    """Recursively emit TOML from a parsed JSON dict."""

    # Separate keys into: scalars/arrays, simple-leaf objects, complex objects, array-of-tables
    scalars = []
    inline_leaves = []
    complex_tables = []
    array_tables = []

    for key, val in data.items():
        # Skip _comment keys (we'll handle them differently)
        if key.startswith('_comment'):
            continue

        if isinstance(val, dict):
            if is_simple_leaf(val):
                inline_leaves.append((key, val))
            else:
                complex_tables.append((key, val))
        elif is_array_of_objects(val):
            array_tables.append((key, val))
        else:
            scalars.append((key, val))

    # Emit scalars first
    for key, val in scalars:
        lines.append(f'{quote_key(key)} = {format_value(val)}')

    # Emit inline leaf objects (like { size = 42.0 })
    for key, val in inline_leaves:
        lines.append(f'{quote_key(key)} = {write_inline_table(val)}')

    # Emit complex sub-tables with [section] headers
    for key, val in complex_tables:
        subprefix = f'{prefix}.{key}' if prefix else key
        lines.append('')
        lines.append(f'[{subprefix}]')
        emit_toml(val, lines, subprefix, depth + 1)

    # Emit array-of-tables with [[section]] headers
    for key, val in array_tables:
        subprefix = f'{prefix}.{key}' if prefix else key
        for item in val:
            lines.append('')
            lines.append(f'[[{subprefix}]]')
            if isinstance(item, dict):
                for ik, iv in item.items():
                    if isinstance(iv, dict):
                        # Nested object inside array item — inline table
                        lines.append(f'{quote_key(ik)} = {write_inline_table(iv)}')
                    else:
                        lines.append(f'{quote_key(ik)} = {format_value(iv)}')

def convert_file(input_path, output_path):
    """Convert a JSON theme file to TOML."""
    with open(input_path) as f:
        data = json.load(f, object_pairs_hook=OrderedDict)

    lines = []

    # Add header comments (converted from _comment_* keys)
    for key, val in data.items():
        if key.startswith('_comment') and isinstance(val, str):
            if val:
                lines.append(f'# {val}')
            else:
                lines.append('')

    if lines:
        lines.append('')

    # Emit all non-comment content
    emit_toml(data, lines)

    # Clean up extra blank lines
    output = '\n'.join(lines).strip() + '\n'
    # Collapse 3+ consecutive newlines to 2
    while '\n\n\n' in output:
        output = output.replace('\n\n\n', '\n\n')

    with open(output_path, 'w') as f:
        f.write(output)

    print(f'Converted: {input_path} -> {output_path}')
    print(f'  JSON: {os.path.getsize(input_path):,} bytes')
    print(f'  TOML: {os.path.getsize(output_path):,} bytes')
    print(f'  Reduction: {100 - 100*os.path.getsize(output_path)/os.path.getsize(input_path):.0f}%')

def main():
    if len(sys.argv) == 2 and sys.argv[1] == '--all':
        base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        themes = os.path.join(base, 'res', 'themes')
        for name in ['ui', 'ui-dark', 'ui-light']:
            inp = os.path.join(themes, f'{name}.json')
            out = os.path.join(themes, f'{name}.toml')
            if os.path.exists(inp):
                convert_file(inp, out)
            else:
                print(f'Skipping (not found): {inp}')
    elif len(sys.argv) == 3:
        convert_file(sys.argv[1], sys.argv[2])
    else:
        print(__doc__)
        sys.exit(1)

if __name__ == '__main__':
    main()
