<?php
/**
 * Box packing (3D bin packing, knapsack problem).
 *
 * @author Doug Wright
 */
namespace D5WEXT\Packing\DVDoug\BoxPacker;

use function max;
use function min;
use const PHP_INT_MAX;

/**
 * A packed layer.
 *
 * @author Doug Wright
 * @internal
 */
class PackedLayer
{
    /**
     * @var int
     */
    private $startX = PHP_INT_MAX;

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

    /**
     * @var int
     */
    private $startY = PHP_INT_MAX;

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

    /**
     * @var int
     */
    private $startZ = PHP_INT_MAX;

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

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

    /**
     * Items packed into this layer.
     *
     * @var PackedItem[]
     */
    protected $items = [];

    /**
     * Add a packed item to this layer.
     * @param PackedItem $packedItem
     * @return void
     */
    public function insert(PackedItem $packedItem)
    {
        $this->items[] = $packedItem;
        $this->weight += $packedItem->getItem()->getWeight();
        $this->startX = min($this->startX, $packedItem->getX());
        $this->endX = max($this->endX, $packedItem->getX() + $packedItem->getWidth());
        $this->startY = min($this->startY, $packedItem->getY());
        $this->endY = max($this->endY, $packedItem->getY() + $packedItem->getLength());
        $this->startZ = min($this->startZ, $packedItem->getZ());
        $this->endZ = max($this->endZ, $packedItem->getZ() + $packedItem->getDepth());
    }

    /**
     * Get the packed items.
     *
     * @return PackedItem[]
     */
    public function getItems()
    {
        return $this->items;
    }

    /**
     * Calculate footprint area of this layer.
     *
     * @return int mm^2
     */
    public function getFootprint()
    {
        return $this->getWidth() * $this->getLength();
    }

    /**
     * @return int
     */
    public function getStartX()
    {
        return $this->startX;
    }

    /**
     * @return int
     */
    public function getEndX()
    {
        return $this->endX;
    }

    /**
     * @return int
     */
    public function getWidth()
    {
        return $this->endX ? $this->endX - $this->startX : 0;
    }

    /**
     * @return int
     */
    public function getStartY()
    {
        return $this->startY;
    }

    /**
     * @return int
     */
    public function getEndY()
    {
        return $this->endY;
    }

    /**
     * @return int
     */
    public function getLength()
    {
        return $this->endY ? $this->endY - $this->startY : 0;
    }

    /**
     * @return int
     */
    public function getStartZ()
    {
        return $this->startZ;
    }

    /**
     * @return int
     */
    public function getEndZ()
    {
        return $this->endZ;
    }

    /**
     * @return int
     */
    public function getDepth()
    {
        return $this->endZ ? $this->endZ - $this->startZ : 0;
    }

    /**
     * @return int
     */
    public function getWeight()
    {
        return $this->weight;
    }

    /**
     * @param PackedLayer $otherLayer
     * @return void
     */
    public function merge(self $otherLayer)
    {
        foreach ($otherLayer->items as $packedItem) {
            $this->insert($packedItem);
        }
    }
}
