<?php

namespace Velis\Model\DataObject;

use BadMethodCallException;
use InvalidArgumentException;

/**
 * Events functionality
 * @author Olek Procki <olo@velis.pl>
 */
trait EventTrait
{
    /**
     * Trigger functions
     * @var array
     */
    protected $_triggers = [];


    /**
     * Adds trigger callback function for event
     *
     * @param string $event
     * @param callable $callback
     *
     * @return $this
     */
    public function on($event, $callback)
    {
        if (!is_callable($callback)) {
            throw new InvalidArgumentException('Argument $callback is not callable');
        }

        if (!isset($this->_triggers[$event]) || !is_array($this->_triggers[$event])) {
            $this->_triggers[$event] = [];
        }

        $this->_triggers[$event][] = $callback;

        return $this;
    }


    /**
     * Returns true if any triggers for $event are registered
     *
     * @param string $event
     * @return bool
     */
    public function hasTriggers($event): bool
    {
        return (
            isset($this->_triggers[$event])
            && is_array($this->_triggers[$event])
            && count($this->_triggers[$event])
        );
    }


    /**
     * Returns triggers registered for $event
     *
     * @param string $event
     * @param bool $clear
     *
     * @return callable[]
     */
    public function getTriggers($event, $clear = true)
    {
        $triggers = $this->_triggers[$event];
        if ($clear) {
            $this->clearTriggers($event);
        }
        return $triggers;
    }


    /**
     * Clears previously attached action triggers
     *
     * @param string $event
     * @return $this
     */
    public function clearTriggers($event = null)
    {
        if ($event) {
            unset($this->_triggers[$event]);
        } else {
            unset($this->_triggers);
        }

        return $this;
    }


    /**
     * Executes $event triggers
     * @param string $event
     * @return $this
     */
    protected function _execTriggers($event)
    {
        if ($this->hasTriggers($event)) {
            foreach ($this->getTriggers($event) as $callback) {
                call_user_func($callback, $this);
            }
        }

        return $this;
    }


    /**
     * Call magic function
     *
     * @param string $name
     * @param array $arguments
     *
     * @throws BadMethodCallException
     */
    public function __call($name, $arguments)
    {
        if (strpos($name, 'trigger') === 0) {
            $event = lcfirst(str_replace('trigger', '', $name));
            if (!$event) {
                throw new BadMethodCallException("Method $name doesn't exist");
            }
            $this->_execTriggers($event);
        } else {
            throw new BadMethodCallException("Method $name doesn't exist");
        }
    }
}
