<?php

// SPDX-FileCopyrightText: 2018 Icinga GmbH <https://icinga.com>
// SPDX-License-Identifier: GPL-3.0-or-later

namespace Icinga\Protocol\File;

use Icinga\Exception\IcingaException;
use SplFileObject;
use Iterator;

/**
 * Iterate over a log file, yielding the regex fields of the log messages
 */
class LogFileIterator implements Iterator
{
    /**
     * Log file
     *
     * @var SplFileObject
     */
    protected $file;

    /**
     * A PCRE string with the fields to extract
     * from the log messages as named subpatterns
     *
     * @var string
     */
    protected $fields;

    /**
     * Value for static::current()
     *
     * @var array
     */
    protected $current;

    /**
     * Index for static::key()
     *
     * @var int
     */
    protected $index;

    /**
     * Value for static::valid()
     *
     * @var boolean
     */
    protected $valid;

    /**
     * @var string
     */
    protected $next = null;

    /**
     * @param string $filename      The log file's name
     * @param string $fields        A PCRE string with the fields to extract
     *                              from the log messages as named subpatterns
     */
    public function __construct($filename, $fields)
    {
        $this->file = new SplFileObject($filename);
        $this->file->setFlags(
            SplFileObject::DROP_NEW_LINE |
            SplFileObject::READ_AHEAD
        );
        $this->fields = $fields;
    }

    public function rewind(): void
    {
        $this->file->rewind();
        $this->index = 0;
        $this->nextMessage();
    }

    public function next(): void
    {
        $this->file->next();
        ++$this->index;
        $this->nextMessage();
    }

    public function current(): array
    {
        return $this->current;
    }

    public function key(): int
    {
        return $this->index;
    }

    public function valid(): bool
    {
        return $this->valid;
    }

    protected function nextMessage()
    {
        $message = $this->next === null ? array() : array($this->next);
        $this->valid = null;
        while ($this->file->valid()) {
            if (false === ($res = preg_match(
                $this->fields,
                $current = $this->file->current()
            ))) {
                throw new IcingaException('Failed at preg_match()');
            }
            if (empty($message)) {
                if ($res === 1) {
                    $message[] = $current;
                }
            } elseif ($res === 1) {
                $this->next = $current;
                $this->valid = true;
                break;
            } else {
                $message[] = $current;
            }

            $this->file->next();
        }
        if ($this->valid === null) {
            $this->next = null;
            $this->valid = ! empty($message);
        }

        if ($this->valid) {
            while (! empty($message)) {
                $matches = array();
                if (false === ($res = preg_match(
                    $this->fields,
                    implode(PHP_EOL, $message),
                    $matches
                ))) {
                    throw new IcingaException('Failed at preg_match()');
                }
                if ($res === 1) {
                    $this->current = $matches;
                    return;
                }
                array_pop($message);
            }
            $this->valid = false;
        }
    }
}
