@@[target("rust")]

// Output Block Parser — Frame state machine.
//
// Consumes exhaustive token stream from OutputBlockLexer.
// Every token is either emitted as-is or transformed.
//
// Since the lexer covers every byte, the parser outputs exactly
// the same text if no transformations apply.
//
// Lua mode (mode=1):
//   IF TEXT LBRACE → "if" TEXT "then"
//   RBRACE ELSE LBRACE → "else"
//   RBRACE ELSE LBRACE IF TEXT LBRACE → "elseif" TEXT "then"
//   WHILE TEXT LBRACE → "while" TEXT "do"
//   RBRACE (block close) → "end"
//   RETURN → emit + mark terminal (skip subsequent non-comment tokens)

@@system OutputBlockParserFsm {
    interface:
        do_parse()

    machine:
        $Init {
            do_parse() { -> $Parsing }
        }

        $Parsing {
            $>() {
                let bytes = &self.bytes;
                let n = self.token_kinds.len();
                let mut ti: usize = 0;
                let mut block_depth: i32 = 0;
                let mut after_return = false;

                while ti < n {
                    let kind = self.token_kinds[ti];
                    let start = self.token_starts[ti];
                    let end = self.token_ends[ti];
                    let text = String::from_utf8_lossy(&bytes[start..end]).to_string();

                    // ---- RBRACE: check for else/elseif patterns ----
                    if kind == 7 && block_depth > 0 {
                        // Look ahead past whitespace/newline for ELSE
                        let mut scan = ti + 1;
                        while scan < n && (self.token_kinds[scan] == 10 || self.token_kinds[scan] == 11) {
                            let st = &bytes[self.token_starts[scan]..self.token_ends[scan]];
                            if st.iter().all(|&b| b == b' ' || b == b'\t' || b == b'\n') {
                                scan += 1;
                            } else {
                                break;
                            }
                        }

                        if scan < n && self.token_kinds[scan] == 2 {
                            // RBRACE ... ELSEIF — look for condition then LBRACE
                            let elseif_ti = scan;
                            scan += 1;
                            let mut cond = String::new();
                            while scan < n && self.token_kinds[scan] != 6 && self.token_kinds[scan] != 10 {
                                let s = self.token_starts[scan];
                                let e = self.token_ends[scan];
                                cond.push_str(&String::from_utf8_lossy(&bytes[s..e]));
                                scan += 1;
                            }
                            if scan < n && self.token_kinds[scan] == 6 {
                                if self.mode == 1 {
                                    self.result.push_str("elseif");
                                    self.result.push_str(cond.trim_end());
                                    self.result.push_str(" then");
                                }
                                ti = scan + 1;
                                after_return = false;
                                continue;
                            }
                        }

                        if scan < n && self.token_kinds[scan] == 3 {
                            // RBRACE ... ELSE — look for LBRACE after else
                            let else_ti = scan;
                            scan += 1;
                            while scan < n && (self.token_kinds[scan] == 10 || self.token_kinds[scan] == 11) {
                                let st = &bytes[self.token_starts[scan]..self.token_ends[scan]];
                                if st.iter().all(|&b| b == b' ' || b == b'\t' || b == b'\n') { scan += 1; } else { break; }
                            }

                            if scan < n && self.token_kinds[scan] == 6 {
                                let lbrace_ti = scan;
                                // Check for IF after LBRACE (elseif pattern)
                                scan += 1;
                                while scan < n && (self.token_kinds[scan] == 10 || self.token_kinds[scan] == 11) {
                                    let st = &bytes[self.token_starts[scan]..self.token_ends[scan]];
                                    if st.iter().all(|&b| b == b' ' || b == b'\t' || b == b'\n') { scan += 1; } else { break; }
                                }

                                if scan < n && self.token_kinds[scan] == 1 {
                                    // RBRACE ELSE LBRACE IF ... LBRACE → elseif
                                    let if_ti = scan;
                                    // Collect condition tokens until next LBRACE
                                    scan += 1;
                                    let mut cond = String::new();
                                    while scan < n && self.token_kinds[scan] != 6 {
                                        let s = self.token_starts[scan];
                                        let e = self.token_ends[scan];
                                        cond.push_str(&String::from_utf8_lossy(&bytes[s..e]));
                                        scan += 1;
                                    }
                                    if scan < n && self.token_kinds[scan] == 6 {
                                        // Found the full elseif pattern
                                        if self.mode == 1 {
                                            self.result.push_str("elseif");
                                            self.result.push_str(cond.trim_end());
                                            self.result.push_str(" then");
                                        }
                                        ti = scan + 1;
                                        after_return = false;
                                        continue;
                                    }
                                }

                                // RBRACE ELSE LBRACE → else
                                if self.mode == 1 {
                                    self.result.push_str("else");
                                }
                                ti = lbrace_ti + 1;
                                after_return = false;
                                continue;
                            }
                        }

                        // Plain RBRACE → end
                        block_depth -= 1;
                        if after_return { after_return = false; }
                        if self.mode == 1 {
                            self.result.push_str("end");
                        }
                        ti += 1;
                        continue;
                    }

                    // ---- IF: look for LBRACE pattern ----
                    if kind == 1 && !after_return {
                        // Collect tokens until LBRACE or NEWLINE
                        let mut scan = ti + 1;
                        let mut cond = String::new();
                        let mut found_brace = false;
                        while scan < n {
                            let sk = self.token_kinds[scan];
                            if sk == 6 {
                                // IF ... LBRACE
                                if self.mode == 1 {
                                    self.result.push_str("if");
                                    self.result.push_str(cond.trim_end());
                                    self.result.push_str(" then");
                                }
                                block_depth += 1;
                                ti = scan + 1;
                                found_brace = true;
                                break;
                            }
                            if sk == 10 { break; } // Newline before brace — not our pattern
                            let s = self.token_starts[scan];
                            let e = self.token_ends[scan];
                            cond.push_str(&String::from_utf8_lossy(&bytes[s..e]));
                            scan += 1;
                        }
                        if found_brace { continue; }
                        // Not our pattern — emit as-is
                    }

                    // ---- WHILE: look for LBRACE pattern ----
                    if kind == 4 && !after_return {
                        let mut scan = ti + 1;
                        let mut cond = String::new();
                        let mut found_brace = false;
                        while scan < n {
                            let sk = self.token_kinds[scan];
                            if sk == 6 {
                                if self.mode == 1 {
                                    self.result.push_str("while");
                                    self.result.push_str(cond.trim_end());
                                    self.result.push_str(" do");
                                }
                                block_depth += 1;
                                ti = scan + 1;
                                found_brace = true;
                                break;
                            }
                            if sk == 10 { break; }
                            let s = self.token_starts[scan];
                            let e = self.token_ends[scan];
                            cond.push_str(&String::from_utf8_lossy(&bytes[s..e]));
                            scan += 1;
                        }
                        if found_brace { continue; }
                    }

                    // ---- RETURN: emit the return keyword, then continue
                    // emitting all subsequent tokens on the same line. We mark
                    // `after_return` only after the newline so any expression
                    // tokens that follow `return` (e.g. `return self.value`)
                    // make it through. Without this, `return X` collapsed to
                    // bare `return` and the X token was stripped — Lua tests
                    // that returned a value silently returned nothing.
                    if kind == 8 && !after_return {
                        self.result.push_str(&text);
                        // Walk subsequent tokens until newline or block close.
                        let mut look = ti + 1;
                        while look < n {
                            let lk = self.token_kinds[look];
                            if lk == 10 {
                                // Newline — emit it and now mark after_return.
                                self.result.push_str(
                                    &String::from_utf8_lossy(
                                        &self.bytes[self.token_starts[look]..self.token_ends[look]],
                                    ),
                                );
                                look += 1;
                                break;
                            }
                            if lk == 7 || lk == 9 {
                                // RBRACE / END — let the outer loop handle it
                                // so block_depth is decremented correctly.
                                break;
                            }
                            // Anything else (text, whitespace, identifiers) —
                            // emit as-is so the return expression survives.
                            self.result.push_str(
                                &String::from_utf8_lossy(
                                    &self.bytes[self.token_starts[look]..self.token_ends[look]],
                                ),
                            );
                            look += 1;
                        }
                        after_return = true;
                        ti = look;
                        continue;
                    }

                    // ---- After return: skip non-comment, non-structural tokens ----
                    if after_return {
                        // Allow through: COMMENT, NEWLINE (to preserve formatting),
                        // RBRACE/END (block closers reset terminal state)
                        if kind == 12 {
                            // Comment — emit
                            self.result.push_str(&text);
                            ti += 1;
                            continue;
                        }
                        if kind == 10 {
                            // Newline — emit (keeps line structure)
                            self.result.push_str(&text);
                            ti += 1;
                            continue;
                        }
                        if kind == 7 {
                            // RBRACE — end of block, reset after_return
                            after_return = false;
                            block_depth -= 1;
                            if self.mode == 1 { self.result.push_str("end"); }
                            ti += 1;
                            continue;
                        }
                        if kind == 9 || kind == 2 || kind == 3 {
                            // END/ELSEIF/ELSE — structural boundary, reset
                            after_return = false;
                            self.result.push_str(&text);
                            ti += 1;
                            continue;
                        }
                        // Skip everything else (unreachable code)
                        ti += 1;
                        continue;
                    }

                    // ---- Default: emit token text unchanged ----
                    self.result.push_str(&text);
                    ti += 1;
                }
            }
        }

    domain:
        bytes: Vec<u8> = Vec::new()
        mode: usize = 1
        token_kinds: Vec<usize> = Vec::new()
        token_starts: Vec<usize> = Vec::new()
        token_ends: Vec<usize> = Vec::new()
        result: String = String::new()
}