<?php

/**
 * @file
 * Main file of Menu Link Attributes module.
 */

use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Url;
use Drupal\Core\Menu\MenuLinkInterface;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function menu_link_attributes_form_menu_link_content_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
  $attributes = \Drupal::config('menu_link_attributes.config')->get('attributes') ?: [];
  $menu_link = $form_state->getFormObject()->getEntity();
  $menu_link_options = $menu_link->link->first()->options ?: [];
  $menu_link_attributes = isset($menu_link_options['attributes']) ? $menu_link_options['attributes'] : [];

  $form['options']['attributes'] = [
    '#type' => 'details',
    '#title' => t('Attributes'),
    '#weight' => -2,
    '#tree' => TRUE,
  ];

  $config_path = Url::fromRoute('menu_link_attributes.config')->toString();
  $referrer_path = parse_url(\Drupal::request()->headers->get('referer'))['path'];
  $coming_from_config = $config_path == $referrer_path;

  // Open <details> element if coming from config page.
  if ($coming_from_config) {
    $form['options']['attributes']['#open'] = TRUE;
  }

  $destination = \Drupal::destination()->getAsArray();
  $config_path = Url::fromRoute('menu_link_attributes.config', [], ['query' => $destination])->toString();

  if (count($attributes)) {
    $form['options']['attributes']['#description'] = '<small>' . t('Manage available attributes <a href="@config">here</a>.', ['@config' => $config_path]) . '</small>';
  }
  else {
    $form['options']['attributes']['help'] = [
      '#markup' => t('Manage available attributes <a href="@config">here</a>.', ['@config' => $config_path]),
    ];
  }

  $autofocus = FALSE;

  // Iterate all defined attributes and create text field for them.
  foreach ($attributes as $attribute => $info) {
    // Provide default label / description for attributes.
    if (empty($info['label'])) {
      $info['label'] = str_replace('-', ' ', Unicode::ucfirst($attribute));
    }
    if (empty($info['description'])) {
      $info['description'] = t('Enter value for <code>@attribute</code> attribute.', ['@attribute' => $attribute]);
    }

    // Determine type based on options field.
    $type = !empty($info['options']) ? 'select' : 'textfield';

    $form['options']['attributes'][$attribute] = [
      '#type' => $type,
      '#title' => $info['label'],
      '#description' => $info['description'],
      '#default_value' => isset($menu_link_attributes[$attribute]) ? $menu_link_attributes[$attribute] : '',
    ];

    // Fill options if select list.
    if ($type == 'select') {
      $form['options']['attributes'][$attribute]['#empty_option'] = t('- Select -');
      $form['options']['attributes'][$attribute]['#options'] = $info['options'];
    }

    // Add "autofocus" attribute for first attribute input field
    // if coming from config page.
    if ($coming_from_config && !$autofocus) {
      $form['options']['attributes'][$attribute]['#attributes'] = ['autofocus' => 'autofocus'];
      $autofocus = TRUE;
    }
  }

  $form['actions']['submit']['#submit'][] = 'menu_link_attributes_menu_link_content_form_submit';
}

/**
 * Submit function for menu add / edit form.
 */
function menu_link_attributes_menu_link_content_form_submit($form, FormStateInterface $form_state) {
  $menu_link = $form_state->getFormObject()->getEntity();
  $options = ['attributes' => $form_state->getValue('attributes')];
  $menu_link_options = $menu_link->link->first()->options;

  $menu_link->link->first()->options = array_merge($menu_link_options, $options);
  $menu_link->save();
}

/**
 * Implements hook_preprocess_menu().
 */
function menu_link_attributes_preprocess_menu(&$variables) {
  menu_link_attibutes_set_attributes($variables['items']);
}

/**
 * Set the attributes on the given items.
 *
 * @param array $items
 *   List of menu items.
 */
function menu_link_attibutes_set_attributes($items) {
  foreach ($items as &$item) {
    $menu_link_attributes = menu_link_attributes_get_attributes($item['original_link']);

    if (count($menu_link_attributes)) {
      $url_attributes = $item['url']->getOption('attributes') ?: [];
      $attributes = array_merge($url_attributes, $menu_link_attributes);

      $item['url']->setOption('attributes', $attributes);
    }

    if (!empty($item['below'])) {
      menu_link_attibutes_set_attributes($item['below']);
    }
  }
}

/**
 * Get menu link attributes.
 *
 * @param \Drupal\Core\Menu\MenuLinkInterface $menu_link_content_plugin
 *   A menu link content plugin.
 *
 * @return array
 *   An array with the attributes.
 */
function menu_link_attributes_get_attributes(MenuLinkInterface $menu_link_content_plugin) {
  $attributes = [];

  try {
    $plugin_id = $menu_link_content_plugin->getPluginId();
  }
  catch (PluginNotFoundException $e) {
    return $attributes;
  }

  if (strpos($plugin_id, ':') === FALSE) {
    return $attributes;
  }

  list($entity_type, $uuid) = explode(':', $plugin_id, 2);

  if ($entity_type == 'menu_link_content') {
    try {
      $entity = \Drupal::entityManager()->loadEntityByUuid($entity_type, $uuid);

      $options = $entity->link->first()->options;
      $attributes = isset($options['attributes']) ? $options['attributes'] : [];

      // Class attribute needs special handling because Drupal
      // may add an "active" class to it.
      if (isset($attributes['class']) && !is_array($attributes['class'])) {
        $attributes['class'] = explode(' ', $attributes['class']);
      }

      // Remove empty attributes by checking their string length.
      foreach ($attributes as &$attribute) {
        if (!is_array($attribute) && Unicode::strlen($attribute) === 0) {
          unset($attribute);
        }
      }

    } catch (PluginNotfoundException $e) {
      // Make sure we catch failed entity loadings.
    }
  }

  return $attributes;
}
