<?php

/**
 * @file
 * Preprocessors, helper functions, and theme callbacks.
 */

/**
 * @addtogroup themeable
 * @{
 */

/**
 * Renders a curricula data summary.
 *
 * Mimicks the summary tree found in the Curricula UI JS application. Renders
 * all  active items in a tree structure. This will also attach the CSS from the
 * Curricula UI JS component to re-use the styles.
 *
 * @param $vars
 *    An associative array containing:
 *   - items: An array containing the item database. This array will be filtered
 *     to only include items with the "active" property set to true. Each active
 *     item will be rendered as an element of an unordered list, which in turn
 *     contains all child items in a child list.
 *   - html: A boolean. If true, items will not be escaped through
 *     check_plain(). Defaults to false.
 */
function theme_archibald_curricula_summary($vars) {
  // Load the library. We want to re-use the CSS rules. However, in order to
  // prevent JS errors, we need to load *all* dependencies, in order.
  if (!archibald_curricula_load_libraries()) {
    return '';
  }

  // Add our own as well.
  drupal_add_css(archibald_curricula_get_path() . '/css/archibald_curricula.css');

  $items = $vars['items'];

  // Prepare a function for recursively rendering items.
  $recursive = function($parent_id) use($items, &$recursive, $vars) {
    if (empty($items[$parent_id])) {
      return '';
    }

    $children = array_filter($items[$parent_id], function($item) {
      return !empty($item['active']);
    });
    $html = '';

    if (!empty($children)) {
      // Render a list of child items.
      $html .= '<ul class="archibald-curriculum-ui-summary__list">';
      foreach ($children as $child) {
        $names = $vars['html'] ? $child['name'] : array_map('check_plain', $child['name']);
        $html .= '<li class="archibald-curriculum-ui-summary__list__item';
        if (!empty($child['type'])) {
          $html .= ' archibald-curriculum-ui-summary__list__item--' . $child['type'];
        }
        $html .= '">';
        $html .= '<span>' . implode('<br />', $names) . '</span>';
        $html .= $recursive($child['id']);
        $html .= '</li>';
      };
      $html .= '</ul>';
    }

    return $html;
  };

  return '<div class="archibald-curriculum__summary-display">' . $recursive('root') . '</div>';
}

/**
 * Renders a list of LOM-CHv1.3 Curricula taxonomy trees.
 *
 * We use this theme callback as a commodity, as it allows other modules to
 * override or preprocess it. It doesn't actually *render* a string,
 * but simply returns an object, suitable for exporting as JSON.
 *
 * @see theme_archibald_curricula_json_curricula_item()
 * @see hook_archibald_json_export_alter()
 *
 * @param $vars
 *    An associative array containing:
 *   - items: An array containing the item database. This array will be filtered
 *     to only include items with the "active" property set to true. Each active
 *     item will be "rendered" as an object suitable for JSON export. Each item
 *     is "rendered" using the theme callback defined in the
 *     "item_theme_callback" variable (see below). This callback gets passes the
 *     item and its children items, pre-rendered (recursive).
 *   - item_theme_callback: The theme callback to use to render single items.
 *     Defaults to theme_archibald_curricula_json_curricula_item().
 *
 * @return array
 *    An array of taxonomy trees, as expected by the LOM-CHv1.3 Curricula field.
 *    Refer to the official documentation of the LOM-CHv1.3 format for more
 *    information.
 */
function theme_archibald_curricula_json_curricula($vars) {
  $items = $vars['items'];
  $item_theme_callback = $vars['item_theme_callback'];
  $langcode = $vars['langcode'];

  // Prepare a function for recursively rendering items.
  $recursive = function($parent_id) use(&$items, &$recursive, $item_theme_callback, $langcode) {
    if (empty($items[$parent_id])) {
      return array();
    }

    $children = array_filter($items[$parent_id], function($item) {
      return !empty($item['active']);
    });
    $list = array();

    if (!empty($children)) {
      foreach ($children as $child) {
        $list[] = theme($item_theme_callback, array(
          'item' => $child,
          'children' => $recursive($child['id']),
          'langcode' => $langcode,
        ));
      };
    }

    return $list;
  };

  return $recursive('root');
}

/**
 * Renders a single item in the LOM-CHv1.3 Curricula structure.
 *
 * We use this theme callback as a commodity, as it allows other modules to
 * override or preprocess it. It doesn't actually *render* a string,
 * but simply returns an object, suitable for exporting as JSON.
 *
 * @see theme_archibald_curricula_json_curricula()
 * @see hook_archibald_json_export_alter()
 *
 * @param $vars
 *    An associative array containing:
 *   - item: The item to be "rendered". If the item's "data" key contains a
 *     "purpose" key, it will be used as the "purpose" value. It's up to the
 *     caller to make sure that "purpose" is a valid LOM Vocabulary element.
 *   - children: An array of pre-rendered child items.
 *   - langcode: In case an item's "name" key is not a valid LangString, it will
 *     be converted to a LangString format. If a langcode is provided, it will
 *     used as the sole language available. If it's not available, the default
 *     language will be used instead.
 *
 * @return array
 *    A taxon item as expected by the LOM-CHv1.3 Curricula field. Refer to the
 *    official documentation of the LOM-CHv1.3 format for more information.
 */
function theme_archibald_curricula_json_curricula_item($vars) {
  $item = $vars['item'];
  $children = $vars['children'];
  $langcode = !empty($vars['langcode']) ? $vars['langcode'] : language_default('language');

  $data = array(
    'id' => check_plain($item['id']),
    'type' => check_plain($item['type']),
  );

  if (isset($item['data']['purpose'])) {
    $data['purpose'] = $item['data']['purpose'];
  }

  // Is the name a LangString? If so, use it as-is. Else, construct a LangString
  // based on the name, using the passed langcode.
  if (
    is_object($item['name']) ||
    (
      is_array($item['name']) &&
      count(array_intersect(
        array_keys($item['name']),
        // We don't depend on Archibald. The function may not be available. If
        // not, provide a simpler language array. Usually, the dsb API doesn't
        // handle other languages anyway.
        function_exists('archibald_language_fallback_list') ?
          archibald_language_fallback_list() :
          array('en', 'de', 'fr', 'it')
      ))
    )
  ) {
    $data['entry'] = $vars['html'] ? $item['name'] : array_map('check_plain', $item['name']);
  }
  else {
    $name = is_array($item['name']) ? implode("\n", $item['name']) : $item['name'];
    $data['entry'] = array(
      $langcode => $vars['html'] ?
        $name :
        check_plain($name),
    );
  }

  if (isset($item['data']['url'])) {
    if (is_object($item['data']['url'])) {
      $item['data']['url'] = (array) $item['data']['url'];
    }
    elseif (!is_array($item['data']['url'])) {
      $item['data']['url'] = array($item['data']['url']);
    }

    $data['identifier'] = array();
    foreach ($item['data']['url'] as $url) {
      $data['identifier'][] = array(
        'catalog' => 'URL',
        'entry' => check_plain($url),
      );
    }
  }

  $data['childTaxons'] = $children;

  return $data;
}

/**
 * Renders a list of LOM-CHv1.1 Classification taxonomy paths.
 *
 * We use this theme callback as a commodity, as it allows other modules to
 * override or preprocess it. It doesn't actually *render* a string,
 * but simply returns an object, suitable for exporting as JSON.
 *
 * @see theme_archibald_curricula_json_classification_item()
 * @see hook_archibald_json_export_alter()
 *
 * @param $vars
 *    An associative array containing:
 *   - items: An array containing the item database. This array will be filtered
 *     to only include items with the "active" property set to true. Each active
 *     item will be "rendered" as an object suitable for JSON export. Each item
 *     is "rendered" using the theme callback defined in the
 *     "item_theme_callback" variable (see below). This callback gets passes the
 *     item and its children items, pre-rendered (recursive).
 *   - item_theme_callback: The theme callback to use to render single items.
 *     Defaults to theme_archibald_curricula_json_classification_item().
 *
 * @return array
 *    An array of taxonomy paths, as expected by the LOM-CHv1.1 Classification
 *    field. Refer to the official documentation of the LOM-CHv1.1 format for
 *    more information.
 */
function theme_archibald_curricula_json_classification($vars) {
  $items = $vars['items'];
  $item_theme_callback = $vars['item_theme_callback'];

  $paths = array();

  // Prepare a function for recursively rendering items.
  $recursive = function($parent_id, $parents) use(&$items, &$recursive, &$paths, $item_theme_callback) {
    // We arrived at a leaf. Store the path and return.
    if (empty($items[$parent_id])) {
      $paths[] = $parents;
      return;
    }

    // Filter out inactive children.
    $children = array_filter($items[$parent_id], function($item) {
      return !empty($item['active']);
    });

    // If there are no active children, we consider this one as a leaf, and
    // store the path.
    if (empty($children)) {
      $paths[] = $parents;
      return;
    }
    else {
      // Each child represents a new path. Recursively treat them.
      foreach ($children as $child) {
        // Pass an updated-on-the-fly parents array using array_merge(), instead
        // of copying and pushing.
        $recursive($child['id'], array_merge(
          $parents,
          array(theme($item_theme_callback, array(
            'item' => $child,
          )))
        ));
      };
    }
  };

  $recursive('root', array());
  return $paths;
}

/**
 * Renders a single item in the LOM-CHv1.1 Classification structure.
 *
 * We use this theme callback as a commodity, as it allows other modules to
 * override or preprocess it. It doesn't actually *render* a string,
 * but simply returns an object, suitable for exporting as JSON.
 *
 * @see theme_archibald_curricula_json_classification()
 * @see hook_archibald_json_export_alter()
 *
 * @param $vars
 *    An associative array containing:
 *   - item: The item to be "rendered".
 *   - langcode: In case an item's "name" key is not a valid LangString, it will
 *     be converted to a LangString format. If a langcode is provided, it will
 *     used as the sole language available. If it's not available, the default
 *     language will be used instead.
 *
 * @return array
 *    A taxon item as expected by the LOM-CHv1.1 Classification field. Refer to
 *    the official documentation of the LOM-CHv1.1 format for more information.
 */
function theme_archibald_curricula_json_classification_item($vars) {
  $item = $vars['item'];
  $langcode = !empty($vars['langcode']) ? $vars['langcode'] : language_default('language');

  $data = array(
    'id' => $item['id'],
  );

  // Is the name a LangString? If so, use it as-is. Else, construct a LangString
  // based on the name, using the passed langcode.
  if (
    is_object($item['name']) ||
    (
      is_array($item['name']) &&
      count(array_intersect(
        array_keys($item['name']),
        // We don't depend on Archibald. The function may not be available. If
        // not, provide a simpler language array. Usually, the dsb API doesn't
        // handle other languages anyway.
        function_exists('archibald_language_fallback_list') ?
          archibald_language_fallback_list() :
          array('en', 'de', 'fr', 'it')
      ))
    )
  ) {
    $data['entry'] = $item['name'];
  }
  else {
    $data['entry'] = array(
      $langcode => is_array($item['name']) ? implode("\n", $item['name']) : $item['name'],
    );
  }

  return $data;
}

/**
 * @} End of "addtogroup themeable".
 */
