<?php
/**
 * @file
 * Generates form elements for searching for entities with entity type selection
 * and a autocomplete field and its required callbacks.
 */

/**
 * Implements hook_menu().
 */
function uuid_entity_autocomplete_menu() {
  $items = array();
  $items['uuid-entity-autocomplete/autocomplete/%'] = array(
    'page callback' => 'uuid_entity_autocomplete',
    'page arguments' => array(2),
    'access callback' => 'entity_access',
    'access arguments' => array('view', 2),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function uuid_entity_autocomplete_theme() {
  return array(
    'uuid_entity_autocomplete_current_items' => array(
      'render element' => 'element',
    ),
  );
}

/**
 * Implements hook_entity_info_alter().
 */
function uuid_entity_autocomplete_entity_info_alter(&$entity_info) {
  // The username should be the label for users.
  $entity_info['user']['entity keys']['label'] = 'name';
}

/**
 * Generate form fields for chosing an entity type and for seaching for entities
 * using autocomplete.
 *
 * @param $defaults
 *   Array with default values for form fields.
 * @param $entity_type
 *   Optional entity type to limit form to, and to disable the type selection
 *   field.
 */
function uuid_entity_autocomplete_form_multiple($defaults, $entity_type = NULL) {
  $form = array(
    '#prefix' => '<div id="uuid-entity-autocomplete-multiple">',
    '#suffix' => '</div>',
  );

  if (!empty($defaults['current_items'])) {
    $form['current_items'] = uuid_entity_autocomplete_current_items_form($defaults);
    $form['current_items']['#prefix'] = '<h3>' . t('Current items') . '</h3>';
  }

  $form['add_item'] = uuid_entity_autocomplete_form($defaults, $entity_type);
  $form['add_item']['#prefix'] = '<h3>' . t('Add new item') . '</h3>';
  $form['add_item']['add_item_submit'] = array(
    '#type' => 'submit',
    '#value' => t('Add new item'),
    '#submit' => array('uuid_entity_autocomplete_add_item_submit'),
    // Restrict validation to appropriate fields only.
    '#limit_validation_errors' => array(array('add_item', 'entity')),
    '#ajax' => array(
      'callback' => 'uuid_entity_autocomplete_ajax_callback',
      'wrapper' => 'uuid-entity-autocomplete-multiple',
    ),
  );

  return $form;
}

/**
 * Return a table to show corrent items added.
 */
function uuid_entity_autocomplete_current_items_form($values) {
  $form = array(
    '#tree' => TRUE,
    '#theme' => 'uuid_entity_autocomplete_current_items',
  );

  $entity_types = entity_get_info();
  foreach ($values['current_items'] as $entity_type => $uuids) {
    // Load entities for getting their labels.
    $entities = entity_uuid_load($entity_type, $uuids);
    $labels = array();
    foreach ($entities as $id => $entity) {
      $labels[$entity->uuid] = entity_label($entity_type, $entity);
    }

    // Loop over all entities and add hidden fields for all of them.
    $form[$entity_type] = array();
    foreach ($uuids as $delta => $uuid) {
      $entity = &$entities[$entity_type];
      $form[$entity_type][$delta] = array();
      $form[$entity_type][$delta]['entity_uuid'] = array(
        '#type' => 'hidden',
        '#value' => $uuid,
      );
      $form[$entity_type][$delta]['type'] = array(
        '#type' => 'markup',
        '#markup' => $entity_types[$entity_type]['label'],
      );
      $form[$entity_type][$delta]['label'] = array(
        '#type' => 'markup',
        '#markup' => $labels[$uuid],
      );
      $form[$entity_type][$delta]['remove'] = array(
        '#type' => 'submit',
        '#value' => t('Delete'),
        '#name' => 'remove_' . $delta,
        '#submit' => array('uuid_entity_autocomplete_remove_item_submit'),
        // Bypass validation for 'Delete' button.
        '#limit_validation_errors' => array(),
        '#ajax' => array(
          'callback' => 'uuid_entity_autocomplete_ajax_callback',
          'wrapper' => 'uuid-entity-autocomplete-multiple',
        ),
      );
    }
  }
  return $form;
}

/**
 * Ajax callback for multiple menu link forms.
 */
function uuid_entity_autocomplete_ajax_callback($form, $form_state) {
  // Clear entity specific value. The other fields will default to previous
  // entered values.
  $form['conditions']['plugins']['context_uuid']['values']['add_item']['entity']['entity_uuid']['#value'] = '';

  // @todo: remove hardcoded 'context_uuid'.
  return $form['conditions']['plugins']['context_uuid']['values'];
}

/**
 * Generate form fields for chosing an entity type and for seaching for entities
 * using autocomplete.
 *
 * @param $defaults
 *   Array with default values for form fields.
 * @param $entity_type
 *   Optional entity type to limit form to, and to disable the type selection
 *   field.
 */
function uuid_entity_autocomplete_form($defaults, $entity_type = NULL) {
  $form = array();

  $form['entity'] = array(
    '#prefix' => '<div id="uuid-entity-autocomplete">',
    '#suffix' => '</div>',
  );

  // A non entity type specific form was resquested. Display te type field
  // so users can choose a type before using the autocomplete.
  if (empty($entity_type)) {
    $entity_type = !empty($defaults['add_item']['entity']['entity_type']) ? $defaults['add_item']['entity']['entity_type'] : '';
    $form['entity']['entity_type'] = array(
      '#type' => 'select',
      '#title' => t('Type'),
      '#options' => array('' => t('Choose one')) + uuid_entity_autocomplete_types(),
      '#ajax' => array(
        'callback' => 'uuid_entity_autocomplete_add_item_choose_entity_type',
        'wrapper' => 'uuid-entity-autocomplete',
        'method' => 'replace',
      ),
      '#default_value' => $entity_type,
    );
  }

  // Entity type was not found in default values. Display a message for the user
  // to choose a type first.
  if (empty($entity_type)) {
    $form['entity']['entity_uuid'] = array(
      '#type' => 'item',
      '#title' => t('Title'),
      '#markup' => '<p>' . t('Choose a type first.') . '</p>',
    );
  }
  else {
    $form['entity']['entity_uuid'] = array(
      '#type' => 'textfield',
      '#title' => t('Title'),
      '#autocomplete_path' => 'uuid-entity-autocomplete/autocomplete/' . $entity_type,
      '#default_value' => !empty($defaults['entity_uuid']) ? $defaults['entity_uuid'] : '',
    );
  }

  return $form;
}

/**
 * Submit handler for 'Add item' ajax submit button.
 */
function uuid_entity_autocomplete_add_item_submit($form, &$form_state) {
  // @todo: remove hardcoded context_uuid and make it independent not depend on
  // context uuid module.
  // @todo: not sure why can'te get values from $form_state['values'].
  $values = &$form_state['input']['conditions']['plugins']['context_uuid']['values']['add_item']['entity'];

  if (!empty($values['entity_uuid'])) {
    extract($values);
    $form_state['storage']['current_items'][$entity_type][] = $entity_uuid;

    // Cause the form to rebuild so the new items show up.
    $form_state['rebuild'] = TRUE;
  }
}

function uuid_entity_autocomplete_remove_item_submit($form, &$form_state) {
  // @todo: not sure why can'te get values from $form_state['values'].
  $values = &$form_state['values'];

  // @todo: remove hardcoded context_uuid and make it independent not depend on
  // context uuid module.
  $entity_type = key($values['conditions']['plugins']['context_uuid']['values']['current_items']);
  $delta = key($values['conditions']['plugins']['context_uuid']['values']['current_items'][$entity_type]);

  // Unset the item so it get removed from the form.
  unset($form_state['storage']['current_items'][$entity_type][$delta]);

  // Cause the form to rebuild so the item is removed from the list.
  $form_state['rebuild'] = TRUE;
}

/**
 * Ajax callback for choosing an entity type.
 */
function uuid_entity_autocomplete_add_item_choose_entity_type($form, $form_state) {
  $condition_form = &$form['conditions']['plugins']['context_uuid']['values'];
  return !empty($condition_form['add_item']) ? $condition_form['add_item']['entity'] : $condition_form['entity'];
}

/**
 * Return a list of entity types compatible with uuid and have label keys, since
 * this is what we use for filtering autocomplete results.
 *
 * @param $access_check
 *   Whether to return only the entities types the user is able to view
 *   entities.
 */
function uuid_entity_autocomplete_types($access_check = TRUE) {
  $types = array();
  $entity_types = entity_get_info();
  foreach ($entity_types as $entity_type => $entity_type_info) {
    if ($access_check && !entity_access('view', $entity_type)) {
      continue;
    }
    // Only include entities that have a label key set. And that supports UUID.
    if (!empty($entity_type_info['entity keys']['label']) && !empty($entity_type_info['entity keys']['uuid'])) {
      $types[$entity_type] = $entity_type_info['label'];
    }
  }
  return $types;
}

/**
 * Autocomplete callback for entities.
 */
function uuid_entity_autocomplete($type, $string = '') {
  $matches = array();
  $entity_type = entity_get_info($type);
  if (!empty($string) && !empty($entity_type['entity keys']['label'])) {
    $label_key = $entity_type['entity keys']['label'];
    $query = new EntityFieldQuery();
    $query->entityCondition('entity_type', $type)
      ->propertyCondition($label_key, $string, 'CONTAINS')
      ->propertyOrderBy($label_key)
      ->range(0, 15);

    // Only search for published nodes.
    if ($type == 'node') {
      $query->propertyCondition('status', 1);
    }

    if ($result = $query->execute()) {
      $entities = entity_load($type, array_keys($result[$type]));
      $uuids = entity_get_uuid_by_id($type, array_keys($entities));
      foreach ($entities as $entity_id => $entity) {
        $uuid = $uuids[$entity_id];
        // If content is language specific show language code.
        if (!empty($entity->language) && $entity->language != LANGUAGE_NONE) {
          $matches[$uuid] = t('[@language] @label', array('@language' => $entity->language, '@label' => entity_label($type, $entity)));
        }
        else {
          $matches[$uuid] = check_plain(entity_label($type, $entity));
        }
      }
    }
  }
  drupal_json_output($matches);
}

/**
 * Theme current menu links table.
 */
function theme_uuid_entity_autocomplete_current_items($variables) {
  $form = $variables['element'];

  $rows = array();
  foreach (element_children($form) as $key) {
    foreach (element_children($form[$key]) as $inner_key) {
      $rows[] = array(
        drupal_render($form[$key][$inner_key]['type']),
        drupal_render($form[$key][$inner_key]['label']),
        drupal_render($form[$key][$inner_key]['remove']),
      );
    }
  }

  $headers = array(
    t('Type'),
    t('Title'),
    t('Operations'),
  );

  $output = theme('table', array('header' => $headers, 'rows' => $rows, 'attributes' => array('id' => 'multiple-node-menu-current-links')));
  $output .= drupal_render_children($form);

  return $output;
}
