<?php
/**
 * @package qfq
 * @author kputyr
 * @date: 09.11.2023
 */

namespace IMATHUZH\Qfq\Core\Parser;

/**
 * A helper class used by StringTokenizer to build a token. It contains extra information
 * that are necessary to properly process the token into a string.
 * @package qfq
 */
class TokenBuilder {

    /**
     * @var array A list of substrings that form the token. Splits occur at escaped
     * delimiters and unescaped quotes so that they do not appear in a final string.
     */
    public array $pieces = [];

    /**
     * @var int The current total length of the token pieces
     */
    public int $length = 0;

    /**
     * @var int the offset of the first unescaped quote
     */
    public int $firstQuoteOffset = -1;

    /**
     * @var int the offset of the last unescaped quote
     */
    public int $lastQuoteOffset = -1;

    /**
     * Returns true when an unescaped quote has been found
     * while creating the token.
     * @return bool
     */
    public function hasQuotes(): bool {
        return $this->firstQuoteOffset >= 0;
    }

    /**
     * Resets the builder to its initial state
     * @return void
     */
    public function reset() {
        $this->pieces = [];
        $this->length = 0;
        $this->firstQuoteOffset = -1;
        $this->lastQuoteOffset = -1;
    }

    /**
     * Processes the data to a token and resets the builder
     * @return Token
     */
    public function process(): Token {
        // Combine all the pieces and trim the resulting string,
        // but keep whitespaces that were surrounded by quotes
        $value = implode('', $this->pieces);
        if ($this->hasQuotes()) {
            $value = ltrim(substr($value, 0, $this->firstQuoteOffset)) .
                substr($value, $this->firstQuoteOffset, $this->lastQuoteOffset - $this->firstQuoteOffset) .
                rtrim(substr($value, $this->lastQuoteOffset));
        } else {
            $value = trim($value);
        }
        $token = new Token($value, $this->hasQuotes());
        $this->reset();
        return $token;
    }

    /**
     * Adds a piece of a token and updates the builder status
     * @param string $data
     * @return void
     */
    public function append(string $data) {
        $this->pieces[] = $data;
        $this->length += strlen($data);
    }

    /**
     * Notifies the builder that a quote has been encountered.
     * The builder updates offsets of quotes accoringly.
     * @return void
     */
    public function markQuote() {
        if ($this->firstQuoteOffset < 0) {
            $this->firstQuoteOffset = $this->length;
        } else {
            $this->lastQuoteOffset = $this->length;
        }
    }
}
