<?php
// $Id$


/**
 * Permission to edit and use relationships.
 */
define('GRAPHMIND_RELATIONSHIP_EDIT_PERM', 'access content');

/**
 * Default relationship id.
 */
define('GRAPHMIND_RELATIONSHIP_DEFAULT', 'default');


/**
 * Default depth for loading new relationships to the map.
 */
define('GRAPHMIND_RELATIONSHIP_DEFAULT_DEPTH', 3);

/**
 * Node preview block delta.
 */
define('GRAPHMIND_RELATIONSHIP_NODE_PREVIEW_BLOCK', 'nodePreviewBlock');


/**
 * Implements hook_menu().
 * 
 * @return array
 */
function graphmind_relationship_menu() {
  return array(
    'graphmind_relationship_root_node' => array(
      'type' => MENU_CALLBACK,
      'access arguments' => array('access content'),
      'page callback' => 'graphmind_relationship_root_node_autocomplete',
    ),
    'graphmind_relationship/node_preview/%node' => array(
      'type' => MENU_CALLBACK,
      'access callback' => 'node_access',
      'access arguments' => array('view', 2),
      'page callback' => 'graphmind_relationship_node_preview',
      'page arguments' => array(2),
      'delivery callback' => 'ajax_deliver',
    ),

    // CTools modal call for the node creation content.
    // arg(3) = parent nid.
    // arg(4) = default node type for creation.
    'graphmind_relationship/overlay/%ctools_js/%/%' => array(
      'type' => MENU_CALLBACK,
      'title' => 'Graphmind modal page',
      'access arguments' => array('access content'),
      'page callback' => 'graphmind_relationship_modal_page',
      'page arguments' => array(2, 3, 4),
    ),

    // Responsible for closing the node creation window.
    'graphmind_relationship/dismiss_node_creation/%/%' => array(
      'type' => MENU_CALLBACK,
      'access callback' => TRUE,
      'page callback' => 'graphmind_relationship_dismiss_node_creation',
      'page arguments' => array(2, 3),
      'delivery callback' => 'ajax_deliver',
    ),

    'graphmind_relationship/overlay/set-parent' => array(
      'type' => MENU_CALLBACK,
      'title' => 'Set parent node',
      'access arguments' => array('access content'),
      'page callback' => 'graphmind_relationship_modal_page_set_parent',
    ),
  );
}


/**
 * Implements hook_services().
 *
 * @return array
 */
function graphmind_relationship_services_resources() {
  return array(
    'graphmindRelationship' => array(
      'addRelationship' => array(
        'help' => 'Add new node - node relationship',
        'access arguments' => array(GRAPHMIND_RELATIONSHIP_EDIT_PERM),
        'access callback' => 'user_access',
        'access arguments append' => FALSE,
        'callback' => 'graphmind_relationship_on_add_relationship',
        'file' => array('type' => 'inc', 'module' => 'graphmind_relationship'),
        'args' => array(
          array(
            'name' => 'source_nid',
            'type' => 'int',
            'description' => t('Source node ID.'),
            'optional' => FALSE,
            'source' => 'data',
          ),
          array(
            'name' => 'target_nid',
            'type' => 'int',
            'description' => t('Target node ID.'),
            'optional' => FALSE,
            'source' => 'data',
          ),
          array(
            'name' => 'relationship_rid',
            'type' => 'string',
            'description' => t('Relationship ID,'),
            'optional' => TRUE,
            'source' => 'data',
          ),
        ),
      ),
      'deleteRelationship' => array(
        'help' => 'Remove node - node relationship',
        'access arguments' => array(GRAPHMIND_RELATIONSHIP_EDIT_PERM),
        'access callback' => 'user_access',
        'access arguments append' => FALSE,
        'file' => array('type' => 'inc', 'module' => 'graphmind_relationship'),
        'callback' => 'graphmind_relationship_on_delete_relationship',
        'args' => array(
          array(
            'name' => 'source_nid',
            'type' => 'int',
            'description' => t('Source node ID.'),
            'optional' => FALSE,
            'source' => 'data',
          ),
          array(
            'name' => 'target_nid',
            'type' => 'int',
            'description' => t('Target node ID.'),
            'optional' => FALSE,
            'source' => 'data',
          ),
          array(
            'name' => 'relationship_rid',
            'type' => 'string',
            'description' => t('Relationship ID,'),
            'optional' => TRUE,
            'source' => 'data',
          ),
        ),
      ),
      'getRelationships' => array(
        'help' => 'Get relationships of a node',
        'access arguments' => array(GRAPHMIND_RELATIONSHIP_EDIT_PERM),
        'access callback' => 'user_access',
        'access arguments append' => FALSE,
        'file' => array('type' => 'inc', 'module' => 'graphmind_relationship'),
        'callback' => 'graphmind_relationship_on_get_relationships',
        'args' => array(
          array(
            'name' => 'source_nid',
            'type' => 'int',
            'description' => t('Source node ID.'),
            'optional' => FALSE,
            'source' => 'data',
          ),
          array(
            'name' => 'relationship_rid',
            'type' => 'string',
            'description' => t('Relationship ID.'),
            'optional' => TRUE,
            'source' => 'data',
          ),
        ),
      ),
      'getSubtree' => array(
        'help' => 'Get all the relationships as a subtree',
        'access arguments' => array(GRAPHMIND_RELATIONSHIP_EDIT_PERM),
        'access callback' => 'user_access',
        'access arguments append' => FALSE,
        'file' => array('type' => 'inc', 'module' => 'graphmind_relationship'),
        'callback' => 'graphmind_relationship_get_subtree',
        'args' => array(
          array(
            'name' => 'nid',
            'type' => 'int',
            'description' => t('Node ID.'),
            'optional' => FALSE,
            'source' => 'data',
          ),
          array(
            'name' => 'depth',
            'type' => 'int',
            'description' => t('Maximum level or relationships.'),
            'optional' => TRUE,
            'source' => 'data',
          ),
        ),
      ),
      'checkUpdate' => array(
        'help' => 'Check if there is an updated version of the relationships',
        'access arguments' => array(GRAPHMIND_RELATIONSHIP_EDIT_PERM),
        'access callback' => 'user_access',
        'access arguments append' => FALSE,
        'file' => array('type' => 'inc', 'module' => 'graphmind_relationship'),
        'callback' => 'graphmind_relationship_check_update',
        'args' => array(
          array(
            'name' => 'tree',
            'type' => 'object',
            'description' => t('Tree of node IDs.'),
            'optional' => FALSE,
            'source' => 'data',
          ),
          array(
            'name' => 'depth',
            'type' => 'int',
            'description' => t('Depth'),
            'optional' => FALSE,
            'source' => 'data',
          ),
        ),
      ),
      'getUserColor' => array(
        'help' => 'Get the defined color for a user',
        'access arguments' => array('access content'),
        'access callback' => 'user_access',
        'access arguments append' => FALSE,
        'file' => array('type' => 'inc', 'module' => 'graphmind_relationship'),
        'callback' => 'graphmind_relationship_get_user_color',
        'args' => array(
          array(
            'name' => 'uid',
            'type' => 'int',
            'description' => t('User ID.'),
            'optional' => FALSE,
            'source' => 'data',
          ),
        ),
      ),
    ),
  );
}


/**
 * This method is called if the user starts to add a node via the modal window.
 * The $_SESSION variables set the actual inserted node type and parent node id.
 */
function graphmind_relationship_modal_page_set_parent() {
  if (isset($_POST['parent_nid'])) {
    $_SESSION['graphmind_relationship_parent_nid'] = $_POST['parent_nid'];
  }
  if (isset($_POST['parent_type'])) {
    $_SESSION['graphmind_relationship_default_node_type'] = $_POST['parent_type'];
  }
  if (isset($_POST['root_node'])) {
    $_SESSION['root_node'] = $_POST['root_node'];
  }
}


/**
 * Define a callback for 'graphmind_relationship/overlay' path.
 *
 * @param boolean $js
 * @return array
 */
function graphmind_relationship_modal_page($js, $parent_nid, $default_node_type) {
  ctools_include('ajax');
  ctools_include('modal');

  module_load_include('inc', 'node', 'node.pages');

  global $user;

  $_SESSION['graphmind_relationship_parent_type'] = $default_node_type;
  $_SESSION['graphmind_relationship_parent_nid']  = $parent_nid;

  // Init a node object.
  $node = new stdClass();
  $node->uid = $user->uid;
  $node->name = isset($user->name) ? $user->name : '';
  $node->language = LANGUAGE_NONE;
  $node->type = str_replace('-', '_', $default_node_type);
  $node->graphmind_relationship_parent = $parent_nid;

  // Entity id - to detect main group content for parent group.
  if (arg(5) && (int)arg(5) > 0) {
    $root_node = node_load((int)arg(5));
    if (isset($root_node->group_audience) && is_array($root_node->group_audience)) {
      $lang_group = current($root_node->group_audience);
      $node->base_group = $lang_group[0]['gid'];
    }

    // Try if the node is a group.
    if (!isset($lang_group) || !$lang_group[0]['gid']) {
      $root_group_info = og_get_group('node', $root_node->nid);
      if ($root_group_info) {
        $node->base_group = $root_group_info->gid;
      }
    }
  }

  // Check that the user has access to create node.
  if (!node_access('create', $node->type)) {
    $out[] = ctools_modal_command_display(t('Access denied'), '<div class="modal-message">' . t('Access denied') . '</div>');
    print ajax_render($out);
    exit;
  }

  // Without js load to actual node form.
  if (!$js) {
    return drupal_get_form($node->type . '_node_form', $node);
  }

  $type_name = node_type_get_name($node);
  $form_state = array(
    'title' => t('Add @type', array('@type' => $type_name)),
    'ajax' => TRUE,
    'build_info' => array(
      'args' => array(
        'node' => $node,
      ),
    ),
  );
  $out = ctools_modal_form_wrapper($node->type . '_node_form', $form_state);

  print ajax_render($out);
  exit;
}


/**
 * Implemenets hook_form_FORM_ID_alter().
 *
 * @param array $form
 * @param array $form_state
 */
function graphmind_relationship_form_node_form_alter(&$form, &$form_state) {
  if (isset($form['#node']) && isset($form['#node']->graphmind_relationship_parent)) {
    $form['actions']['submit']['#submit'][] = 'graphmind_relationship_modal_page_submit';
  }
}


/**
 * The submit callback of graphmind_relationship_modal_page modal window.
 *
 * @param array $form
 * @param array $form_state
 */
function graphmind_relationship_modal_page_submit($form, &$form_state) {
  if (isset($_SESSION['graphmind_relationship_default_node_type'])) {
    unset($_SESSION['graphmind_relationship_default_node_type']);
  }
  if (isset($_SESSION['graphmind_relationship_parent_nid'])) {
    unset($_SESSION['graphmind_relationship_parent_nid']);
  }

  $form_state['redirect'] = 'graphmind_relationship/dismiss_node_creation/' . $form_state['node']->graphmind_relationship_parent . '/' . $form_state['node']->nid;
  $form_state['no_redirect'] = FALSE;
}

/**
 * Page callback to close the node creation modal window.
 * It's important to have these actions separately and not breaking the form_process with an ajax exit - it prevented
 * running the form cache logic and some other form operations.
 *
 * @param $graphmind_relationship_parent
 * @param $nid
 * @return array
 */
function graphmind_relationship_dismiss_node_creation($graphmind_relationship_parent, $nid) {
  $vars = array(
    'parent_nid' => $graphmind_relationship_parent,
    'child_nid' => $nid,
  );
  drupal_add_js(array('graphmind_relationship' => $vars), 'setting');

  ctools_include('ajax');
  ctools_include('modal');
  return array(
    '#type' => 'ajax',
    '#commands' => array(
      ctools_modal_command_dismiss(),
    ),
  );
}

/**
 * Implements hook_field_attach_view_alter().
 *
 * @param array $output
 * @param array $context
 */
function graphmind_relationship_field_attach_view_alter(&$output, $context) {
  ctools_include('ajax');
  ctools_include('modal');
  ctools_modal_add_js();

  foreach (element_children($output) as $field_name) {
    $element = &$output[$field_name];
    if ($element['#field_type'] == 'graphmind') {
      $plugins = unserialize($element['#items'][0]['plugins']);
      
      // If the realtion plugin enabled, the module will puts the javascript in the top of the render array.
      if ($plugins['Relationship']) {
        $element['#suffix'] = ctools_modal_text_button('', "graphmind_relationship/overlay/nojs", '');
        if (isset($element['#attached']['js'])) {
          $first_element = $element['#attached']['js'][0];
          $element['#attached']['js'][0] = drupal_get_path('module', 'graphmind_relationship') . '/js/script.js';
          array_push($element['#attached']['js'], $first_element);
        }
        else {
          $element['#attached']['js'][] = drupal_get_path('module', 'graphmind_relationship') . '/js/script.js';
        }
      }
    }
  }
}


/**
 * Create a relationship - if not exist already.
 *
 * @param integer $source_nid
 * @param integer $target_nid
 * @param string $relationship_rid
 * @return boolean
 */
function graphmind_relationship_add_relationship($source_nid, $target_nid, $relationship_rid = NULL) {
  if ($relationship_rid == NULL) {
    $relationship_rid = GRAPHMIND_RELATIONSHIP_DEFAULT;
  }

  $count = db_query('
    SELECT COUNT(*) AS count
    FROM {graphmind_relationship}
    WHERE
      source_nid = :source_nid AND
      target_nid = :target_nid AND
      relationship_rid = :relationship_rid
  ', array(
    ':source_nid' => $source_nid, 
    ':target_nid' => $target_nid, 
    ':relationship_rid' => $relationship_rid
  ))->fetchObject();

  if ($count->count == 0) {
    $rel = new stdClass();
    $rel->relationship_rid = $relationship_rid;
    $rel->source_nid = $source_nid;
    $rel->target_nid = $target_nid;
    drupal_write_record('graphmind_relationship', $rel);
  }

  return TRUE;
}


/**
 * Remove relationship. Remove all if kind of the relationship is not defined.
 *
 * @param integer $source_nid
 * @param integer $target_nid
 * @param string $relationship_rid
 * @return boolean
 */
function graphmind_relationship_delete_relationship($source_nid, $target_nid, $relationship_rid = NULL) {
  $query = db_delete('graphmind_relationship')
    ->condition('source_nid', $source_nid)
    ->condition('target_nid', $target_nid);

  if ($relationship_rid !== NULL) {
    $query = $query->condition('relationship_rid', $relationship_rid);
  }
  
  $query->execute();

  return TRUE;
}


/**
 * Retreive relationships of a node.
 *
 * @param integer $source_nid
 * @param string $relationship_rid
 * @return array
 */
function graphmind_relationship_get_relationships($source_nid, $relationship_rid = NULL) {
  $query = 'SELECT * FROM {graphmind_relationship} WHERE source_nid = :source_nid';
  $params = array(':source_nid' => $source_nid);

  if ($relationship_rid !== NULL) {
    $query .= ' AND relationship_rid = :relationship_rid';
    $params[':relationship_rid'] = $relationship_rid;
  }
  
  $res = db_query($query, $params)->execute();
  $relationships = array();
  foreach ($res as $row) {
    $relationships[] = $row;
  }

  return $relationships;
}


/**
 * Implements hook_graphmind_plugin_info().
 *
 * @return array
 */
function graphmind_relationship_graphmind_plugin_info() {
  return array(
    'graphmind_relationship' => array(
      // Module implement this plugin
      'module' => 'graphmind_relationship',
      // Name of the plugin in flash (src/plugin/Plugin.as)
      'plugin' => 'Relationship',
      'name' => t('Graphmind relationship'),
      'description' => t('Makes GraphMind a relationship browser.'),
    ),
  );
}

/**
 * Implements hook_graphmind_flashvars_alter().
 *
 * @param array $flashvars
 */
function graphmind_relationship_graphmind_flashvars_alter(&$flashvars, $item) {
  $settings = unserialize($item['settings']);
  if (!empty($settings['graphmind_relationship'])) {
    if (!empty($settings['graphmind_relationship']['graphmind_relationship_depth'])) {
      $flashvars['graphmindRelationshipDepth'] = $settings['graphmind_relationship']['graphmind_relationship_depth'];
    }
    if (!empty($settings['graphmind_relationship']['graphmind_relationship_default_created_node_type'])) {
      $flashvars['graphmindRelationshipDefaultCreatedNodeType'] = str_replace('_', '-', $settings['graphmind_relationship']['graphmind_relationship_default_created_node_type']);
    }
  }
}


/**
 * Implements hook_node_view().
 *
 * @param object $node
 * @param string $view_mode
 * @param string $langcode
 */
function graphmind_relationship_node_view($node, $view_mode, $langcode) {
  if ($node->type == 'graphmind') {
    drupal_add_js(drupal_get_path('module', 'graphmind_relationship') . '/js/script.js');
    drupal_add_js(array('graphmindRelationshipBasePath' => url('<front>', array('absolute' => TRUE))), 'setting');
  }
  
  $create_map_from_node_content_types = array_filter(variable_get('graphmind_relationship_create_from_node_types', array()));
  if (in_array($node->type, $create_map_from_node_content_types)) {
    $node->content['graphmind_relationship'] = array(
      '#markup' =>
        '<div class="graphmind_relationship_link">' . 
        l(
          t('Create a new mindmap from this node'), 
          'node/add/graphmind', 
          array('query' => array('graphmind_relationship_root_node' => $node->title . ' [' . $node->nid . ']'))
        ) . 
        '</div>',
    );
  }
}


/**
 * Implements hook_node_insert().
 *
 * @param object $node
 */
function graphmind_relationship_node_insert($node) {
  if (isset($_SESSION['graphmind_relationship_parent'])) {
    $_SESSION['graphmind_relationship_child'] = $node->nid;
  }
}


/**
 * Implements hook_node_delete().
 *
 * @param object $node
 */
function graphmind_relationship_node_delete($node) {
  db_delete('graphmind_relationship')
   ->condition(db_or()
    ->condition('source_nid', $node->nid)
    ->condition('target_nid', $node->nid)
   )
   ->execute();
}


/**
 * Page callback - results a list of node titles for the autocomplete field.
 * 
 * @param string $fragment 
 */
function graphmind_relationship_root_node_autocomplete($fragment) {
  $result = db_select('node')->fields('node', array('nid', 'title'))->condition('title', db_like($fragment) . '%', 'LIKE')->range(0, 10)->execute();
  
  $nodes = array();
  foreach ($result as $row) {
    $nodes[$row->title . ' ['. $row->nid . ']'] = $row->title;
  }
    
  drupal_json_output($nodes);
  module_invoke_all('exit');
  exit;
}

/**
 * Returns the node view.
 * 
 * @param object $node 
 */
function graphmind_relationship_node_preview($node) {
  $commands = array();

  if ($node) {
    $commands[] = ajax_command_html('#graphmindNodePreview', drupal_render(node_view($node)));
    if (module_exists('comment') && $node->comment == COMMENT_NODE_OPEN) {
      $commands[] = ajax_command_html('#graphmindNodePreviewComment', drupal_render(comment_node_page_additions($node)));
    }
  }
  else {
    $commands[] = ajax_command_html('#graphmindNodePreview', t('Missing view.'));
  }

  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}


/**
 * Implements hook_block_info().
 *
 * @return array
 */
function graphmind_relationship_block_info() {
  $blocks = array();
  $blocks[GRAPHMIND_RELATIONSHIP_NODE_PREVIEW_BLOCK] = array(
    'info' => t('Node preview for GraphMind'),
  );
  return $blocks;
}


/**
 * Implements hook_block_view().
 *
 * @param string $delta
 * @return array
 */
function graphmind_relationship_block_view($delta = '') {
  $block = array();
  if ($delta == GRAPHMIND_RELATIONSHIP_NODE_PREVIEW_BLOCK) {
    $block['subject'] = t('Node Preview');
    $block['content'] = '<div id="graphmindNodePreview"></div><div id="graphmindNodePreviewComment"></div>';
  }
  return $block;
}


/**
 * Implements hook_graphmind_service_field_settings_form_alter().
 * 
 * @param array $form
 * @param array $settings 
 */
function graphmind_relationship_graphmind_service_field_widget_form_alter(&$form, $settings, $instance) {
  if (!isset($instance['settings']['graphmind_relationship']) || !$instance['settings']['graphmind_relationship']) {
    return;
  }

  $node_types = node_type_get_types();
  $node_type_options = array();
  foreach ((array)$node_types as $type_name => $node_type) {
    $node_type_options[$type_name] = $node_type->name;
  }
  
  $default_values = array();
  if (isset($settings['settings'])) {
    $all_settings = is_array($settings['settings']) ? $settings['settings'] : unserialize($settings['settings']);
    if (isset($all_settings['graphmind_relationship'])) {
      $default_values = $all_settings['graphmind_relationship'];
    }
  }
  
  $form['settings']['graphmind_relationship'] = array(
    '#type' => 'fieldset',
    '#title' => t('Relationship plugin'),
    '#collapsible' => FALSE,
  );
  
  $form['settings']['graphmind_relationship']['graphmind_relationship_depth'] = array(
    '#type' => 'textfield',
    '#title' => t('Depth of loaded relationships'),
    '#description' => t('This value will be the maximum depth of levels when a node is loading it\'s relationsips.'),
  );
  if (isset($default_values['graphmind_relationship_depth'])) {
    $form['settings']['graphmind_relationship']['graphmind_relationship_depth']['#default_value'] = $default_values['graphmind_relationship_depth'];
  }
  
  $form['settings']['graphmind_relationship']['graphmind_relationship_root_node'] = array(
    '#type' => 'textfield',
    '#title' => t('Root node'),
    '#autocomplete_path' => 'graphmind_relationship_root_node',
    '#description' => t('Root node of the map. Only available if the map is empty. 
      Use the autocomplete field to find a valid node.'),
  );
  if (isset($default_values['graphmind_relationship_root_node'])) {
    $form['settings']['graphmind_relationship']['graphmind_relationship_root_node']['#default_value'] = $default_values['graphmind_relationship_root_node'];
  }
  
  $form['settings']['graphmind_relationship']['graphmind_relationship_default_created_node_type'] = array(
    '#type' => 'select',
    '#title' => t('Default node type for instant node creation'),
    '#options' => $node_type_options,
    '#description' => t('This will be the default node type when creating a node from the map.'),
  );
  if (isset($default_values['graphmind_relationship_default_created_node_type'])) {
    $form['settings']['graphmind_relationship']['graphmind_relationship_default_created_node_type']['#default_value'] = $default_values['graphmind_relationship_default_created_node_type'];
  }
}

/**
 * Implements hook_graphmind_service_field_attach_collect_settings_alter().
 * 
 * @param array $settings
 * @param object $entity
 * @param string $entity_type 
 */
function graphmind_relationship_graphmind_service_field_attach_collect_settings_alter(&$settings, $entity, $entity_type) {
  if (isset($settings['settings']['graphmind_relationship'])) {
    // Set root node.
    if ($entity_type == 'node' && isset($settings['plugins']) && $settings['plugins']['Relationship'] && empty($settings['map'])) {
      if (is_string($settings['settings'])) {
        $settings['settings'] = unserialize($settings['settings']);
      }
      $root_node = NULL;
      if (!empty($settings['settings']['graphmind_relationship']['graphmind_relationship_root_node'])) {
        $root_nid = preg_replace("/^.*\[(\d*)\]$/", '$1', $settings['settings']['graphmind_relationship']['graphmind_relationship_root_node']);
        $root_node = node_load((int)$root_nid);
      }

      $time = time();
      $title = $root_node ? $root_node->title : $entity->title;
      $nid = $root_node ? $root_node->nid : $entity->nid;
      $uid = $root_node ? $root_node->uid : $entity->uid;
      $node_url = urlencode(url('node/' . $nid, array('absolute' => TRUE)));
      $amfphp_gateway = urlencode(url(variable_get('graphmind_default_endpoint', 'services/amfphp'), array('absolute' => TRUE)));
      
      $settings['map'] = "<map version=\"0.9.0\">
<node CREATED=\"{$time}\" MODIFIED=\"{$time}\" ID=\"ID_0\" FOLDED=\"false\" TEXT=\"{$title}\" LINK=\"{$node_url}\" TYPE=\"node\" >
<attribute NAME=\"userid\" VALUE=\"{$uid}\"/>
<attribute NAME=\"nid\" VALUE=\"{$nid}\"/>
<attribute NAME=\"title\" VALUE=\"{$title}\"/>
<site URL=\"{$amfphp_gateway}\" />
</node>
</map>";
    }
  }
}

/**
 * Implements hook_graphmind_service_entity_wrapper_definition_alter().
 *
 * @param array $definition
 * @param string $field_name
 */
function graphmind_relationship_graphmind_service_entity_wrapper_definition_alter(&$definition, $field_name) {
  $definition[$field_name . '__graphmind_relationship_depth'] = array(
    'description' => t('GraphMind relationship: depth'),
    'label' => t('Relationship depth'),
    'type' => 'integer',
    'field' => TRUE,
    'required' => FALSE,
    'setter callback' => 'graphmind_relationship_entity_metadata_property_set',
  );

  $definition[$field_name . '__graphmind_relationship_root_node'] = array(
    'description' => t('GraphMind relationship: root node'),
    'label' => t('Relationship root node'),
    // @TODO try entity lookup.
    'type' => 'text',
    'field' => TRUE,
    'required' => FALSE,
    'setter callback' => 'graphmind_relationship_entity_metadata_property_set',
  );

  $definition[$field_name . '__graphmind_relationship_default_created_node_type'] = array(
    'description' => t('GraphMind relationship: default node type'),
    'label' => t('Relationship default node type'),
    // @TODO Use list with node types.
    'type' => 'text',
    'field' => TRUE,
    'required' => FALSE,
    'setter callback' => 'graphmind_relationship_entity_metadata_property_set',
  );
}

/**
 * Setter callback for the relationship properties.
 *
 * @param object $entity
 * @param string $name
 * @param string $value
 * @param string $langcode
 * @param string $entity_type
 */
function graphmind_relationship_entity_metadata_property_set($entity, $name, $value, $langcode, $entity_type) {
  list($field_name, $var_name) = explode('__', $name);

  if (!isset($entity->{$field_name})) {
    $entity->{$field_name} = array(
      $langcode => array(array()),
    );
  }

  foreach ((array)$entity->{$field_name}[$langcode] as $delta => $entity_field) {
    if (!isset($entity->{$field_name}[$langcode][$delta]['settings'])) {
      $settings = array();
    }
    else {
      $settings = unserialize($entity->{$field_name}[$langcode][$delta]['settings']);
    }

    $settings['graphmind_relationship'][$var_name] = $value;
    $entity->{$field_name}[$langcode][$delta]['settings'] = serialize($settings);
  }
}

/**
 * Implements hook_graphmind_service_field_instance_settings_form_alter().
 *
 * @param array $form
 * @param array $instance
 */
function graphmind_relationship_graphmind_service_field_instance_settings_form_alter(&$form, $instance) {
  $form['graphmind_relationship'] = array(
    '#type' => 'checkbox',
    '#title' => t('GraphMind relationship settings'),
    '#default_value' =>
      !isset($instance['settings']['graphmind_relationship']) ||
      $instance['settings']['graphmind_relationship'] === NULL ||
      isset($instance['settings']['graphmind_relationship']) &&
      $instance['settings']['graphmind_relationship'],
  );
}
