<?php
/**
 * @file
 * Display Suite extras main functions.
 */

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\Display\EntityDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;

/**
 * Implements hook_layout_alter().
 */
function ds_extras_layout_alter(&$definitions) {
  if (\Drupal::config('ds_extras.settings')->get('hidden_region')) {
    foreach ($definitions as $key => $layout) {
      $definitions[$key]['regions']['ds_hidden']['label'] = t('Hidden');
    }
  }
}

/**
 * Implements hook_module_implements_alter().
 */
function ds_extras_module_implements_alter(&$implementations, $hook) {
  // Because it's possible to turn on/off features for DS extras,
  // we'll unset hooks here if necessary which otherwise do nothing at all.

  // Region to block
  $region_hooks = array(
    'ds_layout_region_alter',
    'entity_view_alter',
  );
  if (!\Drupal::config('ds_extras.settings')->get('region_to_block') && in_array($hook, $region_hooks)) {
    unset($implementations['ds_extras']);
  }

  // Extra fields
  $extra_fields_hooks = array(
    'field_extra_fields',
  );
  if (!\Drupal::config('ds_extras.settings')->get('fields_extra') && in_array($hook, $extra_fields_hooks)) {
    unset($implementations['ds_extras']);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function ds_extras_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state) {
  // Region to block only fires if there is a layout and we're working on the
  // a view mode which is not equal to default.
  $mode = $form_state->getFormObject()->getEntity()->getMode();
  if (isset($form['#ds_layout']) && $mode != 'default' && \Drupal::config('ds_extras.settings')
      ->get('region_to_block')
  ) {

    // Get the entity_type, bundle and view mode.
    $entity_type = $form['#entity_type'];
    $bundle = $form['#bundle'];
    $view_mode = $mode;

    $region_blocks_options = array();
    $region_blocks = \Drupal::config('ds_extras.settings')->get('region_blocks');

    if (!empty($region_blocks)) {
      foreach ($region_blocks as $key => $block) {
        if ($block['info'] == "{$entity_type}_{$bundle}_{$view_mode}") {
          $region_blocks_options[$key] = t('Remove') . ' ' . $block['title'];
        }
      }
    }

    $form['region_to_block'] = array(
      '#type' => 'details',
      '#group' => 'additional_settings',
      '#title' => t('Block regions (deprecated)'),
      '#description' => t('Create additional regions in this layout which will be exposed as blocks.')
    );
    $url = Url::fromUri('https://www.drupal.org/node/2754967');
    $link = Drupal::l(t('handbook page'), $url);
    $form['region_to_block']['deprecated'] = array(
      '#markup' => '<b>' . t('This functionality is not compatible with some caching modules. Read the @handbook for a better alternative.', array('@handbook' => $link)) . '</b>'
    );
    $form['region_to_block']['new_block_region'] = array(
      '#type' => 'textfield',
      '#title' => t('Region name'),
      '#description' => t('Enter a name to create a new region.'),
    );
    $form['region_to_block']['new_block_region_key'] = array(
      '#title' => t('Machine name'),
      '#type' => 'machine_name',
      '#default_value' => '',
      '#maxlength' => 32,
      '#required' => FALSE,
      '#description' => t('The machine-readable name of this block region. This name must contain only lowercase letters and underscores. This name must be unique.'),
      '#disabled' => FALSE,
      '#machine_name' => array(
        'exists' => 'ds_extras_region_to_block_unique',
        'source' => array('region_to_block', 'new_block_region'),
      ),
    );

    if (!empty($region_blocks_options)) {
      $form['region_to_block']['remove_block_region'] = array(
        '#type' => 'checkboxes',
        '#title' => t('Existing block regions'),
        '#options' => $region_blocks_options,
        '#description' => t('Check the regions you want to remove.'),
      );
    }

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

/**
 * Submit callback after Field UI submission of a views display.
 */
function ds_extras_vd_field_ui_submit($form, FormStateInterface $form_state) {
  // Add the 'type' key to the extra title key so we can ditch the notice.
  $form_state->setValue('fields', array('title' => array('type' => 'hidden')));
}

/**
 * Submit callback: manage block regions.
 */
function ds_extras_block_submit($form, FormStateInterface $form_state) {

  // Create new region.
  if ($new_block_region = $form_state->getValue('new_block_region')) {

    // Get the entity_type, bundle and view mode.
    $entity_type = $form['#entity_type'];
    $bundle = $form['#bundle'];
    $view_mode = $form_state->getFormObject()->getEntity()->getMode();

    $block = array(
      'title' => $new_block_region,
      'info' => "{$entity_type}_{$bundle}_{$view_mode}",
    );

    $block_key = $form_state->getValue('new_block_region_key');
    $region_blocks = \Drupal::config('ds_extras.settings')->get('region_blocks');
    if (empty($region_blocks)) {
      $region_blocks = array();
    }
    $region_blocks[$block_key] = $block;
    \Drupal::configFactory()->getEditable('ds_extras.settings')->set('region_blocks', $region_blocks)->save();
  }

  // Remove a region.
  if ($form_state->hasValue('remove_block_region')) {
    $region_blocks = \Drupal::config('ds_extras.settings')->get('region_blocks');
    $region_removal_status = $form_state->getValue('remove_block_region');

    foreach ($region_removal_status as $key => $value) {
      if ($value !== 0 && $key == $value) {
        unset($region_blocks[$key]);

        // Make sure there is no active block instance for this ds block region.
        if (\Drupal::moduleHandler()->moduleExists('block')) {
          $ids = \Drupal::entityQuery('block')
            ->condition('plugin', 'ds_region_block:' . $key)
            ->execute();
          $block_storage = \Drupal::service('entity_type.manager')->getStorage('block');
          foreach ($block_storage->loadMultiple($ids) as $block) {
            $block->delete();
          }
        }
      }
    }

    // Clear cached block and ds plugin defintions
    \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
    \Drupal::service('plugin.manager.ds')->clearCachedDefinitions();

    // Remove the region from ds settings
    \Drupal::configFactory()->getEditable('ds_extras.settings')->set('region_blocks', $region_blocks)->save();
  }
}

/**
 * Return unique region to block.
 */
function ds_extras_region_to_block_unique($name) {
  $region_blocks = \Drupal::config('ds_extras.settings')->get('region_blocks');
  if (empty($region_blocks)) {
    $region_blocks = array();
  }
  $value = strtr($name, array('-' => '_'));
  return isset($region_blocks[$value]) ? TRUE : FALSE;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function ds_extras_form_ds_admin_form_alter(&$form, FormStateInterface $form_state) {
  $config = \Drupal::configFactory()->getEditable('ds_extras.settings');

  $form['fs2'] = array(
    '#type' => 'details',
    '#title' => t('Extra fields'),
    '#group' => 'additional_settings',
    '#weight' => 2,
    '#tree' => TRUE,
  );

  $form['fs2']['fields_extra'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable extra fields'),
    '#description' => t('Make fields from other modules available on the "Manage display" screens.'),
    '#default_value' => $config->get('fields_extra'),
  );

  $form['fs2']['fields_extra_list'] = array(
    '#type' => 'textarea',
    '#description' => t('Enter fields line by line, where each line is a combination of entity type, bundle and field name. E.g. node|article|tweetbutton. It might be possible that the actual content of the field depends on configuration of that field/module.'),
    '#default_value' => implode("\n", $config->get('fields_extra_list')),
    '#states' => array(
      'visible' => array(
        'input[name="fs2[fields_extra]"]' => array('checked' => TRUE),
      ),
    ),
  );

  $form['fs3']['field_permissions'] = array(
    '#type' => 'checkbox',
    '#title' => t('Field permissions'),
    '#description' => t('Enables view permissions on all Display Suite fields.'),
    '#default_value' => $config->get('field_permissions'),
  );

  if (\Drupal::moduleHandler()->moduleExists('block')) {
    $url = Url::fromUri('https://www.drupal.org/node/2754967');
    $link = Drupal::l(t('handbook page'), $url);
    $form['fs3']['region_to_block'] = array(
      '#type' => 'checkbox',
      '#title' => t('Region to block (deprecated)'),
      '#description' => t('Create additional regions exposed as block. Note: this will not work on the default view mode.') . '<br/>' . '<b>' . t('This functionality is not compatible with some caching modules. Read the @handbook for a better alternative.', array('@handbook' => $link)) . '</b>',
      '#default_value' => $config->get('region_to_block'),
    );
  }

  $form['fs3']['switch_field'] = array(
    '#type' => 'checkbox',
    '#title' => t('View mode switcher'),
    '#description' => t('Adds a field with links to switch view modes inline with Ajax. Only works for nodes at this time. It does not work in combination with the reset layout.'),
    '#default_value' => $config->get('switch_field'),
  );

  $form['fs3']['hidden_region'] = array(
    '#type' => 'checkbox',
    '#title' => t('Hidden region'),
    '#description' => t('Add a hidden region to the layouts. Fields will be built but not printed.'),
    '#default_value' => $config->get('hidden_region'),
  );

  $form['fs3']['override_node_revision'] = array(
    '#type' => 'checkbox',
    '#title' => t('Custom node revision view mode'),
    '#description' => t('Override the node revision page view with a custom view mode'),
    '#default_value' => $config->get('override_node_revision'),
  );

  $options = array();
  $view_modes = \Drupal::service('entity_display.repository')->getViewModes('node');
  foreach ($view_modes as $key => $view_mode) {
    $options[$key] = $view_mode['label'];
  }

  $form['fs3']['override_node_revision_view_mode'] = array(
    '#type' => 'select',
    '#description' => t('The revision view mode'),
    '#default_value' => $config->get('override_node_revision_view_mode'),
    '#options' => $options,
    '#states' => array(
      'visible' => array(
        'input[name="fs3[override_node_revision]"]' => array('checked' => TRUE),
      ),
    ),
  );

  $form['#submit'][] = 'ds_extras_settings_submit';
  $form['#attached']['library'][] = 'ds_extras/admin';
}

/**
 * Submit callback: Extras settings screen.
 */
function ds_extras_settings_submit($form, FormStateInterface $form_state) {
  $values = $form_state->getValues();

  $extra_fields = array();
  if (!empty($values['fs2']['fields_extra_list'])) {
    $extra_fields = explode("\n", str_replace("\r", '', $values['fs2']['fields_extra_list']));
  }

  $config = \Drupal::configFactory()->getEditable('ds_extras.settings')
    ->set('fields_extra', $values['fs2']['fields_extra'])
    ->set('fields_extra_list', $extra_fields)
    ->set('field_permissions', $values['fs3']['field_permissions'])
    ->set('switch_field', $values['fs3']['switch_field'])
    ->set('hidden_region', $values['fs3']['hidden_region'])
    ->set('override_node_revision', $values['fs3']['override_node_revision'])
    ->set('override_node_revision_view_mode', $values['fs3']['override_node_revision_view_mode']);
  if (\Drupal::moduleHandler()->moduleExists('block')) {
    $config->set('region_to_block', $values['fs3']['region_to_block']);
  }
  $config->save();

  // Mark the router items for rebuild
  \Drupal::service('router.builder')->setRebuildNeeded();

  // Clear layout plugin caches
  \Drupal::service('plugin.manager.layout_plugin')->clearCachedDefinitions();

}

/**
 * DS fields access.
 *
 * @param $field
 *   The machine name of the field
 * @param $entity_type
 *   The name of the entity type.
 *
 * @return bool
 */
function ds_extras_ds_field_access($field, $entity_type) {
  if (\Drupal::currentUser()->hasPermission('view ' . $field . ' on ' . $entity_type)) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Implements hook_entity_view_alter().
 */
function ds_extras_entity_view_alter(&$build, EntityInterface $entity, EntityDisplayInterface $display) {

  $entity_type = $entity->getEntityTypeId();
  $bundle = $entity->bundle();
  $view_mode = $display->getMode();

  $block_data = &drupal_static('ds_block_region');
  $region_blocks = \Drupal::config('ds_extras.settings')->get('region_blocks');

  if (empty($region_blocks) || empty($build)) {
    return;
  }

  $properties = array();
  foreach (Element::properties($build) as $property) {
    $properties[$property] = $build[$property];
  }
  $properties['#view_mode'] = $view_mode;

  if ($ds_settings = $display->getThirdPartySettings('ds')) {
    foreach ($region_blocks as $block_key => $block) {
      if ($block['info'] == "{$entity_type}_{$bundle}_{$view_mode}" && isset($ds_settings['regions'][$block_key]) && !empty($ds_settings['regions'][$block_key])) {
        foreach ($ds_settings['regions'][$block_key] as $field) {
          if (isset($build[$field])) {
            $block_data[$block_key][$field] = $build[$field];
            unset($build[$field]);
          }
        }
        if (isset($block_data[$block_key]) && is_array($block_data[$block_key])) {
          $block_data[$block_key] += $properties;
        }
      }
    }
  }
}

/**
 * Implements hook_ds_layout_region_alter().
 */
function ds_extras_ds_layout_region_alter($context, &$region_info) {

  $region_blocks = \Drupal::config('ds_extras.settings')->get('region_blocks');

  // Bail out if region_blocks is empty or we are working on default view mode.
  if (empty($region_blocks) || $context['view_mode'] == 'default') {
    return;
  }

  $entity_type = $context['entity_type'];
  $bundle = $context['bundle'];
  $view_mode = $context['view_mode'];

  foreach ($region_blocks as $block_key => $block) {
    if ($block['info'] == "{$entity_type}_{$bundle}_{$view_mode}") {
      $region_info['region_options'][$block_key] = $block['title'];
      if (isset($region_info['table_regions'])) {
        $region_info['table_regions'][$block_key] = array(
          'title' => Html::escape($block['title']),
          'message' => t('No fields are displayed in this region'),
        );
      }
    }
  }
}

/**
 * Implements hook_entity_extra_field_info().
 */
function ds_extras_entity_extra_field_info() {
  $extra = array();

  if (\Drupal::config('ds_extras.settings')->get('fields_extra')) {
    $fields = \Drupal::config('ds_extras.settings')->get('fields_extra_list');
    if (empty($fields)) {
      return $extra;
    }
    foreach ($fields as $field) {
      $field = trim($field);
      if (!empty($field)) {
        list($entity, $bundle, $field_name) = explode('|', $field);
        $extra[Html::escape($entity)][Html::escape($bundle)]['display'][$field_name] = array(
          'label' => Unicode::ucfirst(str_replace('_', ' ', Html::escape($field_name))),
          'description' => Unicode::ucfirst(str_replace('_', ' ', Html::escape($field_name))),
          'weight' => 0,
        );
      }
    }
  }

  return $extra;
}
