<?php

/**
 * @file
 * Allows to set active menu items based on several plugins. Also can provide additional fields on every menu entry.
 */

include_once('includes/views.inc');

/**
 * Implements hook_permission().
 */
function power_menu_permission() {
  return array(
    'administer power menu' => array(
      'title' => t('Administer power menu configuration'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function power_menu_menu() {

  $items['admin/config/search/power_menu'] = array(
    'title' => 'Power Menu configuration',
    'description' => 'Configure power menu plugins.',
    'access arguments' => array('administer power menu'),
    'page arguments' => array('power_menu_configuration_form'),
    'page callback' => 'drupal_get_form',
    'file' => 'power_menu.admin.inc',
  );

  $items['admin/config/search/power_menu/handler'] = array(
    'title' => 'Menu handlers',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -1,
  );

  $items['admin/config/search/power_menu/handler/edit/%'] = array(
    'title' => 'Power Menu configuration',
    'description' => 'Configure power menu plugin.',
    'access arguments' => array('administer power menu'),
    'page arguments' => array('power_menu_configuration_handler_form', 6),
    'page callback' => 'drupal_get_form',
    'type' => MENU_NORMAL_ITEM,
    'file' => 'power_menu.admin.inc',
  );

  $items['admin/config/search/power_menu/fields'] = array(
    'title' => 'Menu fields',
    'description' => 'Configure power menu fields.',
    'access arguments' => array('administer power menu'),
    'page arguments' => array('power_menu_fields_configuration_form'),
    'page callback' => 'drupal_get_form',
    'type' => MENU_LOCAL_TASK,
    'file' => 'power_menu.admin.inc',
    'weight' => 0,
  );

  return $items;
}

/**
 * Implements hook_menu_alter().
 */
function power_menu_menu_alter(&$items) {

  $menus = variable_get('power_menu_fields_menus', array());

  foreach ($menus as $key => $value) {
    $value = power_menu_create_machine_name($value);

    // Hide the field diplay
    $items['admin/config/search/power_menu/fields/' . $value . '/fields']['type'] = MENU_CALLBACK;
    $items['admin/config/search/power_menu/fields/' . $value . '/display']['type'] = MENU_CALLBACK;
  }
}

/**
 * Implements hook_help().
 */
function power_menu_help($path, $arg) {
  switch ($path) {
    case 'admin/config/search/power_menu':
      $output = '<p>' . t('The power menu allows you to set active menu items based on several plugins. This is mainly necessary when nodes not "connected" with a menu item. The power menu also provides fields for every menu entry.') . '</p>';
      $output .= '<p>' . t('On this settings page, you can define in which order the menu handler should process.') . '</p>';
      return $output;

    case 'admin/config/search/power_menu/fields':
      $output = '<p>' . t('The power menu allows you to set active menu items based on several plugins. This is mainly necessary when nodes not "connected" with a menu item. The power menu also provides fields for every menu entry.') . '</p>';
      $output .= '<p>' . t('On this settings page, you can define which menu should have fields.') . '</p>';
      return $output;

    // ...
  }
}

/**
 * Implements hook_theme().
 */
function power_menu_theme() {
  return array(
    'power_menu_plugins_order' => array(
      'render element' => 'element',
      'file' => 'power_menu.admin.inc',
    ),
  );
}

/**
 * Implements hook_ctools_plugin_type() to inform the plugin system that the
 * Power Menu owns menu_handlers plugin type.
 *
 * All of these are empty because the defaults all work.
 */
function power_menu_ctools_plugin_type() {
  return array(
    'menu_handlers' => array('classes' => array('handler')),
  );
}

/**
 * Compare the weigth of the plugins
 */
function power_menu_handlers_weight_compare($a, $b) {
  if ($a['weight'] == $b['weight']) {
    return 0;
  }
  return ($a['weight'] < $b['weight']) ? -1 : 1;
}

/**
 * Shortcut function to get menu_handlers plugins. The handlers are sorted by the module settings.
 *
 * @param $only_enabled
 *   Is this parameter set to TRUE, only enabled menu_handlers returned.
 * @return
 *   An array of available menu_handlers
 */
function power_menu_get_menu_handlers($only_enabled = FALSE) {
  ctools_include('plugins');
  $handlers = ctools_get_plugins('power_menu', 'menu_handlers');

  $ordered_handlers = array();
  $settings = variable_get('power_menu_handlers_settings', array());

  // Sort handlers by settings weight
  uasort($settings, 'power_menu_handlers_weight_compare');

  // Sort available handlers based on settings array and merge with new handler plugins
  foreach ($settings as $key => $value) {

    if ((!$only_enabled || $value['enabled']) && !empty($handlers[$key])) {
      $ordered_handlers[$key] = $handlers[$key];
    }
    unset($handlers[$key]);
  }

  if ($only_enabled) {
    return $ordered_handlers;
  }
  else {
    return array_merge($ordered_handlers, $handlers);
  }
}

/**
 * Implements hook_ctools_plugin_directory() to let the system know
 * where our plugins are.
 */
function power_menu_ctools_plugin_directory($owner, $plugin_type) {

  if ($owner == 'power_menu') {
    return 'plugins/' . $plugin_type;
  }
}

/**
 * Implements hook_ctools_render_alter().
 *
 * When we use CTools Pagemanager, hook_nodeapi (view) is not being called, because ctools alters the
 * hook_menu and redirects the page callback to it's own function in node_view.inc Well, power_menu
 * never knows that a node is actually being displayed -> so we alter the ctools_render function and
 * start the normal power_menu_nodeapi procedure
 */
/*
function power_menu_ctools_render_alter($info, $page, $args, $contexts, $task, $subtask) {
  if (!empty($task['admin path']) && $task['admin path'] == 'node/%node' && $page) {
    power_menu_nodeapi($contexts['argument_nid_1']->data, 'view', NULL, $page);
  }
}
*/

/**
 * Implements hook_entity_view().
 */
function power_menu_entity_view($entity, $type, $view_mode, $langcode) {
  // TODO: Add aditional test, that the function is only called on the main entity. At the moment the function is called as many entities shown in full mode (e.g. comments on a full node view)
  // Power menu sets active menu only on full or defined view modes and enabled handlers
  $defined_view_modes = variable_get('power_menu_view_mode', 'full');

  // Is it an array with multiple view modes
  if (is_array($defined_view_modes)) {
    $set_path = in_array($view_mode, $defined_view_modes) ? TRUE : FALSE;
  }
  else {
    $set_path = ($view_mode == $defined_view_modes) ? TRUE : FALSE;
  }

  if ($set_path && variable_get('power_menu_handlers_enabled', FALSE)) {
    power_menu_set_path($entity, $type, $langcode);
  }
}

/**
 * Sets the system path to use for the active menu trail.
 *
 * @param $entity
 *   The entity
 * @param $type
 *   The entity type
 * @param $langcode
 *   The language code
 *
 * @return
 *   The system path
 */
function power_menu_set_path($entity, $type, $langcode) {

  // TODO: Implement cache_clear when forms submitted!
  $path = power_menu_get_path($entity, $type);

  // Is the path set to <front>, do only activate the item
  if ($path == '<front>') {
    $path = variable_get('site_frontpage', 'node');
    menu_set_active_item($path);
  }
  elseif (preg_match('/^<[A-Za-z0-9]*>$/', $path)) {
    // Do not set a path on special menu items like <nolink> ...
    // This items mostly used on multiple menu-items and in this case
    // Power Menu could/would activate the wrong.
  }
  else {

    // Set the path for evey selected menu in the power_menu settings
    foreach (variable_get('power_menu_handlers_menus', array()) as $menu_name) {
      // The official way to seth the active path
      menu_tree_set_path($menu_name, $path);
    }

    // TODO: Cache the breadcrumbs
    // Set the breadcrumb when is activated in settings and a path is set
    if (variable_get('power_menu_handlers_breadcrumb', TRUE) && isset($path)) {
      $breadcrumbs = power_menu_get_breadcrumbs($path);

      // Add title only a title filed exists
      if (variable_get('power_menu_handlers_breadcrumb_title', FALSE) && !empty($entity->title)) {
        $breadcrumbs[] = $entity->title;
      }

      drupal_set_breadcrumb($breadcrumbs);
    }
  }

  return $path;
}

/**
 * Gets the system path for the current page.
 *
 * @param $entity
 *   The entity
 * @param $type
 *   The entity type
 * @param $defined_path
 *   Define a specific path to get the current page path
 * @param $use_cache
 *   Get the cached path.
 *
 * @return
 *   The system path
 */
function power_menu_get_path($entity, $type, $defined_path = NULL, $use_cache = TRUE) {
  // The cache key is the entity uri
  $uri = entity_uri($type, $entity);
  $cache_key = 'handler:' . $uri['path'];
  $cached_path = cache_get($cache_key, 'cache_power_menu');
  $path = $use_cache && $cached_path ? $cached_path->data : FALSE;

  if (!$path) {
    // Get the active menu item
    $router_item = menu_get_item($defined_path);
    if (!$router_item) {
      return $path;
    }
    // Get the alias for given item to use
    $alias = drupal_get_path_alias($router_item['href']);

    // Call all enabled plugins except a plugin changed the router information
    foreach (power_menu_get_menu_handlers(TRUE) as $handler) {

      $instance = power_menu_plugin_get_handler_instance($handler);

      if ($instance) {
        $path = $instance->getMenuPathToActivate($entity, $type, $router_item, $alias);

        if (!empty($path)) {
          // Is a path set, do not loop over the next plugins
          break;
        }
      }
    }
    cache_set($cache_key, $path, 'cache_power_menu');
    return $path;
  }
  return $path;
}

/**
 * Creates an instance for given handler configuration.
 *
 * @param array $handler
 *   The handler definition.
 *
 * @return
 *   The class instance which implements the PowerMenuHandlerInterface or FALSE if no instance created.
 *
 */
function power_menu_plugin_get_handler_instance($handler) {

  $class_name = ctools_plugin_get_class($handler, 'handler');
  // Plugin class found?
  if ($class_name) {
    $instance = new $class_name();
    // Has the class implemented the PowerMenuHandlerInterface interface
    if ($instance instanceof PowerMenuHandlerInterface) {
      return $instance;
    }
  }

  return FALSE;
}

/**
 * Creates an instance for given handler name.
 *
 * @param array $handler_name
 *   The name of the handler.
 *
 * @return
 *   The class instance which implements the PowerMenuHandlerInterface or FALSE if no instance created.
 *
 */
function power_menu_plugin_get_handler_instance_by_name($handler_name) {

  $handlers = power_menu_get_menu_handlers();

  if (!empty($handlers[$handler_name])) {
    $handler = $handlers[$handler_name];

    return power_menu_plugin_get_handler_instance($handler);
  }

  return FALSE;
}

/**
 * Constructs the breadcrumb for given path. This works only, when the path is used in e menu link.
 *
 * @param $path
 *   The system path to get the breadcrumb for.
 * @return
 *   The populated breadcrumb array.
 */
function power_menu_get_breadcrumbs($path) {
  $crumbs = array(l(t('Home'), '<front>'));

  $menu = db_select('menu_links', 'ml')
    ->fields('ml', array('menu_name'))
    ->condition('ml.link_path', $path, '=')
    ->execute()
    ->fetchField();

  if ($menu) {
    $tree = menu_tree_page_data($menu);

    // Use translated menu items
    if (function_exists('i18n_menu_localize_tree')) {
      $tree = i18n_menu_localize_tree($tree);
    }

    _power_menu_recurse_crumbs($tree, $path, $crumbs);
  }

  return $crumbs;
}

/**
 *  Recursively loop ober the menu tree to get the breadcrumbs.
 */
function _power_menu_recurse_crumbs($tree, $path, &$crumbs, $above = array()) {
  foreach ($tree as $menu_item) {
    if (!$menu_item['link']['in_active_trail']) {
      continue;
    }
    if ($menu_item['link']['link_path'] == $path) {
      foreach ($above as $trail_item) {
        $crumbs[] = l($trail_item['link']['title'], $trail_item['link']['link_path']);
      }
      $crumbs[] = l($menu_item['link']['title'], $menu_item['link']['link_path']);
      break;
    }
    if (is_array($menu_item['below'])) {
      _power_menu_recurse_crumbs($menu_item['below'], $path, $crumbs, array_merge($above, array($menu_item)));
    }
  }
}

/**
 * Implements hook_form_ID_alter().
 *
 * We are going to alter the form for editing a menu item and are going to add
 * some additional fields.
 */
function power_menu_form_menu_edit_item_alter(&$form, &$form_state) {
  $mlid = $form['mlid']['#value'];

  // Is it a new menu link, get the menu name from the arguments
  if ($mlid) {
    $menu_link = menu_link_load($mlid);
    $menu_name = $menu_link['menu_name'];
  }
  else {
    // A new menu link is created
    $menu_name = $form['original_item']['#value']['menu_name'];
    // Set mlid to NULL in case 0 is used for the menu overview entity
    $mlid = NULL;
  }

  // Add handler fields only when the  menu is defined as a power menu
  $menus = variable_get('power_menu_handlers_menus', array());

  if (in_array($menu_name, $menus)) {

    $form['#submit'][] = 'power_menu_form_menu_edit_item_handlers_submit';
    $form['#validate'][] = 'power_menu_form_menu_edit_item_handlers_validate';

    $handlers = power_menu_get_menu_handlers(TRUE);
    if ($handlers) {
      $form['power_menu']['handlers'] = array(
        '#type' => 'fieldset',
        '#title' => t('Power Menu Handlers'),
        '#collapsible' => TRUE,
        '#collapsed' => FALSE,
      );

      foreach ($handlers as $handler) {

        $instance = power_menu_plugin_get_handler_instance($handler);

        if ($instance) {
          $handler_form = $instance->menuFormAlter($form, $form_state);

          // Add additional form elements from handler
          if (is_array($handler_form)) {

            $form['power_menu']['handlers'][$handler['name']] = array(
              '#type' => 'fieldset',
              '#title' => t($handler['title']),
              '#collapsible' => TRUE,
              '#collapsed' => FALSE,
            );

            $form['power_menu']['handlers'][$handler['name']]['sa'] = $handler_form;
          }
        }
      }
    }
  }

  // Add menu field entity fields
  power_menu_add_fields_entity_fields($menu_name, $mlid, $form, $form_state);
}

/**
 * Implements hook_form_ID_alter().
 *
 * We are going to alter the form so that a user can set default properties.
 * Default properties are used incase no properties are found on menu items.
 *
 */
function power_menu_form_menu_overview_form_alter(&$form, &$form_state) {
  // Atach menu field entity fields.
  power_menu_add_fields_entity_fields($form['#menu']['menu_name'], 0, $form, $form_state);
}

/**
 * Implements hook_menu_link_delete().
 */
function power_menu_menu_link_delete($link) {
  $args = array(
    $link,
  );

  _power_menu_call_handler_function('menuLinkDelete', $args);

  // Cleanup power_menu fields for deleted menu_links
  $id = power_menu_get_fields_entity_id_by_mlid($link['mlid']);
  if ($id) {
    entity_delete('power_menu_fields', $id);
  }
}

/**
 * Implements hook_menu_delete():
 */
function power_menu_menu_delete($menu) {

  // Cleanup power_menu fields for deleted menus
  $id = power_menu_get_fields_entity_id_by_menu($menu['menu_name']);
  if ($id) {
    entity_delete('power_menu_fields', $id);
  }
}

/**
 * Add the power menu field entity to the given form.
 */
function power_menu_add_fields_entity_fields($menu_name, $mlid, &$form, &$form_state) {
  // Create bundle name for the given menu name
  $bundle_name = power_menu_create_machine_name($menu_name);

  // Check is this field selected for menu fields
  $selected_menus = variable_get('power_menu_fields_menus', array());

  // Check is this menu selected for fieldable
  if (in_array($menu_name, $selected_menus)) {

    $form['#submit'][] = 'power_menu_form_menu_edit_fields_submit';
    $form['#validate'][] = 'power_menu_form_menu_edit_fields_validate';

    // Add Power Menu fields
    $form['power_menu']['fields'] = array(
      '#type' => 'fieldset',
      '#title' => t('Power Menu fields'),
      '#collapsible' => TRUE,
      '#collapsed' => FALSE,
      '#tree' => TRUE,
      '#parents' => array('power_menu', 'fields'),
    );

    // Load entity id for this menu link
    $query = new EntityFieldQuery();
    $query->entityCondition('entity_type', 'power_menu_fields')
      ->propertyCondition('mlid', $mlid)
      ->propertyCondition('menu_name', $bundle_name);
    $result = $query->execute();

    // Is no entity found, create a new
    if (empty($result)) {

      $entity = entity_create('power_menu_fields',
        array(
          'menu_name' => $bundle_name,
          'mlid' => $mlid,
        )
      );
    }
    else {
      $entity_nid = array_keys($result['power_menu_fields']);
      $entity = entity_load('power_menu_fields', $entity_nid);
      $entity = reset($entity);
    }

    // Set the correct parents for the attached field values, because they added to a fieldset
    $form['#parents'] = array('power_menu', 'fields');
    // Add entity fields
    field_attach_form('power_menu_fields', $entity, $form['power_menu']['fields'], $form_state);

    // Add entity to form
    $form['power_menu']['fields']['entity'] = array(
      '#type' => 'value',
      '#value' => $entity,
    );
  }
}

/**
 * Attached menu form validate callback.
 *
 * Callback function when menu_edit_item form is being validated.
 */
function power_menu_form_menu_edit_item_handlers_validate(&$form, &$form_state, $form_id = NULL) {

  $args = array(
    &$form,
    &$form_state,
  );

  _power_menu_call_handler_function('menuFormValidate', $args);
}

/**
 * Attached menu form submit callback.
 *
 * Callback function when menu_edit_item form is being submitted.
 */
function power_menu_form_menu_edit_item_handlers_submit($form, &$form_state) {

  $args = array(
    $form,
    &$form_state,
  );

  _power_menu_call_handler_function('menuFormSubmit', $args);
}


/**
 * Attached menu entity fields validation callback.
 *
 * Callback function when menu or menu link forms validated.
 */
function power_menu_form_menu_edit_fields_validate(&$form, &$form_state, $form_id = NULL) {

}

/**
 * Attached menu entity fields submit callback.
 *
 * Callback function when menu or menu link forms submitted.
 */
function power_menu_form_menu_edit_fields_submit($form, &$form_state) {

  // Fields relevant processing.
  // Is no entity added, no field processing necessary
  if (isset($form_state['values']['power_menu']['fields']['entity'])) {
    // Get and save the entity
    $entity = $form_state['values']['power_menu']['fields']['entity'];
    // Unset the 'menu_name' because this is a value from the core menu field and overwrites in entity_form_submit_build_entity()
    // the power_menu_fields bundle name also called 'menu_name'!
    unset($form_state['values']['menu_name']);
    entity_form_submit_build_entity('power_menu_fields', $entity, $form, $form_state);
    $entity->save();

    // Clear cache for given path or the default entry
    if (isset($form_state['values']['link_path'])) {
      cache_clear_all('fields:' . $form_state['values']['link_path'], 'cache_power_menu');
    }
    elseif ($entity->mlid == 0) {
      cache_clear_all('fields:/', 'cache_power_menu');
    }

    // Clear cache for the defined menu link
    if (isset($form_state['values']['mlid'])) {
      cache_clear_all('fields:mlid:' . $form_state['values']['mlid'], 'cache_power_menu');
    }

    // Clear block cache
    cache_clear_all('power_menu:power_menu_field_display', 'cache_block', TRUE);
  }
}

/**
 * Helper function to call a function in all registred handler objects.
 *
 * @param $function
 *   Function to call in handler object
 * @param $args
 *   The parameters to be passed to the callback, as an indexed array.
 */
function _power_menu_call_handler_function($function, &$args) {
  $handlers = power_menu_get_menu_handlers();

  foreach ($handlers as $handler) {
    $instance = power_menu_plugin_get_handler_instance($handler);

    if ($instance) {
      call_user_func_array(array(&$instance, $function), $args);
    }
  }
}

/**
 * Get all entity bundles with an uri callback
 *
 * @return array
 *   An array with 'entity_key|bundle_key' as key and 'entity_label : bundle_lable' as value.
 */
function power_menu_get_entities_and_bundles() {
  $entity_infos = entity_get_info();
  $bundles = array();
  foreach ($entity_infos as $entity_key => $entity) {

    // Ignore entities with no uri callback
    if (empty($entity['uri callback'])) {
      continue;
    }

    foreach ($entity['bundles'] as $bundle_key => $bundle) {
      $bundles[$entity_key . '|' . $bundle_key] = $entity['label'] . ' : ' . $bundle['label'];
    }
  }

  return $bundles;
}

/**
 * Implementation of hook_flush_caches()
 */
function power_menu_flush_caches() {
  return array('cache_power_menu');
}

/**
 * Implements hook_entity_info().
 */
function power_menu_entity_info() {

  // Add a bundle for every activated menu in the fields settings
  $menus = variable_get('power_menu_fields_menus', array());
  $bundles = array();
  foreach ($menus as $key => $value) {

    // Allways use underscores for menu key hyphens, because hyphens are not allowed for bundle names.
    $value = power_menu_create_machine_name($value);

    $bundles[$value] = array(
      'admin' => array(
        'path' => 'admin/config/search/power_menu/fields/' . $value,
        'access arguments' => array('administer power menu'),
      ),
      'label' => $value, // TODO: Set a better label name
    );
  }

  $info['power_menu_fields'] = array(
    'label' => t('Power Menu fields'),
    'controller class' => 'EntityAPIController',
    'base table' => 'power_menu_fields',
    'uri callback' => 'entity_class_uri',
    'fieldable' => TRUE,
    'entity keys' => array(
      'id' => 'id',
      'bundle' => 'menu_name',
    ),
    'bundle keys' => array(
      'bundle' => 'menu_name',
    ),
    'bundles' => $bundles,
    // Additional entity API keys
    'entity class' => 'Entity',
    'module' => 'power_menu',
  );

  return $info;
}

/**
 * Gets the Power Menu Fileds entity for given path
 *
 * @param $path
 *  The system path to search for the fields entity. Is no path set, the system path is used. To get the default element use '/' as value for $path.
 * @param $fallback
 *  Is set to TRUE, the default entity is returned when no entity is retrived from defined path.
 * @param string $menu_name
 *   The menu name to retrive the default menu field entity from
 * @return
 *   The entity or NULL is no entity found for the given path
 */
function power_menu_get_fields_entity($path = NULL, $fallback = TRUE, $menu_name = 'main-menu') {
  $menu_name_orig = $menu_name;
  $menu_name = power_menu_create_machine_name($menu_name);

  // Is no path set, use the current sytsem path
  if (!isset($path)) {
    $path = implode('/', arg());
  }

  // The cache is used to store the resolved menu item path from power menu for given system path.
  // With this mechanism it is not necessary to store the field entity for every system path
  $cache_path_key = 'path:' . $path;
  $cached_path = cache_get($cache_path_key, 'cache_power_menu');
  $lookup_path = $cached_path ? $cached_path->data : $path;

  // Loaded menu field entities also stored in cache. Get it.
  $cache_key = 'fields:' . $lookup_path;
  $entity = cache_get($cache_key, 'cache_power_menu');
  $entity = $entity ? $entity->data : NULL;

  // Is the entity not cached, load it
  if (!$entity) {

    // Lookup for the default menu field entity?
    if ($lookup_path == '/' && $fallback) {

      $id = power_menu_get_fields_entity_id_by_menu($menu_name);
    }
    else {
      // Is the defined path the front page, get <front> as search condition
      if (variable_get('site_frontpage', 'node') == $lookup_path) {
        $lookup_path = '<front>';
      }

      // Get the menu link from given path
      $query = db_select('power_menu_fields', 'pmf');
      $query->leftJoin('menu_links', 'ml', 'pmf.mlid = ml.mlid');
      $id = $query->fields('pmf', array('id'))
        ->condition('ml.link_path', $lookup_path)
        ->condition('ml.menu_name', $menu_name_orig)
        ->execute()->fetchField();
    }

    // No element found, lookup for the default entry
    if (!$id && $fallback && $lookup_path != '/') {
      $entity = power_menu_get_fields_entity('/', TRUE, $menu_name);
      $lookup_path = '/';
    }
    elseif ($id) {
      $entity = entity_load('power_menu_fields', array($id));
      // Return only the entity and not an array
      $entity = $entity ? array_pop($entity) : NULL;
    }

    cache_set($cache_path_key, $lookup_path, 'cache_power_menu');
    cache_set('fields:' . $lookup_path, $entity, 'cache_power_menu');
  }

  return $entity;
}

/**
 * Implements hook_block_info().
 */
function power_menu_block_info() {
  $blocks = array();

  $blocks['power_menu_field_display'] = array(
    'info' => t('Power Menu field display'),
    'cache' => DRUPAL_CACHE_PER_PAGE,
  );

  return $blocks;
}

/**
 * Implements hook_block_configure().
 */
function power_menu_block_configure($delta) {
  $form = array();

  switch ($delta) {
    case 'power_menu_field_display':

      $menus = menu_get_menus();

      $entity_info = entity_get_info('power_menu_fields');
      $view_modes = array('default' => t('Default'));
      foreach ($entity_info['view modes'] as $key => $view_mode) {
        $view_modes[$key] = $view_mode['label'];
      }

      $form['power_menu_fields_menu'] = array(
        '#type' => 'select',
        '#title' => t('Display Menu'),
        '#default_value' => variable_get('power_menu_field_display_block_menu', 'main-menu'),
        '#options' => $menus,
        '#description' => t('Select the menu from which the <a href="/admin/config/search/power_menu/fields">display</a> should be used.'),
      );

      $form['power_menu_fields_view_mode'] = array(
        '#type' => 'select',
        '#title' => t('Display View mode'),
        '#default_value' => variable_get('power_menu_field_display_block_view_mode', 'default'),
        '#options' => $view_modes,
        '#description' => t('Select the view mode which should be used to render the Power Menu field entity.'),
      );
  }

  return $form;
}

/**
 * Implements hook_block_save().
 */
function power_menu_block_save($delta, $edit) {

  switch ($delta) {
    case 'power_menu_field_display':
      variable_set('power_menu_field_display_block_menu', $edit['power_menu_fields_menu']);
      variable_set('power_menu_field_display_block_view_mode', $edit['power_menu_fields_view_mode']);
  }
}

/**
 * Implements hook_block_view().
 */
function power_menu_block_view($delta = '') {
  $block = array();

  switch ($delta) {
    case 'power_menu_field_display':

      $content = '';
      $field_entity = power_menu_get_fields_entity(NULL, TRUE, variable_get('power_menu_field_display_block_menu', 'main-menu'));
      if ($field_entity) {
        $content = entity_view('power_menu_fields', array($field_entity), variable_get('power_menu_field_display_block_view_mode', 'default'), NULL, TRUE);
      }

      $block['content'] = $content;
      break;
  }
  return $block;
}

/**
 * Gets the Power Menu Fields entity for given Menu Link ID
 *
 * @param $mlid
 *  The Menu Link ID to search for the fields entity.
 * @return
 *   The entity or NULL is no entity found for the given Menu Link ID
 */
function power_menu_get_fields_entity_by_mlid($mlid) {

  $cache_key = 'fields:mlid:' . $mlid;
  $entity = cache_get($cache_key, 'cache_power_menu');
  $entity = $entity ? $entity->data : NULL;

  // Is the entity not cached, load it
  if (!$entity) {

    // Get the menu link from given mlid
    $id = power_menu_get_fields_entity_id_by_mlid($mlid);

    if ($id) {
      $entity = entity_load('power_menu_fields', array($id));
      // Return only the entity and not an array
      $entity = $entity ? array_pop($entity) : NULL;
    }

    cache_set($cache_key, $entity, 'cache_power_menu');
  }

  return $entity;
}

/**
 * Returns the power_menu fields entity id for given menu link id (mlid).
 *
 * @param $mlid
 *  The menu link id
 * @return
 *  The power_menu field entity id or FALSE is no entity found for this mlid
 */
function power_menu_get_fields_entity_id_by_mlid($mlid) {
  $query = db_select('power_menu_fields', 'pmf');
  $id = $query->fields('pmf', array('id'))
    ->condition('pmf.mlid', $mlid)
    ->execute()->fetchField();
  return $id;
}

/**
 * Returns the power_menu fields entity id for given menu (menu name).
 *
 * @param $menu_name
 *  The menu machine name
 * @return
 *  The power_menu field entity id or FALSE is no entity found for this menu name
 */
function power_menu_get_fields_entity_id_by_menu($menu_name) {
  $bundle_name = power_menu_create_machine_name($menu_name);

  $id = db_select('power_menu_fields', 'pmf')
    ->fields('pmf', array('id'))
    ->condition('pmf.mlid', 0)
    ->condition('pmf.menu_name', $bundle_name)
    ->execute()->fetchField();
  return $id;
}

/**
 * Implements hook_features_pipe_COMPONENT_alter().
 */
function power_menu_features_pipe_field_alter(&$pipe, $data, $export) {
  // Add a dependency to power_menu when Power Menu fields exported to Features
  foreach ($data as $identifier) {
    if (strpos($identifier, 'power_menu_fields') === 0) {
      $pipe['dependencies'][] = 'power_menu';
      break;
    }
  }
}

/**
 * Creates a machine name from given string.
 *
 * At the moment, it replaces all hyphens with underscores.
 *
 * @param $value
 *   The string to use as machine names
 * @return
 *   The machine name
 */
function power_menu_create_machine_name($value) {
  return $value = str_replace('-', '_', $value);
}

/**
 * Implements hook_form_FORMID_alter().
 *
 * Append the menu-link info for the editors.
 *
 * We can't add additional columns to the taxonomy_overview_terms theme function and we
 * don't want to "copy" the code to add an additional column. That's the reason why we
 * add the information as additional markup to the term-name.
 */
function power_menu_form_taxonomy_overview_terms_alter(&$form, &$form_state) {

  $show_info = variable_get('power_menu_taxonomy_show_menu_link_info', FALSE);
  $vocabulary = variable_get('power_menu_taxonomy_vocabulary', array('vid' => NULL, 'machine_name' => NULL));

  // Is it the defined navigation taxonomy
  if($show_info && isset($form['#vocabulary']->vid) && $form['#vocabulary']->vid == $vocabulary['vid']) {

    $terms = variable_get('power_menu_taxonomy_terms', array());

    foreach ($form as &$value) {
      if (is_array($value) && isset($value['#term']['name'])) {

        $tid = $value['#term']['tid'];

        if(array_key_exists($tid, $terms)) {

          // Change the render array, that we can append additional information
          $value['view']['link'] = $value['view'];
          unset($value['view']['#type']);

          $menu_link = menu_link_load($terms[$tid]);
          $title = l($menu_link['link_title'], $menu_link['link_path']);
          $title = strlen($title) != 0 ? $title : $menu_link['title'];

          $options = array(
            '!title' => $title,
            '!edit' => l(t('edit'), 'admin/structure/menu/item/' . $menu_link['mlid'] . '/edit'),
          );

          $value['view']['menu_information']['#type'] = 'markup';
          $value['view']['menu_information']['#markup'] = ' - ' . t('Associated menu link: !title (!edit)', $options);
        }
      }
    }
  }
}
