#!/usr/bin/env python3
import importlib
import os
import re
from pathlib import Path

generator_path = os.path.dirname(os.path.realpath(__file__))
include_pattern = re.compile(r'CM_ "IMPORT (.*?)";\n')


def _read_dbc(src_dir: str, filename: str, extra_files: dict[str, str] | None = None) -> str:
  if extra_files and filename in extra_files:
    return extra_files[filename]
  with open(os.path.join(src_dir, filename), encoding='utf-8') as file_in:
    return file_in.read()


def _create_dbc_content(src_dir: str, filename: str, extra_files: dict[str, str] | None = None) -> str:
  dbc_file_in = _read_dbc(src_dir, filename, extra_files)
  includes = include_pattern.findall(dbc_file_in)

  parts = ['CM_ "AUTOGENERATED FILE, DO NOT EDIT";\n']
  for include_filename in includes:
    parts.append(f'\n\nCM_ "Imported file {include_filename} starts here";\n')
    parts.append(_read_dbc(src_dir, include_filename, extra_files))

  parts.append(f'\nCM_ "{filename} starts here";\n')
  core_dbc = include_pattern.sub('', dbc_file_in)
  parts.append(core_dbc)

  return ''.join(parts)


def _collect_script_outputs() -> dict[str, dict[str, str]]:
  """Import and call generate() from each sub-generator script.
  Returns {dir_name: {filename: content}}."""
  outputs: dict[str, dict[str, str]] = {}

  for py_file in sorted(Path(generator_path).rglob("*.py")):
    if py_file.name.startswith("test_") or py_file.name == "generator.py":
      continue

    dir_name = py_file.parent.name
    module_name = f"opendbc.dbc.generator.{dir_name}.{py_file.stem}"
    mod = importlib.import_module(module_name)
    if hasattr(mod, 'generate'):
      outputs.setdefault(dir_name, {}).update(mod.generate())

  return outputs


def generate_all() -> dict[str, str]:
  """Generate all DBC content in memory. Returns {name: content} where name has no .dbc extension."""
  script_outputs = _collect_script_outputs()

  result = {}
  for src_dir, _, filenames in os.walk(generator_path):
    if src_dir == generator_path:
      continue

    dir_name = os.path.basename(src_dir)
    extra = script_outputs.get(dir_name, {})

    # all non-_ .dbc files: on-disk templates + script-generated
    all_dbc_files = {f for f in filenames if f.endswith('.dbc') and not f.startswith('_')}
    all_dbc_files |= {f for f in extra if not f.startswith('_')}

    for filename in sorted(all_dbc_files):
      output_name = filename.replace('.dbc', '_generated')
      content = _create_dbc_content(src_dir, filename, extra)
      result[output_name] = content

  return result


def create_all(output_path: str):
  """Generate all DBC files and write them to output_path (for backward compatibility)."""
  import glob
  generated_suffix = '_generated.dbc'

  # clear out old generated DBCs
  for f in glob.glob(os.path.join(output_path, f"*{generated_suffix}")):
    os.remove(f)

  for name, content in generate_all().items():
    with open(os.path.join(output_path, name + '.dbc'), 'w', encoding='utf-8') as f:
      f.write(content)


if __name__ == "__main__":
  opendbc_root = os.path.join(generator_path, '../')
  create_all(opendbc_root)
