jaroslavtyc/drd-plus-health

View on GitHub
Health/Wound.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php declare(strict_types=1);

namespace DrdPlus\Health;

use DrdPlus\Codes\Body\OrdinaryWoundOriginCode;
use DrdPlus\Codes\Body\SeriousWoundOriginCode;
use DrdPlus\Codes\Body\WoundOriginCode;
use Granam\Integer\IntegerInterface;
use Granam\Strict\Object\StrictObject;

abstract class Wound extends StrictObject implements IntegerInterface
{
    /**
     * @var Health
     */
    private $health;
    /**
     * @var array|PointOfWound[]
     */
    private $pointsOfWound;
    /**
     * @var WoundOriginCode
     */
    private $woundOriginCode;
    /**
     * @var bool
     */
    private $old;

    /**
     * @param Health $health
     * @param WoundSize $woundSize (it can be also zero; usable for afflictions without a damage at all)
     * @param WoundOriginCode $woundOriginCode Ordinary origin is for lesser wound, others for serious wound
     * @throws \DrdPlus\Health\Exceptions\WoundHasToBeCreatedByHealthItself
     */
    protected function __construct(Health $health, WoundSize $woundSize, WoundOriginCode $woundOriginCode)
    {
        $this->checkIfCreatedByGivenHealth($health);
        $this->health = $health;
        $this->pointsOfWound = $this->createPointsOfWound($woundSize);
        $this->woundOriginCode = $woundOriginCode;
        $this->old = false;
    }

    /**
     * @param Health $health
     * @throws \DrdPlus\Health\Exceptions\WoundHasToBeCreatedByHealthItself
     */
    private function checkIfCreatedByGivenHealth(Health $health)
    {
        if (!$health->isOpenForNewWound()) {
            throw new Exceptions\WoundHasToBeCreatedByHealthItself(
                'Given health is not open for new wounds. Every wound has to be created by health itself.'
            );
        }
    }

    /**
     * @param WoundSize $woundSize
     * @return PointOfWound[]|array
     */
    private function createPointsOfWound(WoundSize $woundSize): array
    {
        $pointsOfWound = [];
        for ($wounded = $woundSize->getValue(); $wounded > 0; $wounded--) {
            $pointsOfWound[] = new PointOfWound($this); // implicit value of point of wound is 1
        }
        return $pointsOfWound;
    }

    public function getHealth(): Health
    {
        return $this->health;
    }

    /**
     * @return array|PointOfWound[]
     */
    public function getPointsOfWound(): array
    {
        return $this->pointsOfWound;
    }

    /**
     * @return SeriousWoundOriginCode|OrdinaryWoundOriginCode|WoundOriginCode
     */
    public function getWoundOriginCode(): WoundOriginCode
    {
        return $this->woundOriginCode;
    }

    public function getValue(): int
    {
        // each point has value of 1, therefore count is enough
        return \count($this->getPointsOfWound());
    }

    public function getWoundSize(): WoundSize
    {
        return WoundSize::createIt($this->getValue());
    }

    abstract public function isSerious(): bool;

    abstract public function isOrdinary(): bool;

    /**
     * @param int $healUpToWounds
     * @return int amount of healed points of wound
     */
    public function heal(int $healUpToWounds): int
    {
        $this->setOld(); // any wound is "old", treated and can be healed by regeneration or a true professional only
        // technical note: orphaned points of wound are removed automatically on persistence
        if ($healUpToWounds >= $this->getValue()) { // there is power to heal it all
            $healed = $this->getValue();
            $this->pointsOfWound = []; // unbinds all the points of wound

            return $healed;
        }
        $healed = 0;
        for ($healing = 1; $healing <= $healUpToWounds; $healing++) {
            // removing points one by one
            \array_pop($this->pointsOfWound);
            $healed++;
        }
        return $healed; // just a partial heal
    }

    public function isHealed(): bool
    {
        return $this->getValue() === 0;
    }

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

    public function isFresh(): bool
    {
        return !$this->old;
    }

    public function setOld()
    {
        $this->old = true;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return (string)$this->getValue();
    }
}