<?php

/**
 * @file
 * Provide the curricula mapping suggestions.
 *
 * Archibald can use multiple curricula, like PER or LP21. However, working on
 * multiple curricula can be tedious. The dsb API provides a mechanism to
 * suggest corresponding elements in a curriculum based on a user's selection in
 * another curriculum. The Archibald Curricula Mapping module provides the logic
 * and user interface to do so.
 */

/**
 * Implements hook_menu().
 */
function archibald_curr_mapping_menu() {
  return array(
    'admin/config/services/archibald-curr-mapping' => array(
      'title' => "Archibald Curricula Mapping",
      'description' => 'Configure the curricula mapping suggestion service.',
      'access arguments' => array('administer archibald_curr_mapping'),
      'page callback' => 'drupal_get_form',
      'page arguments' => array('archibald_curr_mapping_admin_settings_form'),
      'file' => 'includes/archibald_curr_mapping.admin.inc',
    ),
    'archibald-curr-mapping/suggestions/%/%' => array(
      'page callback' => 'archibald_curr_mapping_ajax_suggestions',
      'page arguments' => array(2, 3),
      'access arguments' => array('view archibald_curr_mapping suggestions'),
      'type' => MENU_CALLBACK,
    ),
  );
}

/**
 * Implements hook_permission().
 */
function archibald_curr_mapping_permission() {
  return array(
    'administer archibald_curr_mapping' => array(
      'title' => t('Administer the Archibald Curricula Mapping module settings'),
    ),
    'view archibald_curr_mapping suggestions' => array(
      'title' => t('View Archibald Curricula Mapping suggestions'),
    ),
  );
}

/**
 * Implements hook_field_widget_form_alter().
 */
function archibald_curr_mapping_field_widget_form_alter(&$element, &$form_state, &$context) {
  if (
    !empty($element['#type']) &&
    $element['#type'] == 'archibald_curricula' &&
    !empty($element['#curricula_name'])
  ) {
    // Get available mappings. Check if this curriculum can receive suggestions,
    // and based on what.
    $available_mappings = archibald_curr_mapping_get_mappings();
    if (!empty($available_mappings[$element['#curricula_name']]['provides_for'])) {
      // Find all curricula we must fetch suggestions for.
      $active_curricula = array();

      foreach (
        $available_mappings[$element['#curricula_name']]['provides_for']
        as $key => $value
      ) {
        if (variable_get(
          "archibald_curr_mapping__from_{$key}_to_{$element['#curricula_name']}",
          FALSE
        )) {
          $active_curricula[$key] = $value;
        }
      }

      if (!empty($active_curricula)) {
        // We found a match. Activate the suggestion JS logic.
        $path = drupal_get_path('module', 'archibald_curr_mapping');

        drupal_add_js("$path/js/archibald_curr_mapping.templates.js", array(
          'weight' => -100,
        ));
        drupal_add_js("$path/js/archibald_curr_mapping.js", array(
          'weight' => 100,
        ));
        drupal_add_js(array(
          'archibaldCurrMapping' => array(
            'activateSuggestions' => array(
              $element['#curricula_name'] => array(
                'label' => $available_mappings[$element['#curricula_name']]['label'],
                'providesFor' => $active_curricula,
              ),
            ),
            // Prevent the alias from being looked up. Saves some DB lookups,
            // especially because the URL doesn't exist.
            'suggestionURL' => url('archibald-curr-mapping/suggestions', array('alias' => TRUE)),
          ),
        ), 'setting');

        if (module_exists('archibald_help')) {
          drupal_add_js(array(
            'archibaldCurrMapping' => array(
              'helpTooltip' =>
                '<span class="archibald-help-tooltip qtip-link"></span>' .
                theme('qtip_form', array(
                  'instance' => 'archibald_help',
                  'title' => t("Suggested items are automatically added"),
                  'tooltip' => t("Corresponding items are automatically activated in %label when you activate one in %name. You can uncheck the checkboxes to cancel this automatic addition. The suggestions will automatically disappear as soon as you click anywhere else.", array(
                      '%label' => $available_mappings[$element['#curricula_name']]['label'],
                      '%name' => $value,
                    )),
                )),
            ),
          ), 'setting');
        }

        drupal_add_css("$path/css/archibald_curr_mapping.css");
      }
    }
  }
}

/**
 * Page callback: load suggestions and send it back as JSON.
 */
function archibald_curr_mapping_ajax_suggestions($from, $to) {
  $suggestions = array();
  foreach ($_POST['item_ids'] as $item_id) {
    $suggestions = array_merge_recursive(
      archibald_curr_mapping_get_suggestions($from, $to, $item_id),
      $suggestions
    );
  }
  drupal_json_output($suggestions);
}

/**
 * Load suggestions.
 *
 * Load suggestion from the module that registered the "to" curriculum, based
 * on the item ID.
 *
 * @param string $source
 *    The source curricula.
 * @param string|array $target
 *    The curricula for which to provide suggestions. This can be an array or a
 *    comma-separated list. Each curriculum will be treated separately.
 * @param string $item_id
 *    The item in the source curriculum that was just selected.$
 *
 * @return array
 *    A list of suggestions. Each suggestion is an array with the following
 *    keys:
 *    - id: The item identifier in the target curriculum.
 *    - label: A human-readable string, which will be presented to the user as
 *      an explanation for the suggestion. Usually, a "path" is sufficient.
 *      E.g.: "Cycle 1 > Arts > Visual arts"
 */
function archibald_curr_mapping_get_suggestions($from, $to, $item_id) {
  $available_mappings = archibald_curr_mapping_get_mappings();
  $suggestions = array();

  // The "to" curricula can be a list.
  if (!is_array($to)) {
    $to = array_map('trim', explode(',', $to));
  }

  foreach ($to as $to_curriculum) {
    if (
      !empty($available_mappings[$to_curriculum]['provides_for']) &&
      !empty($available_mappings[$to_curriculum]['suggestion_callback']) &&
      function_exists($available_mappings[$to_curriculum]['suggestion_callback']) &&
      in_array($from, array_keys($available_mappings[$to_curriculum]['provides_for']))
    ) {
      if (!isset($suggestions[$from][$to_curriculum])) {
        $suggestions[$from][$to_curriculum]['suggestions'] = array();
      }

      $suggestions[$from][$to_curriculum]['suggestions'] = array_merge(
        $available_mappings[$to_curriculum]['suggestion_callback'](
          $from,
          $to_curriculum,
          $item_id
        ),
        $suggestions[$from][$to_curriculum]['suggestions']
      );
    }
  }

  return $suggestions;
}

/**
 * Helper function to fetch all available mappings.
 *
 * @see hook_archibald_curr_mapping()
 * @see hook_archibald_curr_mapping_alter()
 *
 * @return array
 *    A list of registered mappings. See hook_archibald_curr_mapping() for
 *    more information.
 */
function archibald_curr_mapping_get_mappings() {
  $available_mappings = &drupal_static(__FUNCTION__);
  if (!isset($available_mappings)) {
    $available_mappings = module_invoke_all('archibald_curr_mapping');
    drupal_alter('archibald_curr_mapping', $available_mappings);
  }
  return $available_mappings;
}
