<?php

/**
 * @file
 * Provides integration with the relation module by a custom field formatter.
 */

function graphapi_relation_menu() {
  return array(
    'admin/config/system/graphapi/relation' => array(
      'title' => 'Global relation graphapi settings',
      'description' => 'Adjust the global relation graphapi settings',
      'page callback' => 'drupal_get_form',
      'page arguments' => array('graphapi_relation_global_settings_form'),
      'access arguments' => array('administer site configuration'),
      'type' => MENU_LOCAL_TASK,
    )
  );
}

function graphapi_relation_get_global_settings() {
  $field_settings = graphapi_relation_field_formatter_info();

  $default = $field_settings['graphapi_relation_default']['settings'];

  $settings = variable_get('graphapi_relation_global_settings', $default);
  $settings += graphapi_default_config();

  return $settings;
}

function graphapi_relation_global_settings_form() {
  $settings = graphapi_relation_get_global_settings();
  $form = graphapi_relation_settings_form( $settings);

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Save configuration',
  );
  $form['#tree'] = true;
  $form['#submit'][] = 'graphapi_relation_global_settings_submit';

  // We don't use system_settings_form ... not sure whether #tree works
  //return system_settings_form($form);
  return $form;
}

function graphapi_relation_global_settings_submit($form, &$form_state) {
  $values = $form_state['values'];
  $settings = array();
  $keys = array('links', 'graph', 'physics');
  foreach ($keys as $key) {
    $settings[$key] = $values[$key];
  }
  variable_set('graphapi_relation_global_settings', $settings);
  }

/**
 * Implements hook_field_formatter_settings_form().
 */
function graphapi_relation_field_formatter_settings_form($field, $instance, $view_mode, $form, $form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

  if ($display['type'] == 'graphapi_relation_default') {
    $settings += graphapi_default_config();
    $form['global'] = array(
      '#title' => t('Global'),
      '#type' => 'fieldset',
      '#collapsed' => FALSE,
      '#collapsible' => TRUE,
    );
    $form['global']['use_global'] = array(
      '#title' => t('Use global settings'),
      '#type' => 'checkbox',
      '#description' => 'You can change the ' . l('global settings', 'admin/relation/graphapi/global'),
      '#default_value' => $settings['global']['use_global'],
    );
    // We hide all fieldsets when using global settings.
    // TODO: this input[*] is insane long ... why is that? File a bug?
    $hide_state = array(
      'visible' => array(
        ':input[name="fields[field_relations][settings_edit_form][settings][global][use_global]"]' => array('checked' => FALSE),
      )
    );
    $form += graphapi_relation_settings_form( $settings, $hide_state);
    return $form;
  }
}

function graphapi_relation_settings_form( $settings, $hide_state= NULL) {
  $form['links'] = array(
    '#title' => t('Relation settings'),
    '#type' => 'fieldset',
    '#collapsed' => FALSE,
    '#collapsible' => TRUE,
    '#states' => $hide_state,
  );
  $types = relation_get_types();
  foreach ($types as $type => $data) {
    if (!isset($settings['links'][$type])) {
      $settings['links'][$type] = graphapi_relation_default_link_data();
    }
    $form['links'][$type] = array(
      '#title' => t($data->label),
      '#type' => 'fieldset',
      '#collapsed' => FALSE,
      '#collapsible' => TRUE,
    );
    $form['links'][$type]['color'] = array(
      '#title' => t('Color'),
      '#type' => 'textfield',
      '#default_value' => $settings['links'][$type]['color'],
    );
  }
  $form['graph'] = array(
    '#title' => t('Graph settings'),
    '#type' => 'fieldset',
    '#collapsed' => TRUE,
    '#collapsible' => TRUE,
    '#states' => $hide_state,
  );
  $form['graph']['width'] = array(
    '#title' => t('Width'),
    '#type' => 'textfield',
    '#default_value' => $settings['graph']['width'],
  );
  $form['graph']['height'] = array(
    '#title' => t('Height'),
    '#type' => 'textfield',
    '#default_value' => $settings['graph']['height'],
  );
  $form['graph']['menu'] = array(
    '#title' => t('Show menu'),
    '#type' => 'checkbox',
    '#default_value' => $settings['graph']['menu'],
  );
  $form['graph']['showLinks'] = array(
    '#title' => t('Show link labels'),
    '#type' => 'checkbox',
    '#default_value' => $settings['graph']['showLinks'],
  );
  $form['graph']['background-color'] = array(
    '#title' => t('Background color'),
    '#type' => 'textfield',
    '#default_value' => $settings['graph']['background-color'],
  );

  $form['physics'] = array(
    '#title' => t('Physics settings'),
    '#type' => 'fieldset',
    '#collapsed' => TRUE,
    '#collapsible' => TRUE,
    '#states' => $hide_state,
  );
  foreach (_graphap_relation_physics() as $key => $title) {
    $form['physics'][$key] = array(
      '#title' => t($title),
      '#type' => 'checkbox',
      '#default_value' => $settings['physics'][$key],
    );
  }
  return $form;
}

/**
 * Helper function to genereate physics form and summary
 */
function _graphap_relation_physics() {
  return array(
    'showForces' => 'Show forces applied',
    'applyAttractToCenter' => 'Attract to center',
    'applyBoundingBox' => 'Bounding box',
    'applyBoxOverlap' => 'Do not overlap',
    'applyCoulombsLaw' => 'Apply Coulombs law',
    'applyDamping' => 'Use damping',
    'applyHookesLaw' => 'Apply Hookes law',
    'applyCompass' => 'Apply compass on directed links',
  );
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function graphapi_relation_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  if ($display['type'] == 'graphapi_relation_default') {
    $settings = $display['settings'];
    if ($settings['global']['use_global']) {
      $settings = graphapi_relation_get_global_settings() + $settings;
    }
    $summary = '';
    $items = array();
    if ($settings['global']['use_global']) {
      $items[] = 'Using ' . l('global settings', 'admin/relation/graphapi/global');
      $summary .= theme('item_list', array('items' => $items, 'title' => 'Using global settings'));
    }
    $items = array();
    $items[] = t('The relation graph is sized (@width x @height).', array('@width' => $settings['graph']['width'], '@height' => $settings['graph']['height']));
    if ($settings['graph']['menu']) {
      $items[] = t('The menu is displayed');
    }
    if ($settings['graph']['showLinks']) {
      $items[] = t('The link labels are displayed on hover');
    }

    $color = $settings['graph']['background-color'];
    $items[] = array('data' => 'Background color: <span style="background-color:' . $color . ';width:10px;">&nbsp;&nbsp;</span> (' . $color . ')');

    $summary .= theme('item_list', array('items' => $items, 'title' => 'Graph settings'));

    $items = array();
    $types = relation_get_types();
    foreach($types as $type => $data) {
      if (!isset($settings['links'][$type])) {
        $settings['links'][$type] = graphapi_relation_default_link_data();
      }
      $color = $settings['links'][$type]['color'];
      $items[] = array('data' => $data->label . ': <span style="background-color:' . $color . ';width:10px;">&nbsp;&nbsp;</span> (' . $color . ')');
    }
    $summary .= theme('item_list', array('items' => $items, 'title' => 'Relations are colored'));

    $items = array();
    foreach (_graphap_relation_physics() as $key => $label) {
      if ($settings['physics'][$key]) {
        $items[] = t($label);
      }
    }
    $summary .= theme('item_list', array('items' => $items, 'title' => 'Physics settings'));

    return $summary;
  }
}

/**
 * Implements hook_field_formatter_info().
 */
function graphapi_relation_field_formatter_info() {
  $types = relation_get_types();
  $links = array();
  foreach($types as $type => $data) {
    $links[$type] = graphapi_relation_default_link_data();
  }
  
  $return = array();
  $return['graphapi_relation_default'] = graphapi_settings_defaults();
  $return['graphapi_relation_default']['field types'] = array('relation');
  $return['graphapi_relation_default']['links'] = $links;
  return $return;
}

/**
 * Default settings for link data.
 */
function graphapi_relation_default_link_data() {
  return array(
    'classes' => array(
      'edge',
    ),
    'color' => 'green',
  );
}

/**
 * Adds and id to the settings array.
 *
 * @param $id
 *   Id.
 * @param $settings
 *   Existing settings array.
 */
function graphapi_relation_default_config($id, $settings = array()) {
  return $settings + array(
    'id' => $id,
  );
}

/**
 * Implementation of hook_field_formatter_view
 *
 * As we only format relation_dummy_field joining all its relations
 * we stuff all relation types into $element[0]
 *
 * @return $element-array
 */
function graphapi_relation_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $settings = $display['settings'];
  if ($settings['global']['use_global']) {
    $global_settings = graphapi_relation_get_global_settings();
    $global_settings['global']['use_global'] = TRUE;
    $settings = $global_settings + $settings;
  }
  // Adjust hierarchy for physics settings
  $physics = $settings['physics'];
  unset($settings['physics']);
  $settings += $physics;
  // Adjust hierarchy for graph settings
  $graphapi = $settings['graph'];
  unset($settings['graph']);
  $settings += $graphapi;
  $settings += graphapi_default_config();

  $relation_types = relation_get_types();
  $element = array();
  list($entity_id) = entity_extract_ids($entity_type, $entity);
  $graph = graphapi_new_graph();
  $source_id = $entity_type . "-" . $entity_id;
  graphapi_set_node_title($graph, $source_id, entity_label($entity_type, $entity));
  foreach ($items as $delta => $item) {
    $relation_type = $relation_types[$item['relation_type']];
    foreach ($item['endpoints'][LANGUAGE_NONE] as $endpoint) {
      $related_entities = entity_load($endpoint['entity_type'], array($endpoint['entity_id']));
      $related_entity = reset($related_entities);
      if ($related_entity) {
        $endpoint_id = $endpoint['entity_type'] . "-" . $endpoint['entity_id'];

        if ($endpoint['entity_type'] == $entity_type && $endpoint['entity_id'] == $entity_id) {
          // A self reference
          // TODO make graphapi handle self references
          // graphapi_add_link($graph, $source_id, $endpoint_id);
        } else {
          $label = entity_label($endpoint['entity_type'], $related_entity);
          $uri = entity_uri($endpoint['entity_type'], $related_entity);
          $linked_title = l($label, $uri['path'], $uri['options']);

          graphapi_set_node_title($graph, $endpoint_id, $linked_title);
          $link_data = graphapi_relation_default_link_data();
          if ($relation_type->directional) {
            $link_data['type'] = 'bi';
          }
          $link_data['color'] = $settings['links'][$relation_type->relation_type]['color'];
          $link_data['classes'][] = 'edge-' . $relation_type->relation_type;
          graphapi_set_link_title($graph, $source_id, $endpoint_id, $relation_type->label);
          graphapi_set_link_data($graph, $source_id, $endpoint_id, $link_data);
        }
      }
    }
  }
  $config = graphapi_relation_default_config($source_id . "-" . $delta, $settings);
  $element[0]['relation']['graph']['#markup'] = theme('graphapi_dispatch', array('graph' => $graph, 'config' => $config));

  return $element;
}
