<?php

/**
 * Emits Vala source lines while preserving source line numbers where possible.
 */
final class ValaCodeLineEmitter
{
    private array $lines;
    private int $nextStandaloneLine;
    private bool $canAppendToWrapperLine = true;

    /**
     * Create an emitter with one or more prelude/wrapper lines.
     *
     * `$nextStandaloneLine` must be the generated line number available for the
     * first source-mapped statement. This keeps source line preservation stable
     * even when a `using` line is emitted before the main wrapper.
     */
    public function __construct(string|array $wrapperLines, ?int $nextStandaloneLine = null)
    {
        $this->lines = is_array($wrapperLines) ? array_values($wrapperLines) : [$wrapperLines];
        $this->nextStandaloneLine = $nextStandaloneLine ?? (count($this->lines) + 1);
    }

    /**
     * Emit one generated line at the requested source line.
     *
     * Returns the actual generated Vala line number used for the emitted line.
     */
    public function emitAtSourceLine(int $sourceLine, string $line): int
    {
        $sourceLine = max(1, $sourceLine);

        if ($this->canAppendToWrapperLine && $sourceLine === 1 && count($this->lines) === 1) {
            $this->lines[count($this->lines) - 1] .= ' ' . ltrim($line);
            $this->canAppendToWrapperLine = false;
            return count($this->lines);
        }

        $this->fillBlankLinesUntil($sourceLine);
        $this->lines[] = $line;
        $lineNo = count($this->lines);
        $this->nextStandaloneLine++;
        $this->canAppendToWrapperLine = false;
        return $lineNo;
    }

    /**
     * Finish the wrapped Vala source.
     */
    public function finish(): string
    {
        $this->lines[] = '}';
        $this->lines[] = '';

        return implode("\n", $this->lines);
    }

    private function fillBlankLinesUntil(int $sourceLine): void
    {
        while ($this->nextStandaloneLine < $sourceLine) {
            $this->lines[] = '';
            $this->nextStandaloneLine++;
            $this->canAppendToWrapperLine = false;
        }
    }
}
