// Extracts TMemo Lines.Strings from example/pq/Config.dfm into a Dart const map.
// Usage:
//   dart run tool/dfm_extract.dart [input_dfm] [output_dart]
// Defaults:
//   input_dfm: example/pq/Config.dfm
//   output_dart: lib/data/pq_config_data.dart

import 'dart:convert';
import 'dart:io';

void main(List<String> args) {
  final inputPath = args.isNotEmpty ? args[0] : 'example/pq/Config.dfm';
  final outputPath = args.length > 1 ? args[1] : 'lib/data/pq_config_data.dart';

  final file = File(inputPath);
  if (!file.existsSync()) {
    stderr.writeln('Input file not found: $inputPath');
    exitCode = 1;
    return;
  }

  final lines = file.readAsLinesSync();
  final data = _parseMemos(lines);
  _writeDart(outputPath, data);
  _printSummary(outputPath, data);
}

Map<String, List<String>> _parseMemos(List<String> lines) {
  final result = <String, List<String>>{};
  String? currentMemo;
  bool inLines = false;

  for (final rawLine in lines) {
    final line = rawLine.trimRight();
    final trimmed = line.trimLeft();

    // Detect TMemo object names.
    if (trimmed.startsWith('object ') && trimmed.contains(': TMemo')) {
      final token = trimmed.split(' ')[1];
      currentMemo = token.contains(':') ? token.split(':').first : token;
      inLines = false;
      continue;
    }

    // Detect start of Lines.Strings block.
    if (trimmed.contains('Lines.Strings')) {
      if (currentMemo == null) continue;
      inLines = true;
      result[currentMemo] = <String>[];
      continue;
    }

    if (!inLines || currentMemo == null) {
      continue;
    }

    // End of block.
    if (trimmed == ')') {
      inLines = false;
      continue;
    }

    // Extract string literal line (may end with "')" on last item).
    final hasClosing = trimmed.endsWith(')');
    final value = _decodeDelphiLiteral(line, stripTrailingParen: hasClosing);
    if (value.isNotEmpty) {
      result[currentMemo]!.add(value);
    }

    // Handle closing parenthesis on same line.
    if (hasClosing) {
      inLines = false;
    }
  }

  return result;
}

String _decodeDelphiLiteral(String line, {required bool stripTrailingParen}) {
  // Remove trailing ')' only when it denotes end-of-block.
  var work = line;
  if (stripTrailingParen && work.trimRight().endsWith(')')) {
    work = work.substring(0, work.lastIndexOf(')'));
  }

  final buffer = StringBuffer();
  var i = 0;
  while (i < work.length) {
    final ch = work[i];
    if (ch == '\'') {
      // Quoted segment.
      i++;
      final start = i;
      while (i < work.length && work[i] != '\'') {
        i++;
      }
      buffer.write(work.substring(start, i));
      i++; // Skip closing quote.
    } else if (ch == '#') {
      // Numeric char code segment.
      i++;
      final start = i;
      while (i < work.length && _isDigit(work.codeUnitAt(i))) {
        i++;
      }
      final code = int.parse(work.substring(start, i));
      buffer.write(String.fromCharCode(code));
    } else {
      i++; // Ignore other characters between tokens.
    }
  }
  return buffer.toString();
}

bool _isDigit(int charCode) => charCode >= 48 && charCode <= 57;

void _writeDart(String outputPath, Map<String, List<String>> data) {
  final buffer = StringBuffer()
    ..writeln('// GENERATED CODE - DO NOT EDIT BY HAND.')
    ..writeln(
      '// Generated by tool/dfm_extract.dart from example/pq/Config.dfm',
    )
    ..writeln('')
    ..writeln('const Map<String, List<String>> pqConfigData = {');

  final sortedKeys = data.keys.toList()..sort();
  for (final key in sortedKeys) {
    final encodedKey = jsonEncode(key);
    final encodedList = jsonEncode(data[key]);
    buffer.writeln('  $encodedKey: $encodedList,');
  }

  buffer.writeln('};');

  File(outputPath)
    ..createSync(recursive: true)
    ..writeAsStringSync(buffer.toString());
}

void _printSummary(String outputPath, Map<String, List<String>> data) {
  stdout.writeln('Wrote $outputPath');
  for (final entry in data.entries) {
    stdout.writeln(' - ${entry.key}: ${entry.value.length} items');
  }
}
