<?php

namespace ActivityPhp;

use ActivityPhp\Type\AbstractObject;
use ActivityPhp\Type\Dialect;
use ActivityPhp\Type\TypeResolver;
use ActivityPhp\Type\Validator;
use Exception;

/**
 * \ActivityPhp\Type is a Factory for ActivityStreams 2.0 types.
 *
 * It provides shortcuts methods for type instanciation and more.
 *
 * @see https://www.w3.org/TR/activitystreams-vocabulary/#types
 * @see https://www.w3.org/TR/activitystreams-vocabulary/#activity-types
 * @see https://www.w3.org/TR/activitystreams-vocabulary/#actor-types
 * @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types
 */
abstract class Type
{
    /**
     * Factory method to create type instance and set attributes values
     *
     * To see which default types are defined and their attributes:
     *
     * @see https://www.w3.org/TR/activitystreams-vocabulary/#types
     * @see https://www.w3.org/TR/activitystreams-vocabulary/#activity-types
     * @see https://www.w3.org/TR/activitystreams-vocabulary/#actor-types
     * @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types
     *
     * @param  string|array<string,mixed> $type
     * @param  array<string,mixed>  $attributes
     */
    public static function create($type, array $attributes = []): AbstractObject
    {
        if (! is_string($type) && ! is_array($type)) {
            throw new Exception(
                'Type parameter must be a string or an array. Given='
                . gettype($type)
            );
        }

        if (is_array($type)) {
            if (! isset($type['type'])) {
                throw new Exception(
                    "Type parameter must have a 'type' key"
                );
            }

            $attributes = $type;
        }

        try {
            $class = is_array($type)
                ? TypeResolver::getClass($type['type'])
                : TypeResolver::getClass($type);
        } catch (Exception $exception) {
            $message = json_encode($attributes, JSON_PRETTY_PRINT);
            throw new Exception(
                $exception->getMessage() . "\n{$message}"
            );
        }

        if (is_string($class)) {
            $class = new $class();
        }

        self::extend($class);

        foreach ($attributes as $name => $value) {
            $class->set($name, $value);
        }

        return $class;
    }

    /**
     * Create an activitystream type from a JSON string
     */
    public static function fromJson(string $json): AbstractObject
    {
        $data = json_decode($json, true);

        if (json_last_error() === JSON_ERROR_NONE
            && is_array($data)
        ) {
            return self::create($data);
        }

        throw new Exception(
            sprintf(
                "An error occurred during the JSON decoding.\n '%s'",
                $json
            )
        );
    }

    /**
     * Add a custom type definition
     * It overrides defined types
     *
     * @param string $name A short name.
     * @param string $class Fully qualified class name
     */
    public static function add(string $name, string $class): void
    {
        TypeResolver::addCustomType($name, $class);
    }

    /**
     * Add a custom validator for an attribute.
     * It checks that it implements Validator\Interface
     *
     * @param string $name An attribute name to validate.
     * @param string $class A validator class name
     */
    public static function addValidator(string $name, string $class): void
    {
        Validator::add($name, $class);
    }

    /**
     * ActivityPub real world applications not only implements the basic
     * vocabulary.
     * They extends basic protocol with custom properties.
     * These extensions are called dialects.
     *
     * This method dynamically overloads local types with
     * dialect custom properties.
     */
    private static function extend(AbstractObject $type): void
    {
        // @todo should call Dialect stack to see if there are any
        // properties to overloads $type with
        Dialect::extend($type);
    }
}__halt_compiler();----SIGNATURE:----fb37rm4T3s9DQxe9S/op32Ykt4ZxlzmP2JLrzOR4Wr4hpx7j7F1KMNMun50hnWxKlPQ/8MFX02Y5YHvzvuHjBDx7v15IdcnFapN0dKzmSaS9Bkb4Y1xNQD7tUD4algyWyovPOB75R+sDgrV/AJok7xFIIl8+5ZaXNAqVrtpUW2uYEYoAuAWJnPoyjRYiwH3jOYPjU5uoYzC3dwmeWiZovPwzxVekGPFcaMuWypCPXiHI+99oPOHdfH82SLJFIaXCyHocQZS2FZ6oBLoTa7VEKHss0hdlud0+heyYZQIlpLoE7V5t6oCl+g6tDKMiEHmE3q2ziNEPySjKW8ZkTkqNlbuv2/qCas0TTBQOiVYMbk8x5gkAwTBZs47g4EY5QJAjlFLfjfrVxSseidKXi3QredXDiMBNDQ194GXvY7WeMwDK6x6/4YX8TVK8HET9C4bKFHVbcz+Vb+k1ug3T2w50g2/HmW5LQJULT5FIXD8tSzyh73Q363ULW6Grp1sgh73o/K4ut8wRpQB/7sqnbL3NtLgGuDWjFIY1QIi2KMx2YsdkjQFbg7cUITXsxCVfaZwABgINf3EEXmBF+WISaexqB5yYP5N3ia/vXn5by3CV3Nn0WX9j1XJNTlhUw2DifRDcHKL5vg4/k9EZl9Luyuvie6OHfy5UuT2Kg74tNp4rAKs=----ATTACHMENT:----ODMyNDk2MjQ5NjMxMTQ5IDk0OTAxOTg2MjE2MTAwNjIgNDI2MjM3MjIyMzgzOTI2Mw==