<?php
/**
 * Implementation of hook_help().
 */
function ec_node_variation_help($section) {
  switch ($section) {
    case 'admin/help#ec_node_variation':
    case 'admin/modules#description':
      return t('ec_node_variation - is a helper module that automatically creates a variation using the same title as a node.');
  }
}

/**
 * Implementation of hook_menu().
 */
function ec_node_variation_menu($may_cache) {
  $items = array();

  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/ec_node_variation',
      'title' => t('ec_node_variation'),
      'callback' => 'ec_node_variation_settings_form',
      'access' => user_access('administer ec_node_variation configuration'),
    );
    $items[] = array(
      'path' => 'admin/ec_node_variation/settings',
      'title' => t('settings'),
      'callback' => 'ec_node_variation_settings_form',
      'access' => user_access('administer ec_node_variation configuration'),
      'type' => MENU_DEFAULT_LOCAL_TASK
    );
  }
  return $items;
}

/**
 * Implementation of hook_perm().
 */
function ec_node_variation_perm() {
  return array('administer ec_node_variation configuration');
}

/**
 * Implementation of hook_nodeapi().
 */
function ec_node_variation_nodeapi(&$node, $op, $teaser, $page) {
  $ec_node_variation_config = variable_get('ec_node_variation_config', array());
  if (!isset($ec_node_variation_config['types'][$node->type]) || empty($ec_node_variation_config['types'][$node->type])) {
    return;
  }

  switch ($op) {
    case 'load':
      $node->ec_node_variation = ec_node_variation_get_terms($node->nid);
      break;
    case 'insert':
      $body = isset($ec_node_variation_config['body'][$node->type]) ? $node->body : '';
      // Add node title as terms.
      $tids = _ec_node_variation_add_terms($ec_node_variation_config['types'][$node->type], $node->title, $body, $node->taxonomy);

      // Save node-term association in the ec_node_variation table.
      _ec_node_variation_save_association($node->nid, $tids);
      break;
    case 'update':
      $body = isset($ec_node_variation_config['body'][$node->type]) ? $node->body : '';
      // Update node title in term(s).
      $terms = ec_node_variation_get_terms($node->nid);
      _ec_node_variation_update_terms($terms, $node->title, $body, $node->taxonomy);
      break;
    case 'delete':
      if (isset($ec_node_variation_config['delete'][$node->type])) {
        _ec_node_variation_delete_terms($node->nid);
      }
      // Delete node-term association from the ec_node_variation table.
      _ec_node_variation_delete_association($node->nid);
      break;
  }
}

/**
 * Menu Callback: ec_node_variation module settings form.
 */
function ec_node_variation_settings_form() {
  if (!module_exist('subproducts')) {
    drupal_set_message(t('The ec_node_variation module requires the subproducts module to be activated.'), 'error');
    drupal_goto('admin/modules');
  }

  $types = node_get_types();
  $variationss = _ec_node_variation_get_variations();

  if (empty($vocabularies)) {
    drupal_set_message(t('The ec_node_variation module requires at least one variation to be defined.'), 'error');
    drupal_goto('admin/store/settings/subproducts');
  }

  $ec_node_variation_config = variable_get('ec_node_variation_config', array());

  foreach ($types as $type => $name) {
    $collapsed = (!isset($ec_node_variation_config['types'][$type]) && !isset($ec_node_variation_config['body'][$type]) && !isset($ec_node_variation_config['delete'][$type])) || (empty($ec_node_variation_config['types'][$type]) && empty($ec_node_variation_config['body'][$type]) && empty($ec_node_variation_config['delete'][$type]));
    $form['ec_node_variation_'. $type] = array(
      '#type' => 'fieldset',
      '#title' => $name,
      '#collapsible' => TRUE,
      '#collapsed' => $collapsed,
    );
    $form['ec_node_variation_'. $type][$type] = array(
      '#type' => 'select',
      '#title' => t('Variations'),
      '#options' => $variations,
      '#default_value' => isset($ec_node_variation_config['types'][$type]) ? $ec_node_variation_config['types'][$type] : array(),
      '#multiple' => TRUE,
      '#description' => t('Creating a node of type %type will automatically create an attribute in any selected variations.', array('%type' => theme('placeholder', $type))),
      '#parents' => array('types', $type),
    );
    $form['ec_node_variation_'. $type]['body_'. $type] = array(
      '#type' => 'checkbox',
      '#title' => t('Associate node body with attribute description.'),
      '#default_value' => isset($ec_node_variation_config['body'][$type]) ? $ec_node_variation_config['body'][$type] : 0,
      '#parents' => array('body', $type),
    );
    $form['ec_node_variation_'. $type]['delete_'. $type] = array(
      '#type' => 'checkbox',
      '#title' => t('Delete associated attribute if a node is deleted.'),
       '#default_value' => isset($ec_node_variation_config['delete'][$type]) ? $ec_node_variation_config['delete'][$type] : 0,
      '#parents' => array('delete', $type),
    );
  }
  $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));

  return drupal_get_form('ec_node_variation_settings_form', $form);
}

/**
 * Process ec_node_variation settings form submissions.
 */
function ec_node_variation_settings_form_submit($form_id, $form_values) {
  unset($form_values['form_id'], $form_values['submit']);

  $form_values['body'] = array_filter($form_values['body']);
  $form_values['delete'] = array_filter($form_values['delete']);
  variable_set('ec_node_variation_config', $form_values);

  drupal_set_message(t('Configuration settings saved.'));
}

/**
 * Gets terms associated with a node.
 */
function ec_node_variation_get_terms($nid) {
  static $term_cache = NULL;

  if (isset($term_cache[$nid])) {
    return $term_cache[$nid];
  }

  $return = array();

  $result = db_query("SELECT td.* FROM {ec_node_variation} n INNER JOIN {term_data} td USING (tid) WHERE n.nid = %d", $nid);
  while ($term = db_fetch_object($result)) {
    $return[$term->tid] = $term;
  }

  // Cache result.
  $term_cache[$nid] = $return;

  return $return;
}

/**
 * Gets node IDs/nodes associated with a term.
 */
function ec_node_variation_get_nids($tids, $get_nodes = FALSE) {
  static $nid_cache = NULL;
  static $node_cache = NULL;

  // Keep processing to a minimum for empty tid arrays.
  if (empty($tids)) {
    return array();
  }

  // Sort tid array to ensure that the cache_string never suffers from order
  // issues. 
  sort($tids);
  $cache_string = implode('+', $tids);
  if ($get_nodes) {
    if (isset($node_cache[$cache_string])) {
      return $node_cache[$cache_string];
    }
    elseif (isset($nid_cache[$cache_string])) {
      // If the nid cache stores the same string, node_load() each nid and
      // return them.
      $nids = $nid_cache[$cache_string];
      $return = array();

      foreach ($nids as $nid) {
        $return[$nid] = node_load($nid);
      }
      $node_cache[$cache_string] = $return;
      $return = array_keys($return);
      $nid_cache[$cache_string] = $return;

      return $return;
    }
  }
  else {
    if (isset($nid_cache[$cache_string])) {
      return $nid_cache[$cache_string];
    }
    elseif (isset($node_cache[$cache_string])) {
      // If the node cache stores the same string, retrieve only the nids and
      // return them.
      $return = $node_cache[$cache_string];
      return array_keys($return);
    }
  }

  $return = array();

  // $dees == the %d list :S
  $dees = implode(', ', $tids);
  $result = db_query("SELECT nid FROM {ec_node_variation} WHERE tid IN ($dees)", $tids);
  while ($nid = db_fetch_object($result)) {
    if ($get_nodes) {
      $return[$nid->nid] = node_load($nid->nid);
    }
    else {
      $return[] = $nid->nid;
    }
  }

  if ($get_nodes) {
    $node_cache[$cache_string] = $return;
  }
  else {
    $nid_cache[$cache_string] = $return;
  }

  return $return;
}

/**
 * Retrieve all vocabularies.
 */
function _ec_node_variation_get_variations() {
  $variations = subproducts_get_variations();
  foreach ($variations as $ptype => $variation) {
    foreach ($variation as $v){
      $vars[$v->vid] = $v->name;
    }
  }
  return $vars;
}

/**
 * Add node titles as terms into the taxonomy system.
 * @todo Ideas are welcome to allow to retain hierarchy for vocabularies not 
 * present in the node form.
 */
function _ec_node_variation_add_terms($vids, $title, $body, $hierarchy = array()) {
  $edit = array('name' => $title,
    'description' => $body,
    'weight' => 0,
  );

  $tids = array();

  foreach ($vids as $vid) {
    // $edit is passed by reference and 'tid' is set with the tid of the new term. 
    unset($edit['tid']);

    $edit['vid'] = $vid;
    // Save hierarchy for vocabularies also present in the node form.
    if (isset($hierarchy[$vid])) {
      $edit['parent'] = $hierarchy[$vid];
    }
    else {
      $edit['parent'] = array();
    }
    taxonomy_save_term($edit);
    $tids[] = $edit['tid'];
  }

  return $tids;
}

/**
 * Update saved node-terms.
 */
function _ec_node_variation_update_terms($terms, $title, $body, $hierarchy) {
  $edit = array('name' => $title,
    'description' => $body,
    'weight' => 0,
  );

  foreach ($terms as $term) {
    if (isset($hierarchy[$term->vid])) {
      $edit['parent'] = $hierarchy[$term->vid];
    }
    else {
      $edit['parent'] = array();
    }
    $edit['tid'] = $term->tid;
    taxonomy_save_term($edit);
  }
}

/**
 * Delete node-terms into the taxonomy system. Presently unused.
 * @todo Options to not delete terms and to delete child nodes etc.
 */
function _ec_node_variation_delete_terms($nid) {
  $terms = ec_node_variation_get_terms($nid);
  foreach ($terms as $term) {
    taxonomy_del_term($term->tid);
  }
}

/**
 * Save node-term associations in the ec_node_variation table.
 */
function _ec_node_variation_save_association($nid, $tids) {
  foreach ($tids as $tid) {
    db_query("INSERT INTO {ec_node_variation} (nid, tid) VALUES (%d, %d)", $nid, $tid);
  }
}

/**
 * Delete node-term associations from the ec_node_variation table.
 */
function _ec_node_variation_delete_association($nid) {
  db_query("DELETE FROM {ec_node_variation} WHERE nid = %d", $nid);
}
