<?php


namespace Mobile\Shared\Util;


use Mobile\Device\Entity\Block;
use Mobile\Device\Entity\Device;
use Mobile\Device\Entity\Individual;
use Mobile\Device\Entity\SubBlock;
use Mobile\Device\Entity\UnitParcel;
use Mobile\Project\Entity\Platform;

/**
 * Class PlatformCrawler
 * Used to perform a depth-first traverse of any part of a Platform
 * This can be used dynamically by setting the differents callbacks
 * You can also extends this class by overriding handleXXX() methods in order to use one kind of crawler multiple time
 * @package Mobile\Shared\Util
 */
class PlatformCrawler
{
    /**
     * @var callable
     */
    private $platformCallable;
    /**
     * @var callable
     */
    private $deviceCallable;
    /**
     * @var callable
     */
    private $blockCallable;
    /**
     * @var callable
     */
    private $subBlockCallable;
    /**
     * @var callable
     */
    private $unitParcelCallable;
    /**
     * @var callable
     */
    private $individualCallable;

    public function __construct()
    {
        //Default callback is needed to avoid call_user_func to throw an error if not set
        $defaultCallback = function ($object){};
        $this->platformCallable = $defaultCallback;
        $this->deviceCallable = $defaultCallback;
        $this->blockCallable = $defaultCallback;
        $this->subBlockCallable = $defaultCallback;
        $this->unitParcelCallable = $defaultCallback;
        $this->individualCallable = $defaultCallback;
    }

    /**
     * Method that start the crawler. You might set one or more callback before calling this
     * @param $businessObject
     */
    public function crawl($businessObject){
        if($businessObject === null){
            return;
        }
        $this->handle($businessObject);
        $children = PlatformUtil::getChildren($businessObject);
        if($children !== null) {
            foreach ($children as $child) {
                $this->crawl($child);
            }
        }
    }

    private function handle($obj){
        if($obj instanceof Platform){
            $this->handlePlatform($obj);
        }elseif ($obj instanceof Device){
            $this->handleDevice($obj);
        }elseif ($obj instanceof Block){
            $this->handleBlock($obj);
        }elseif ($obj instanceof SubBlock){
            $this->handleSubBlock($obj);
        }elseif ($obj instanceof UnitParcel){
            $this->handleUnitParcel($obj);
        }elseif ($obj instanceof Individual){
            $this->handleIndividual($obj);
        }
    }

    private function handlePlatform(Platform $platform){
        call_user_func($this->platformCallable, $platform);
    }
    private function handleDevice(Device $device){
        call_user_func($this->deviceCallable, $device);
    }
    private function handleBlock(Block $block){
        call_user_func($this->blockCallable, $block);
    }
    private function handleSubBlock(SubBlock $subBlock){
        call_user_func($this->subBlockCallable, $subBlock);
    }
    private function handleUnitParcel(UnitParcel $unitParcel){
        call_user_func($this->unitParcelCallable, $unitParcel);
    }
    private function handleIndividual(Individual $individual){
        call_user_func($this->individualCallable, $individual);
    }

    /**
     * @param callable $platformCallable
     * @return PlatformCrawler
     */
    public function setPlatformCallable(callable $platformCallable): PlatformCrawler
    {
        $this->platformCallable = $platformCallable;
        return $this;
    }

    /**
     * @param callable $deviceCallable
     * @return PlatformCrawler
     */
    public function setDeviceCallable(callable $deviceCallable): PlatformCrawler
    {
        $this->deviceCallable = $deviceCallable;
        return $this;
    }

    /**
     * @param callable $blockCallable
     * @return PlatformCrawler
     */
    public function setBlockCallable(callable $blockCallable): PlatformCrawler
    {
        $this->blockCallable = $blockCallable;
        return $this;
    }

    /**
     * @param callable $subBlockCallable
     * @return PlatformCrawler
     */
    public function setSubBlockCallable(callable $subBlockCallable): PlatformCrawler
    {
        $this->subBlockCallable = $subBlockCallable;
        return $this;
    }

    /**
     * @param callable $unitParcelCallable
     * @return PlatformCrawler
     */
    public function setUnitParcelCallable(callable $unitParcelCallable): PlatformCrawler
    {
        $this->unitParcelCallable = $unitParcelCallable;
        return $this;
    }

    /**
     * @param callable $individualCallable
     * @return PlatformCrawler
     */
    public function setIndividualCallable(callable $individualCallable): PlatformCrawler
    {
        $this->individualCallable = $individualCallable;
        return $this;
    }

}