/**
 * DSL Parser & Serializer — mirrors FaceParser.kt
 * Supports @var declarations for computed variables.
 */

function tokenize(line) {
  const tokens = [];
  let i = 0;
  while (i < line.length) {
    if (line[i] === ' ' || line[i] === '\t') { i++; continue; }
    if (line[i] === '"') {
      const end = line.indexOf('"', i + 1);
      if (end < 0) { tokens.push('"' + line.substring(i + 1) + '"'); i = line.length; }
      else { tokens.push('"' + line.substring(i + 1, end) + '"'); i = end + 1; }
    } else {
      const start = i;
      let depth = 0;
      while (i < line.length && !(depth === 0 && (line[i] === ' ' || line[i] === '\t'))) {
        if (line[i] === '(') depth++;
        else if (line[i] === ')') depth = Math.max(0, depth - 1);
        i++;
      }
      tokens.push(line.substring(start, i));
    }
  }
  return tokens;
}

function parseElement(line) {
  const tokens = tokenize(line);
  if (!tokens.length) return null;
  const type = tokens[0];
  const args = [], props = {};
  for (let k = 1; k < tokens.length; k++) {
    const t = tokens[k];
    if (t === 'bold' || t === 'italic') { props[t] = 'true'; }
    else if (t.includes(':') && !t.startsWith('#') && !t.startsWith('$')) {
      const ci = t.indexOf(':');
      props[t.substring(0, ci)] = t.substring(ci + 1);
    } else { args.push(t); }
  }
  return { type, args, props };
}

function parseDSL(source) {
  let name = 'Custom', author = '', bg = '#000000';
  const vars = {};
  const elements = [];
  const lines = source.split('\n').map(l => l.trim());
  let i = 0;
  while (i < lines.length) {
    const line = lines[i];
    if (!line || line.startsWith('#')) { i++; continue; }
    if (line.startsWith('@name ')) { name = extractQuoted(line.substring(6).trim()); i++; continue; }
    if (line.startsWith('@author ')) { author = extractQuoted(line.substring(8).trim()); i++; continue; }
    if (line.startsWith('@var ')) {
      const m = line.match(/^@var\s+(\$\w+)\s*=\s*(.+)$/);
      if (m) vars[m[1]] = m[2].trim();
      i++; continue;
    }
    if (line.startsWith('bg ')) { bg = line.substring(3).trim(); i++; continue; }
    if (line.startsWith('group ')) {
      const [node, nextI] = parseGroup(lines, i);
      elements.push(node);
      i = nextI; continue;
    }
    if (line.startsWith('if ')) {
      const [node, nextI] = parseConditional(lines, i);
      elements.push(node);
      i = nextI; continue;
    }
    if (line.startsWith('for ')) {
      const [node, nextI] = parseForLoop(lines, i);
      elements.push(node);
      i = nextI; continue;
    }
    const el = parseElement(line);
    if (el) elements.push(el);
    i++;
  }
  return { name, author, bg, vars, elements };
}

function parseConditional(lines, startIndex) {
  const condLine = lines[startIndex];
  const condition = condLine.replace(/^if\s+/, '').replace(/\s*\{\s*$/, '').trim();
  const children = [];
  let i = startIndex + 1;
  while (i < lines.length) {
    const line = lines[i].trim();
    if (line === '}') return [{ condition, children, isConditional: true }, i + 1];
    if (!line || line.startsWith('#')) { i++; continue; }
    if (line.startsWith('group ')) {
      const [nested, nextI] = parseGroup(lines, i);
      children.push(nested);
      i = nextI; continue;
    }
    if (line.startsWith('if ')) {
      const [nested, nextI] = parseConditional(lines, i);
      children.push(nested);
      i = nextI; continue;
    }
    if (line.startsWith('for ')) {
      const [nested, nextI] = parseForLoop(lines, i);
      children.push(nested);
      i = nextI; continue;
    }
    const el = parseElement(line);
    if (el) children.push(el);
    i++;
  }
  return [{ condition, children, isConditional: true }, i];
}

function parseGroup(lines, startIndex) {
  const groupLine = lines[startIndex];
  const name = groupLine.replace(/^group\s+/, '').replace(/\s*\{\s*$/, '').trim();
  const children = [];
  let i = startIndex + 1;
  while (i < lines.length) {
    const line = lines[i].trim();
    if (line === '}') return [{ isGroup: true, name, children }, i + 1];
    if (!line || line.startsWith('#')) { i++; continue; }
    if (line.startsWith('group ')) {
      const [nested, nextI] = parseGroup(lines, i);
      children.push(nested);
      i = nextI; continue;
    }
    if (line.startsWith('if ')) {
      const [nested, nextI] = parseConditional(lines, i);
      children.push(nested);
      i = nextI; continue;
    }
    if (line.startsWith('for ')) {
      const [nested, nextI] = parseForLoop(lines, i);
      children.push(nested);
      i = nextI; continue;
    }
    const el = parseElement(line);
    if (el) children.push(el);
    i++;
  }
  return [{ isGroup: true, name, children }, i];
}

function parseForLoop(lines, startIndex) {
  const header = lines[startIndex].replace(/^for\s+/, '').replace(/\s*\{\s*$/, '').trim();
  const tokens = tokenize(header);
  const varName = tokens[0] || '$i';
  const from = tokens[1] || '0';
  const to = tokens[2] || '0';
  const step = (tokens.length >= 5 && tokens[3] === 'step') ? tokens[4] : '1';
  const children = [];
  let i = startIndex + 1;
  while (i < lines.length) {
    const line = lines[i].trim();
    if (line === '}') return [{ isForLoop: true, varName, from, to, step, children }, i + 1];
    if (!line || line.startsWith('#')) { i++; continue; }
    if (line.startsWith('group ')) { const [n, ni] = parseGroup(lines, i); children.push(n); i = ni; continue; }
    if (line.startsWith('if ')) { const [n, ni] = parseConditional(lines, i); children.push(n); i = ni; continue; }
    if (line.startsWith('for ')) { const [n, ni] = parseForLoop(lines, i); children.push(n); i = ni; continue; }
    const el = parseElement(line);
    if (el) children.push(el);
    i++;
  }
  return [{ isForLoop: true, varName, from, to, step, children }, i];
}

function extractQuoted(s) {
  if (s.startsWith('"') && s.endsWith('"')) return s.slice(1, -1);
  return s;
}

function serializeFace(face) {
  let s = `@name "${face.name}"\n`;
  if (face.author) s += `@author "${face.author}"\n`;
  s += `bg ${face.bg}\n`;
  // Serialize @var declarations
  if (face.vars && Object.keys(face.vars).length > 0) {
    for (const [name, expr] of Object.entries(face.vars)) {
      s += `@var ${name} = ${expr}\n`;
    }
  }
  s += '\n';
  for (const el of face.elements) s += serializeNode(el, '');
  return s.trimEnd();
}

function serializeNode(node, indent) {
  if (node.isGroup) {
    let s = `${indent}group ${node.name} {\n`;
    for (const c of node.children || []) s += serializeNode(c, indent + '  ');
    s += `${indent}}\n`;
    return s;
  }
  if (node.isConditional) {
    let s = `${indent}if ${node.condition} {\n`;
    for (const c of node.children) s += serializeNode(c, indent + '  ');
    s += `${indent}}\n`;
    return s;
  }
  if (node.isForLoop) {
    const stepPart = node.step !== '1' ? ' step ' + node.step : '';
    let s = `${indent}for ${node.varName} ${node.from} ${node.to}${stepPart} {\n`;
    for (const c of node.children || []) s += serializeNode(c, indent + '  ');
    s += `${indent}}\n`;
    return s;
  }
  let s = `${indent}${node.type}`;
  for (const a of node.args) s += ` ${a}`;
  for (const [k, v] of Object.entries(node.props)) {
    if (k === 'bold' || k === 'italic') s += ` ${k}`;
    else s += ` ${k}:${v}`;
  }
  return s + '\n';
}
