<?php

declare(strict_types=1);

namespace PhpWA\Protocol\Binary;

use PhpWA\Exception\ProtocolException;

final class BinaryReader
{
    /** @var string */ private $buffer;
    /** @var int */ private $offset = 0;

    public function __construct(string $buffer)
    {
        $this->buffer = $buffer;
    }

    public function byte(): int
    {
        $this->requireBytes(1);
        return ord($this->buffer[$this->offset++]);
    }

    public function bytes(int $length): string
    {
        if ($length < 0) {
            throw new ProtocolException('Negative WABinary byte length.');
        }
        $this->requireBytes($length);
        $value = substr($this->buffer, $this->offset, $length);
        $this->offset += $length;
        return $value;
    }

    public function integer(int $bytes): int
    {
        if ($bytes < 1 || $bytes > 4) {
            throw new ProtocolException('Invalid WABinary integer width.');
        }
        $this->requireBytes($bytes);
        $value = 0;
        for ($index = 0; $index < $bytes; $index++) {
            $value = ($value << 8) | $this->byte();
        }
        return $value;
    }

    public function integer20(): int
    {
        $this->requireBytes(3);
        return (($this->byte() & 0x0f) << 16) | ($this->byte() << 8) | $this->byte();
    }

    public function offset(): int { return $this->offset; }
    public function setOffset(int $offset): void
    {
        if ($offset < 0 || $offset > strlen($this->buffer)) {
            throw new ProtocolException('Invalid WABinary reader offset.');
        }
        $this->offset = $offset;
    }
    public function remaining(): int { return strlen($this->buffer) - $this->offset; }

    private function requireBytes(int $length): void
    {
        if ($length > $this->remaining()) {
            throw new ProtocolException('Unexpected end of WABinary frame.');
        }
    }
}
