<?php

/**
 * @file
 * Install, update and uninstall functions for the token module.
 */

/**
 * Implements hook_requirements().
 */
function token_requirements($phase = 'runtime') {
  $requirements = [];

  if ($phase == 'runtime') {
    // Check for various token definition problems.
    $token_problems = token_get_token_problems();
    // Format and display each token problem.
    foreach ($token_problems as $problem_key => $problem) {
      if (!empty($problem['problems'])) {
        $problems = array_unique($problem['problems']);

        $build = [
          '#theme' => 'item_list',
          '#items' => $problems,
        ];

        $requirements['token-' . $problem_key] = [
          'title' => $problem['label'],
          'value' => \Drupal::service('renderer')->renderPlain($build),
          'severity' => $problem['severity'],
        ];
      }
    }
  }

  return $requirements;
}

/**
 * Implements hook_install().
 */
function token_install() {
  // Create a token view mode for each entity type.
  $info = \Drupal::entityTypeManager()->getDefinitions();
  foreach ($info as $entity_type => $entity_type_info) {
    // We're only interested in entity types with a view builder.
    if (!$entity_type_info->getViewBuilderClass()) {
      continue;
    }
    // Try to find a token view mode for that entity type.
    $storage = \Drupal::entityTypeManager()->getStorage('entity_view_mode');
    // Add a token view mode if it does not already exist.
    if (!$storage->load("$entity_type.token")) {
      $storage->create([
        'targetEntityType' => $entity_type,
        'id' => "$entity_type.token",
        'status' => TRUE,
        'label' => t('Token'),
      ])->save();
    }
  }
}

/**
 * Build a list of Drupal 6 tokens and their Drupal 7 token names.
 */
function _token_upgrade_token_list() {
  $tokens = [
    // Global tokens
    'user-name' => 'current-user:name',
    'user-id' => 'current-user:id',
    'user-mail' => 'current-user:mail',
    'site-url' => 'site:url',
    'site-name' => 'site:name',
    'site-slogan' => 'site:slogan',
    'site-mission' => 'site:mission',
    'site-mail' => 'site:mail',
    'site-date' => 'date:short',
    //'site-date-' => '', // Date tokens expanded below
    'current-page-path' => 'current-page:path',
    'current-page-url' => 'current-page:url',
    'page-number' => 'current-page:page-number',

    // Comment tokens
    'comment-cid' => 'comment:cid',
    'comment-nid' => 'comment:node:nid',
    'comment-title' => 'comment:title',
    'comment-body' => 'comment:body',
    'comment-author-name' => 'comment:author:name',
    'comment-author-mail' => 'comment:author:mail',
    //'comment-body-format' => '',
    //'comment-' => '', // Date tokens expanded below
    'comment-node-title' => 'comment:node',

    // Node tokens
    'nid' => 'node:nid',
    'type' => 'node:type',
    'type-name' => 'node:type-name',
    'language' => 'node:language',
    'title' => 'node:title',
    'author-uid' => 'node:author:uid',
    'author-name' => 'node:author:name',
    'author-mail' => 'node:author:mail',
    'node_comment_count' => 'node:comment-count',
    'unread_comment_count' => 'node:comment-count-new',
    'log' => 'node:log',
    //'' => '', // Date tokens expanded below
    //'mod-' => '', // Date tokens expanded below
    'menupath' => 'node:menu-link:parent:path][node:menu-link',
    'menu' => 'node:menu-link:menu-name',
    'menu-link-title' => 'node:menu-link',
    'menu-link-mlid' => 'node:menu-link:mlid',
    'menu-link-plid' => 'node:menu-link:parent:mlid',
    //'term' => 'node:term',
    //'term-id' => 'node:term:tid',
    //'vocab' => 'node:term:vocabulary',
    //'vocab-id' => 'node:term:vocabulary:vid',

    // Book tokens
    //'book' => 'node:book',
    //'book_id' => 'node:book:bid',
    //'bookpath' => 'node:book:path',

    // Taxonomy tokens
    'tid' => 'term:tid',
    'cat' => 'term:name',
    'cat-description' => 'term:description',
    'vid' => 'term:vocabulary:vid',
    'vocab' => 'term:vocabulary',
    'vocab-description' => 'term:vocabulary:description',

    // User tokens
    'user' => 'user:name',
    'uid' => 'user:uid',
    'mail' => 'user:mail',
    'reg-date' => 'user:created',
    'reg-since' => 'user:created:since',
    //'user-created' => '', // Date tokens expanded below
    'log-date' => 'user:last-login',
    'log-since' => 'user:last-login:since',
    //'user-last-login' => '', // Date tokens expanded below
    //'date-in-tz' => '',
    'account-url' => 'user:url',
    'account-edit' => 'user:edit-url',
  ];

  // Account for date tokens which need to be expanded.
  $tokens += _token_upgrade_token_date_list('site-', 'site:date');
  $tokens += _token_upgrade_token_date_list('', 'node:created');
  $tokens += _token_upgrade_token_date_list('mod-', 'node:changed');
  //$tokens += _token_upgrade_token_date_list('node-revision-', 'node:changed');
  $tokens += _token_upgrade_token_date_list('comment-', 'comment:created');
  $tokens += _token_upgrade_token_date_list('user-register-', 'user:created');
  $tokens += _token_upgrade_token_date_list('user-last-login-', 'user:last-login');

  return $tokens;
}

/**
 * Build a list of Drupal 6 date tokens and their Drupal 7 token names.
 */
function _token_upgrade_token_date_list($old_token, $new_token) {
  $tokens = [];
  $formats = [
    'yyyy' => 'Y',
    'yy' => 'y',
    'month' => 'F',
    'mon' => 'M',
    'mm' => 'm',
    'm' => 'n',
    'ww' => 'W',
    'date' => 'N',
    'day' => 'l',
    'ddd' => 'D',
    'dd' => 'd',
    'd' => 'j',
  ];
  foreach ($formats as $token_format => $date_format) {
    $tokens[$old_token . $token_format] = "$new_token:custom:$date_format";
  }
  $tokens[$old_token . 'raw'] = "$new_token:raw";
  $tokens[$old_token . 'since'] = "$new_token:since";
  return $tokens;
}

/**
 * Update a string containing Drupal 6 style tokens to Drupal 7 style tokens.
 *
 * @param $text
 *   A string containing tokens.
 * @param $updates
 *   An optional array of Drupal 7 tokens keyed by their Drupal 6 token name.
 *   The default tokens will be merged into this array. Note neither the old
 *   or new token names should include the surrounding bracket ([ and ])
 *   characters.
 * @return
 *   A string with the tokens upgraded
 *
 * @see _token_upgrade_token_list()
 */
function token_update_token_text($text, $updates = [], $leading = '[', $trailing = ']') {
  $updates += _token_upgrade_token_list();
  $regex = '/' . preg_quote($leading, '/') . '([^\s]*)' . preg_quote($trailing, '/') . '/';
  preg_match_all($regex, $text, $matches);

  foreach ($matches[1] as $index => $old_token) {
    if (isset($updates[$old_token])) {
      $new_token = $updates[$old_token];
      $text = str_replace("{$leading}{$old_token}{$trailing}", "[$new_token]", $text);
      // Also replace any tokens that have a -raw suffix.
      $text = str_replace("{$leading}{$old_token}-raw{$trailing}", "[$new_token]", $text);
    }
  }

  return $text;
}

/**
 * Get token problems.
 */
function token_get_token_problems() {
  // @todo Improve the duplicate checking to report which modules are the offenders.
  //$token_info = [];
  //foreach (module_implements('token_info') as $module) {
  //  $module_token_info = module_invoke($module, 'token_info');
  //  if (in_array($module, _token_core_supported_modules())) {
  //    $module .= '/token';
  //  }
  //  if (isset($module_token_info['types'])) {
  //    if (is_array($module_token_info['types'])) {
  //      foreach (array_keys($module_token_info['types']) as $type) {
  //        if (is_array($module_token_info['types'][$type])) {
  //          $module_token_info['types'][$type] += ['module' => $module];
  //        }
  //      }
  //    }
  //  }
  //  if (isset($module_token_info['tokens'])) {
  //    if (is_array($module_token_info['tokens'])) {
  //
  //    }
  //  }
  //  if (is_array($module_token_info)) {
  //    $token_info = array_merge_recursive($token_info, $module_token_info);
  //  }
  //}

  $token_info = \Drupal::token()->getInfo();
  $token_problems = [
    'not-array' => [
      'label' => t('Tokens or token types not defined as arrays'),
      'severity' => REQUIREMENT_ERROR,
    ],
    'missing-info' => [
      'label' => t('Tokens or token types missing name property'),
      'severity' => REQUIREMENT_WARNING,
    ],
    'type-no-tokens' => [
      'label' => t('Token types do not have any tokens defined'),
      'severity' => REQUIREMENT_INFO,
    ],
    'tokens-no-type' => [
      'label' => t('Token types are not defined but have tokens'),
      'severity' => REQUIREMENT_INFO,
    ],
    'duplicate' => [
      'label' => t('Token or token types are defined by multiple modules'),
      'severity' => REQUIREMENT_ERROR,
    ],
  ];

  // Check token types for problems.
  foreach ($token_info['types'] as $type => $type_info) {
    $real_type = !empty($type_info['type']) ? $type_info['type'] : $type;
    if (!is_array($type_info)) {
      $token_problems['not-array']['problems'][] = "\$info['types']['$type']";
      continue;
    }
    elseif (!isset($type_info['name'])) {
      $token_problems['missing-info']['problems'][] = "\$info['types']['$type']";
    }
    elseif (is_array($type_info['name'])) {
      $token_problems['duplicate']['problems'][] = "\$info['types']['$type']";
    }
    elseif (empty($token_info['tokens'][$real_type])) {
      $token_problems['type-no-tokens']['problems'][] = "\$info['types']['$real_type']";
    }
  }

  // Check tokens for problems.
  foreach ($token_info['tokens'] as $type => $tokens) {
    if (!is_array($tokens)) {
      $token_problems['not-array']['problems'][] = "\$info['tokens']['$type']";
      continue;
    }
    else {
      foreach (array_keys($tokens) as $token) {
        if (!is_array($tokens[$token])) {
          $token_problems['not-array']['problems'][] = "\$info['tokens']['$type']['$token']";
          continue;
        }
        elseif (!isset($tokens[$token]['name'])) {
          $token_problems['missing-info']['problems'][] = "\$info['tokens']['$type']['$token']";
        }
        elseif (is_array($tokens[$token]['name'])) {
          $token_problems['duplicate']['problems'][] = "\$info['tokens']['$type']['$token']";
        }
      }
    }
    if (!isset($token_info['types'][$type])) {
      $token_problems['tokens-no-type']['problems'][] = "\$info['types']['$type']";
    }
  }

  return $token_problems;
}
