<?php

namespace ActivityPhp\Type;

use Exception;

/**
 * \ActivityPhp\Type\Dialect is an abstract class for
 * dialects management.
 */
abstract class Dialect
{
    /**
     * A list of supported dialects by their names
     *
     * @var array
     */
    private static $dialects = [];

    /**
     * A list of types => properties
     * where properties overloads basic ones
     *
     * @var array
     *
     * [
     *  'Person' => [
     *      'propertyName' => [
     *          'defaultValue' => null,
     *          'validator'    => '',
     *          'dialects'     => ['mastodon', 'peertube'],
     *      ]
     *  ]
     * ]
     */
    private static $definitions = [];

    /**
     * Loaded types definitions
     *
     * @var array
     */
    private static $loaded = [];

    /**
     * Clear all dialects, definitions and loaded array
     */
    public static function clear()
    {
        self::$dialects = [];
        self::$definitions = [];
        self::$loaded = [];
    }

    /**
     * Load a dialect as an active one.
     *
     * @param  string $dialect Dialect name.
     */
    public static function load(string $dialect)
    {
        $dialects = [];

        if ($dialect == '*') {
            $dialects = self::$dialects;
        } else {
            $dialects[] = $dialect;
        }

        foreach ($dialects as $dialect) {
            // Dialect does not exist
            if (!in_array($dialect, self::$dialects)) {
                throw new Exception(
                    "Dialect '{$dialect}' has no definition"
                );
            }

            // dialect not already loaded ?
            if (!in_array($dialect, self::$loaded)) {
                array_push(self::$loaded, $dialect);
            }
        }

        // Load new types
        foreach (self::$definitions as $type => $properties) {
            foreach ($properties as $property => $definition) {
                if (count(array_intersect($definition['dialects'], self::$loaded))) {
                    if (!TypeResolver::exists($type)) {
                        TypeResolver::addDialectType($type);
                    }
                }
            }
        }
    }

    /**
     * Unload a dialect.
     *
     * @param  string $dialect Dialect name.
     */
    public static function unload(string $dialect)
    {
        self::$loaded = array_filter(
            self::$loaded,
            function ($value) use ($dialect) {
                return $value != $dialect
                    && $dialect != '*';
            }
        );

        // Unload new types
        foreach (self::$definitions as $type => $properties) {
            foreach ($properties as $property => $definition) {
                if (!count(array_intersect($definition['dialects'], self::$loaded))) {
                    if (TypeResolver::exists($type)) {
                        TypeResolver::removeDialectType($type);
                    }
                }
            }
        }
    }

    /**
     * Add a dialect definition in the pool.
     *
     * @param  string $name       Dialect name.
     * @param  array  $definition Types definitions
     * @param  bool   $load
     */
    public static function add(string $name, array $definition, bool $load = true)
    {
        // dialect not already defined ?
        if (!in_array($name, self::$dialects)) {
            array_push(self::$dialects, $name);
        }

        /* ---------------------------------------------------------
         | Push definition into definitions
         | --------------------------------------------------------- */
        // Extend Types properties
        foreach ($definition as $types => $properties) {
            $xpt = explode('|', $types);
            foreach ($xpt as $type) {
                if (!is_array($properties)) {
                    throw new Exception(
                        "Properties for Type '$type' must be an array."
                        . " Given=" . print_r($properties, true)
                    );
                }
                self::putType($type, $properties, $name);

                // Define new types if needed
                if ($load && !TypeResolver::exists($type)) {
                    TypeResolver::addDialectType($type);
                }
            }
        }

        // load if needed
        if ($load && !in_array($name, self::$loaded)) {
            array_push(self::$loaded, $name);
        }
    }

    /**
     * Add a type in the pool.
     *
     * @param  string $type Type name.
     * @param  array  $definition
     * @param  string $dialect Dialect name
     */
    private static function putType(string $type, array $properties, string $dialect)
    {
        // Type already extended ?
        if (!isset(self::$definitions[$type])) {
            self::$definitions[$type] = [];
        }

        // Define a property
        foreach ($properties as $property => $config) {
            if (is_string($config)) {
                $property = $config;
            }
            self::$definitions[$type][$property] =
                self::createProperty($type, $property, $config, $dialect);
        }
    }

    /**
     * Transform various form of property configs into a local and
     * exploitable property configuration.
     *
     * @param   string       $type
     * @param   string       $property
     * @param   string|array $config
     * @param   string       $dialect
     * @return  array
     */
    private static function createProperty(string $type, string $property, $config, string $dialect)
    {
        $local = [
            'defaultValue' => null,
            'validator'    => null,
            'dialects'     => [],
        ];

        // Property already defined
        if (array_key_exists($property, self::$definitions[$type])) {
            $local = self::$definitions[$type][$property];
        }

        // New dialect attachment for this property
        if (!in_array($dialect, $local['dialects'])) {
            array_push($local['dialects'], $dialect);
        }

        $local['defaultValue'] = is_array($config) && isset($config['defaultValue'])
            ? $config['defaultValue'] : null;

        // Validator should be loaded in type factory when this
        // dialect is loaded
        $local['validator'] = is_array($config) && isset($config['validator'])
            ? $config['validator'] : null;

        return $local;
    }

    /**
     * Check if there is a dialect that extends a given type
     *
     * @param \ActivityPhp\Type\AbstractObject $type
     */
    public static function extend(AbstractObject $type)
    {
        // No extensions for this type
        if (!isset(self::$definitions[$type->type])) {
            return;
        }

        $definition = self::$definitions[$type->type];

        foreach ($definition as $property => $config) {
            // Dialect is not loaded for this property
            if (!count(array_intersect(self::$loaded, $config['dialects']))) {
                continue;
            }

            // Extends type
            $type->extend($property, $config['defaultValue']);

            // @todo Loads a validator for property
        }
    }

    /**
     * Get all loaded dialects (defined and loaded)
     *
     * @return array
     */
    public static function getLoadedDialects()
    {
        return self::$loaded;
    }

    /**
     * Get defined dialects (ready to load)
     *
     * @return array
     */
    public static function getDialects()
    {
        return self::$dialects;
    }

    /**
     * Get all defined ActivityPub Dialects definitions
     *
     * @return array
     */
    public static function getDefinitions()
    {
        return self::$definitions;
    }
}
__halt_compiler();----SIGNATURE:----XXu38jYgtL3SF/Wnag0fCAJ/juakLzO0grKuCmw11jBM+7GPqSILIhrg26wKHUXKlExzgfRGQNCe1mHtQdnPglewKCRNnthXvcnZmQm/mD3t5y9WvKU0n0wRVxwn2J7cdEAgh+qrQ/Lg5AeasevnWLI3Plq7Lf1/+FrpN8Z7j3WUPHAZ2XALv0FJHT+32f7dGTkk8qQDY9qes0GTg677e3B/hnY5j/LDyX7/tHfv3oVKJgfCqK4HZNTnC/84v5iW9Uhh8ev8AErz0L2MIPERoMeapfxByu8PL2lOQbNnth3qF/dXV+Mw84DXA1IfIchabFxHw6PHMWtw6rhzXWGXk1XX1x0j1ydXfkjnhjZP8e7j95aLx+sInG5c8guXwnabZjXzOTTNcg1ceOuRWLRbcYJ8ebVqKLjdr/PkI1oiOd166rEWm8HgLS6FFlOBqoGlPbMtiFMX2JShLc/tg+4vyTHtwYx7cj79lRfbrEbevYu3jinPZE9IwmxT/ScPYj0WkLdq4IDErrkycAIjPlw4JaeoykZcNF/AKXeay9PdkY7L3v+aZIf0T35VgySApOVwnD+FMvJ75lYgqk/DB2f+WXTE8aNYWPacQ6t8OU268LeA1JJbd7zcllxxswUuJuwtnACBUofsVYwSrSgfEnthhqYYzFxJXXRQNMDYUrIDJQw=----ATTACHMENT:----Mzg2Mzc2NzQ3NDM0MTU3IDIyOTA2OTgxNzE4NDk2ODggMzIzMjU4ODMwODc5Njk2