<?php

namespace Drupal\Core\Annotation;

use Drupal\Component\Annotation\Plugin;
use Drupal\Core\StringTranslation\TranslatableMarkup;

/**
 * @defgroup plugin_context Annotation for context definition
 * @{
 * Describes how to use ContextDefinition annotation.
 *
 * When providing plugin annotations, contexts can be defined to support UI
 * interactions through providing limits, and mapping contexts to appropriate
 * plugins. Context definitions can be provided as such:
 * @code
 *   context = {
 *     "node" = @ContextDefinition("entity:node")
 *   }
 * @endcode
 *
 * To add a label to a context definition use the "label" key:
 * @code
 *   context = {
 *     "node" = @ContextDefinition("entity:node", label = @Translation("Node"))
 *   }
 * @endcode
 *
 * Contexts are required unless otherwise specified. To make an optional
 * context use the "required" key:
 * @code
 *   context = {
 *     "node" = @ContextDefinition("entity:node", required = FALSE, label = @Translation("Node"))
 *   }
 * @endcode
 *
 * To define multiple contexts, simply provide different key names in the
 * context array:
 * @code
 *   context = {
 *     "artist" = @ContextDefinition("entity:node", label = @Translation("Artist")),
 *     "album" = @ContextDefinition("entity:node", label = @Translation("Album"))
 *   }
 * @endcode
 *
 * Specifying a default value for the context definition:
 * @code
 *   context = {
 *     "message" = @ContextDefinition("string",
 *       label = @Translation("Message"),
 *       default_value = @Translation("Checkout complete! Thank you for your purchase.")
 *     )
 *   }
 * @endcode
 *
 * @see annotation
 *
 * @}
 */

/**
 * Defines a context definition annotation object.
 *
 * Some plugins require various data contexts in order to function. This class
 * supports that need by allowing the contexts to be easily defined within an
 * annotation and return a ContextDefinitionInterface implementing class.
 *
 * @Annotation
 *
 * @ingroup plugin_context
 */
class ContextDefinition extends Plugin {

  /**
   * The ContextDefinitionInterface object.
   *
   * @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface
   */
  protected $definition;

  /**
   * Constructs a new context definition object.
   *
   * @param array $values
   *   An associative array with the following keys:
   *   - value: The required data type.
   *   - label: (optional) The UI label of this context definition.
   *   - required: (optional) Whether the context definition is required.
   *   - multiple: (optional) Whether the context definition is multivalue.
   *   - description: (optional) The UI description of this context definition.
   *   - default_value: (optional) The default value in case the underlying
   *     value is not set.
   *   - class: (optional) A custom ContextDefinitionInterface class.
   *
   * @throws \Exception
   *   Thrown when the class key is specified with a non
   *   ContextDefinitionInterface implementing class.
   */
  public function __construct(array $values) {
    $values += [
      'required' => TRUE,
      'multiple' => FALSE,
      'default_value' => NULL,
    ];
    // Annotation classes extract data from passed annotation classes directly
    // used in the classes they pass to.
    foreach (['label', 'description'] as $key) {
      // @todo Remove this workaround in https://www.drupal.org/node/2362727.
      if (isset($values[$key]) && $values[$key] instanceof TranslatableMarkup) {
        $values[$key] = (string) $values[$key]->get();
      }
      else {
        $values[$key] = NULL;
      }
    }
    if (isset($values['class']) && !in_array('Drupal\Core\Plugin\Context\ContextDefinitionInterface', class_implements($values['class']))) {
      throw new \Exception('ContextDefinition class must implement \Drupal\Core\Plugin\Context\ContextDefinitionInterface.');
    }
    $class = isset($values['class']) ? $values['class'] : 'Drupal\Core\Plugin\Context\ContextDefinition';
    $this->definition = new $class($values['value'], $values['label'], $values['required'], $values['multiple'], $values['description'], $values['default_value']);
  }

  /**
   * Returns the value of an annotation.
   *
   * @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface
   */
  public function get() {
    return $this->definition;
  }

}
