<?php
/**
 * @file
 * Provides primary Drupal hook implementations.
 *
 * @author Jimmy Berry ("boombatower", http://drupal.org/user/214218)
 */

/**
 * Implements hook_field_group_formatter_info().
 */
function field_group_views_field_group_formatter_info() {
  $info = array(
    'view' => array(
      'label' => t('View'),
      'description' => t('Display fieldgroup using a view.'),
      'instance_settings' => array('primary_field' => '', 'field_suppress' => 'never'),
    ),
  );
  return array(
//     'form' => $info, @TODO Requires editting of fields in a view.
    'display' => $info,
  );
}

/**
 * Implements hook_field_group_format_settings().
 */
function field_group_views_field_group_format_settings($group) {
  if ($group->format_type == 'view') {
    $field_group_types = field_group_formatter_info();
    $mode = $group->mode == 'form' ? 'form' : 'display';
    $formatter = $field_group_types[$mode][$group->format_type];

    $form['instance_settings']['primary_field'] = array(
      '#type' => 'select',
      '#title' => t('Primary field'),
      '#description' => t('The field to use as the base table for the view on which all other field tables will be joined.'),
      '#options' => drupal_map_assoc($group->children),
      '#default_value' => $formatter['instance_settings']['primary_field'],
      '#weight' => 2,
    );
    if (isset($group->format_settings['instance_settings']['primary_field'])) {
      $form['instance_settings']['primary_field']['#default_value'] = $group->format_settings['instance_settings']['primary_field'];
    }

    if (module_exists('field_suppress')) {
      $form['instance_settings']['field_suppress'] = array(
        '#type' => 'select',
        '#title' => t('Suppress fields'),
        '#options' => field_suppress_states(),
        '#default_value' => $formatter['instance_settings']['field_suppress'],
        '#weight' => 3,
      );
      if (isset($group->format_settings['instance_settings']['field_suppress'])) {
        $form['instance_settings']['field_suppress']['#default_value'] = $group->format_settings['instance_settings']['field_suppress'];
      }
    }
    return $form;
  }
}

/**
 * Implements hook_field_group_format_summary().
 */
function field_group_views_field_group_format_summary($group) {
  if ($group->format_type == 'view') {
    return t('Determined by <em>!name</em> view.', array('!name' => field_group_views_name_display($group)));
  }
}

/**
 * Implements hook_field_group_pre_render().
 */
function field_group_views_field_group_pre_render(&$element, $group, &$form) {
  if ($group->format_type == 'view') {
    // Since all fields are assumed to have the same object, take the first
    // field's object determine the entity ID key, and get the entity ID.
    $field = current($element);
    $info = entity_get_info($group->entity_type);
    // Set view arguments.
    $args[] = $field['#object']->{$info['entity keys']['id']};
    if (isset($info['entity keys']['revision'])) {
      $args[] = $field['#object']->{$info['entity keys']['revision']};
    }
    // Load the group view and override the fields display with the view.
    if ($view = views_get_view($group->group_name)) {
      $view->override_path = $_GET['q'];
      $element += array(
        '#markup' => $view->preview('default', $args),
        '#weight' => $group->weight,
      );
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter(): field_group_delete_form.
 */
function field_group_views_form_field_group_delete_form_alter(&$form, &$form_state) {
  $group = $form_state['build_info']['args'][0];
  if ($group->format_type == 'view') {
    $form['description']['#markup'] = t('The <em>!name</em> view will be deleted.', array('!name' => field_group_views_name_display($group))) . ' ' . $form['description']['#markup'];
  }
}

/**
 * Delete view related to group.
 */
function field_group_views_field_group_delete_form_submit($form, &$form_state) {
  // Load the view related to the group and delete it.
  $group = $form_state['build_info']['args'][0];
  if ($view = views_get_view($group->group_name)) {
    $view->delete();
    drupal_set_message(t('Deleted <em>@name</em> view.', array('@name' => $group->group_name)));
  }
}

/**
 * Implements hook_field_group_create_field_group().
 */
function field_group_field_group_create_field_group($group) {
  field_group_views_flush_caches();
}

/**
 * Implements hook_field_group_update_field_group().
 */
function field_group_views_field_group_update_field_group($group) {
  field_group_views_flush_caches();
}

/**
 * Implements hook_field_group_delete_field_group().
 */
function field_group_views_field_group_delete_field_group($group) {
  // Load the view related to the group and delete it.
  if ($view = views_get_view($group->group_name)) {
    $view->delete();
    drupal_set_message(t('Deleted <em>@name</em> view.', array('@name' => $group->group_name)));
  }
  field_group_views_flush_caches();
}

/**
 * Implements hook_flush_caches().
 */
function field_group_views_flush_caches() {
  // Create/Recreate group views and collect list of relevant field names.
  $field_names = field_group_views_groups_iterate('field_group_views_flush_caches_interator');
}

/**
 * Create/Recreate group views and return list of relevant field names.
 *
 * @param $group
 *   Field group object.
 * @return
 *   Relevant field names.
 */
function field_group_views_flush_caches_interator($group) {
  $view = views_get_view($group->group_name);
  $primary_field = field_group_views_primary_field($group);
  $primary_field_table = "field_data_$primary_field";

  // If the related view does not exist or the primary table needs to be
  // changed then go through the view creation process. Either the standard
  // field_data table or field_revision table is fine, but if a view is
  // generated it will always use the field_data table.
  if ($primary_field && (!$view || ($view->base_table != $primary_field_table && $view->base_table != "field_revision_$primary_field"))) {
    // If view already exists then delete it.
    if ($view) {
      $view->delete();
    }

    $view = views_new_view();
    $view->name = $group->group_name;
    $view->base_table = $primary_field_table;

    // Add entity_id argument.
    $handler = $view->new_display('default', 'Defaults', 'default');
    $handler->override_option('arguments', array(
      'entity_id_raw' => array(
        'id' => 'entity_id_raw',
        'table' => $view->base_table,
        'field' => 'entity_id_raw',
        'default_action' => 'not found',
      ),
    ));
    $view->save();

    // Ensure static cache is reset so new view will be detected.
    views_get_view($group->group_name, TRUE);
    drupal_set_message(t('Created <em>!name</em> view.', array('!name' => field_group_views_name_display($group))));
  }

  // Update field settings for all children.
  $fields = field_info_fields();
  foreach ($group->children as $child) {
    $settings = $group->format_settings['instance_settings'];
    $field = $fields[$child];
    $field['settings']['field_suppress'] = isset($settings['field_suppress']) ? $settings['field_suppress'] : 'never';
    $field['settings']['field_suppress_blank'] = $child == $primary_field;
    $field['settings']['views_base_table'] = TRUE;
    field_update_field($field);
  }

  // Build list of children of relevant groups.
  return drupal_map_assoc($group->children);
}

/**
 * Implements hook_views_data_alter().
 */
function field_group_views_views_data_alter(&$data) {
  field_group_views_groups_iterate('field_group_views_views_data_alter_group', $data);
}

/**
 * Add view join information for non-primary fields within groups.
 *
 * @param $group
 *   Field group object.
 * @see hook_views_data_alter()
 */
function field_group_views_views_data_alter_group($group, &$data) {
  $primary_field = field_group_views_primary_field($group);
  foreach ($group->children as $field_name) {
    if ($field_name != $primary_field) {
      views_field_add_multi_join($data, $field_name, $primary_field);
      views_field_add_multi_join($data, $field_name, $primary_field, 'field_revision');
    }
  }
  return array();
}

/**
 * Interate over all relevant field groups and invoke a callback.
 *
 * @param $callback
 *   Function name to call for each group.
 * @param $argument
 *   (Optional) Reference to argument that can be modified by callback.
 * @return
 *   Merged array of values returned by callback.
 */
function field_group_views_groups_iterate($callback, &$argument = NULL) {
  $return = array();
  $info = field_group_info_groups(NULL, NULL, NULL, TRUE);
  foreach ($info as $entity_type => $bundles) {
    foreach ($bundles as $bundle => $displays) {
      foreach ($displays as $mode => $groups) {
        if ($mode != 'form') { // Currently only support views for the display mode.
          foreach ($groups as $group_name => $group) {
            if ($group->format_type == 'view') { // Only process groups using the view format type.
              $return += $callback($group, $argument);
            }
          }
        }
      }
    }
  }
  return $return;
}

/**
 * Generate display text for a group view.
 *
 * @param $group
 *   Field group.
 * @return
 *   Text representing a group view, either a link or just the name.
 */
function field_group_views_name_display($group) {
  $name = $group->group_name;
  if (module_exists('views_ui') && views_get_view($name)) {
    ctools_include('export-ui');
    $plugin = ctools_get_export_ui('views_ui');
    $handler = ctools_export_ui_get_handler($plugin);
    $name = l($name, ctools_export_ui_plugin_menu_path($handler->plugin, 'edit', $name));
  }
  return $name;
}

/**
 * Determine the primary field for a group.
 *
 * @param $group
 *   Field group object.
 */
function field_group_views_primary_field($group) {
  // If the primary_field is set then use it, otherwise default to the first.
  if (!($primary_field = $group->format_settings['instance_settings']['primary_field'])) {
    $primary_field = $group->children ? current($group->children) : FALSE;
  }
  return $primary_field;
}
