<?php

namespace Drupal\token\Element;

use Drupal\Component\Utility\Html;
use Drupal\Core\Render\Element\Table;

/**
 * Provides a render element for a token tree table.
 *
 * @RenderElement("token_tree_table")
 */
class TokenTreeTable extends Table {

  protected static $cssFilter = [' ' => '-', '_' => '-', '/' => '-', '[' => '-', ']' => '', ':' => '--', '?' => '', '<' => '-', '>' => '-'];

  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    $class = get_class($this);
    return [
      '#header' => [],
      '#rows' => [],
      '#token_tree' => [],
      '#columns' => ['name', 'token', 'description'],
      '#empty' => '',
      '#show_restricted' => FALSE,
      '#show_nested' => FALSE,
      '#skip_empty_values' => FALSE,
      '#click_insert' => TRUE,
      '#sticky' => FALSE,
      '#responsive' => TRUE,
      '#input' => FALSE,
      '#pre_render' => [
        [$class, 'preRenderTokenTree'],
        [$class, 'preRenderTable'],
      ],
      '#theme' => 'table__token_tree',
      '#attached' => [
        'library' => [
          'token/token',
        ],
      ],
    ];
  }

  /**
   * Pre-render the token tree to transform rows in the token tree.
   *
   * @param array $element
   *
   * @return array
   *   The processed element.
   */
  public static function preRenderTokenTree($element) {
    $multiple_token_types = count($element['#token_tree']) > 1;
    foreach ($element['#token_tree'] as $token_type => $type_info) {
      // Do not show nested tokens.
      if (!empty($type_info['nested']) && empty($element['#show_nested'])) {
        continue;
      }

      if ($multiple_token_types) {
        $row = static::formatRow($token_type, $type_info, $element['#columns'], TRUE);
        $element['#rows'][] = $row;
      }

      foreach ($type_info['tokens'] as $token => $token_info) {
        if (!empty($token_info['restricted']) && empty($element['#show_restricted'])) {
          continue;
        }
        if ($element['#skip_empty_values'] && empty($token_info['value']) && !empty($token_info['parent']) && !isset($tree[$token_info['parent']]['value'])) {
          continue;
        }
        if ($multiple_token_types && !isset($token_info['parent'])) {
          $token_info['parent'] = $token_type;
        }
        $row = static::formatRow($token, $token_info, $element['#columns']);
        $element['#rows'][] = $row;
      }
    }

    if (!empty($element['#rows'])) {
      $element['#attached']['library'][] = 'token/jquery.treeTable';
    }

    // Fill headers if one is not specified.
    if (empty($element['#header'])) {
      $column_map = [
        'name' => t('Name'),
        'token' => t('Token'),
        'value' => t('Value'),
        'description' => t('Description'),
      ];
      foreach ($element['#columns'] as $col) {
        $element['#header'][] = $column_map[$col];
      }
    }

    $element['#attributes']['class'][] = 'token-tree';

    if ($element['#click_insert']) {
      $element['#caption'] = t('Click a token to insert it into the field you\'ve last clicked.');
      $element['#attributes']['class'][] = 'token-click-insert';
    }

    return $element;
  }

  protected static function cleanCssIdentifier($id) {
    return 'token-' . Html::cleanCssIdentifier(trim($id, '[]'), static::$cssFilter);
  }

  protected static function formatRow($token, $token_info, $columns, $is_group = FALSE) {
    $row = [
      'id' => static::cleanCssIdentifier($token),
      'data-tt-id' => static::cleanCssIdentifier($token),
      'class' => [],
      'data' => [],
    ];

    foreach ($columns as $col) {
      switch ($col) {
        case 'name':
          $row['data'][$col] = $token_info['name'];
          break;

        case 'token':
          $row['data'][$col]['data'] = $token;
          $row['data'][$col]['class'][] = 'token-key';
          break;

        case 'description':
          $row['data'][$col] = isset($token_info['description']) ? $token_info['description'] : '';
          break;

        case 'value':
          $row['data'][$col] = !$is_group && isset($token_info['value']) ? $token_info['value'] : '';
          break;
      }
    }

    if ($is_group) {
      // This is a token type/group.
      $row['class'][] = 'token-group';
    }
    elseif (!empty($token_info['parent'])) {
      $row['data-tt-parent-id'] = static::cleanCssIdentifier($token_info['parent']);
      unset($row['parent']);
    }

    return $row;
  }
}
