<?php declare(strict_types=1);

namespace Newland\Toubiz\Map\Neos\Provider\Pagination;

/**
 * Simple paginator that supports getting data from multiple backends.
 */
class Paginator
{
    /** @var array */
    private $providers = [];

    /** @var int */
    private $numberOfItems = 0;

    /** @var int */
    private $pageSize;

    public function __construct(int $pageSize)
    {
        $this->pageSize = $pageSize;
    }

    public function addProvider(int $numberOfItems, \Closure $generator): void
    {
        $this->providers[] = [ $numberOfItems, $generator ];
        $this->numberOfItems += $numberOfItems;
    }

    public function numberOfPages(): int
    {
        return (int) ceil($this->numberOfItems / $this->pageSize);
    }

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

    public function get(int $page): array
    {
        if ($page <= 0 || $page > $this->numberOfPages()) {
            throw InvalidPageException::create($page, $this->numberOfPages());
        }

        return $this->getOffsetLimit(($page - 1) * $this->pageSize, $this->pageSize);
    }

    public function getOffsetLimit(int $offset, int $limit): array
    {
        $itemsToSkip = $offset;
        $itemsToGenerate = $limit;
        $items = [ [] ];

        foreach ($this->providers as [ $availableItems, $generator ]) {
            $offsetInProvider = 0;
            if ($itemsToSkip > 0) {
                $offsetInProvider = min($availableItems, $itemsToSkip);
                $availableItems -= $offsetInProvider;
                $itemsToSkip -= $offsetInProvider;
            }

            if ($availableItems === 0) {
                continue;
            }

            $limitInProvider = min($availableItems, $itemsToGenerate);
            $items[] = (array) $generator($offsetInProvider, $limitInProvider);
            $itemsToGenerate -= $limitInProvider;

            if ($itemsToGenerate === 0) {
                break;
            }
        }

        return array_merge(...$items);
    }
}
