<?php

namespace KupShop\FeedsBundle\Wrapper;

use KupShop\FeedGeneratorBundle\Expressions\ExpressionObjectTrait;
use KupShop\FeedGeneratorBundle\Expressions\IExpressionObject;
use KupShop\FeedGeneratorBundle\Utils\IObjectInfo;

abstract class BaseWrapper implements \ArrayAccess, IExpressionObject, IObjectInfo
{
    use ExpressionObjectTrait;

    protected $object;

    protected static $objectType;

    public function setObject($object)
    {
        if ($object === null && isDevelopment()) {
            throw new \Exception('Setuješ null do wrapperu, to není dobré, radši ten wrapper přeskoč, ne?');
        }

        if (static::$objectType && !$object instanceof static::$objectType) {
            throw new \Exception('Incorrect type wrapped: '.get_class($object).' not instanceof '.static::$objectType);
        }

        $this->object = $object;

        return $this;
    }

    public static function wrap($object)
    {
        $wrapper = new static();
        $wrapper->setObject($object);

        return $wrapper;
    }

    // unwrap() and getObject() intentionally not implemented

    /**
     * Implements ArrayAccess interface.
     */
    public function offsetSet($offset, $value): void
    {
        $this->{$offset} = $value;
    }

    public function offsetExists($offset): bool
    {
        return isset($this->{$offset}) ? true : method_exists($this, $this->offsetMethod($offset));
    }

    public function offsetUnset($offset): void
    {
        unset($this->{$offset});
    }

    public function offsetMethod($offset)
    {
        return 'field_'.$offset;
    }

    public function offsetGet($offset): mixed
    {
        $method = $this->offsetMethod($offset);
        if (method_exists($this, $method)) {
            $res = call_user_func([$this, $method]);

            return $res;
        }

        return isset($this->{$offset}) ? $this->{$offset} : null;
    }

    public function __call($name, $arguments)
    {
        if (method_exists($this, $name)) {
            return call_user_func_array([$this, $name], $arguments);
        }

        throw new \Exception("Undefined method '{$name}' called on '{$this}'");
    }

    public function __set($name, $value)
    {
        return $this->offsetSet($name, $value);
    }

    public function __get($name)
    {
        return $this->offsetGet($name);
    }

    public function __isset($name)
    {
        return $this->offsetExists($name);
    }

    public function __unset($name)
    {
        $this->offsetUnset($name);

        return null;
    }

    public function __toString()
    {
        return get_class($this);
    }
}
