<?php

/**
 * Implementation of hook_taxonomy()
 */
function lineage_taxonomy($op, $type, $term='') {
  // we care not about vocabularies
  if ($type == 'vocabulary')
    return;

  switch ($op) {
    case 'delete':
      lineage_delete_term($term['tid']);
      break;
    case 'insert':
    case 'update':
      lineage_update_term($term);
      break;
  }
}

function lineage_enable() {
  drupal_set_message(t("Updated @number taxonomy records.", array('@number' => lineage_update_all())));
}

function lineage_update_all() {
  $tids = array();

  $result = db_query("SELECT td.tid, td.name, td.weight FROM {term_data} td LEFT JOIN {term_hierarchy} th ON th.tid = td.tid WHERE th.parent = 0");
  while ($term = db_fetch_object($result)) {
    $tids = lineage_update_term_r($term, array(), $tids);
  }
  return count($tids);
}

function lineage_update_term($term) {
  $term = (object)$term;
  $base = _lineage_get_parent_lineage($term->parent);
  return count(lineage_update_term_r($term, $base, array()));
}

function lineage_update_term_r($term, $base, $tids) {
  // Extend the base.
  $base['base'] .= lineage_string($term);
  
  // Update the hierarchy for the current tid.
  db_query("DELETE FROM {term_lineage} WHERE tid = '%d'", $term->tid);
  db_query("INSERT INTO {term_lineage} (tid, lineage, depth) VALUES ('%d', '%s', '%d')", $term->tid, $base['base'], $base['depth']);

  $base['depth']++;
  // Mark that we've done this one to prevent looping.
  $tids[$term->tid] = true;

  // Update all the children.
  $result = db_query("SELECT td.tid, td.name, td.weight FROM {term_hierarchy} th LEFT JOIN {term_data} td ON td.tid = th.tid WHERE th.parent = '%d'", $term->tid);
  while ($child = db_fetch_object($result)) {
    // loop protection, just in case.
    if (!isset($tids[$child->tid])) {
      $tids = lineage_update_term_r($child, $base, $tids);
    }
  }
  return $tids;
}

function lineage_delete_term($tid) {
  db_query("DELETE FROM {term_lineage} WHERE tid = '%d'", $tid);
}

function lineage_string($term) {
  // add 10 to the weight cause negative numbers don't sort the same
  // in strong form as they do numerically.
  return sprintf("%02d", $term->weight + 10) . $term->name . "\n";
}

// recurse until there are no more parents.
function _lineage_get_parent_lineage($tid) {
  if (!$tid)
    return array();
  $term = db_fetch_object(db_query("SELECT td.tid, td.name, td.weight, th.parent FROM {term_hierarchy} th LEFT JOIN {term_data} td ON td.tid = th.tid WHERE td.tid = '%d'", $tid));

  if (!$term->tid)
    return array();

  $ret = _lineage_get_parent_lineage($term->parent);
  $ret['base'] .= lineage_string($term);
  $ret['depth'] += 1;
  return $ret;
}

function lineage_views_tables() {
  $tables['term_lineage'] = array(
    "name" => "term_lineage", 
    "join" => array(
      "left" => array(
        "table" => "term_node",
        "field" => "tid"
      ), 
      "right" => array(
        "field" => "tid"
      ), 
    ),
    "fields" => array(
      "lineage" => array(
      	'name' => "Lineage: Taxonomy Hierarchy",
        'sortable' => true,
        'handler' => 'lineage_view_handler',
      ),
      "depth" => array(
        'name' => "Lineage: Depth", 
        'sortable' => false
      ),
    ),
    "sorts" => array(
      "lineage" => array('name' => "Lineage: Taxonomy Hierarchy")
    ),
  );
  
  return $tables;
}

function lineage_view_handler($field, $field_details, $content, $content_details) {
	// split lineage string into pieces, i.e. hierarchial path (getting rid of weight numbers, too)
	$path = split("\n[0-9]+", "\n".$content);
	$s = '';
	// compose the path in readable form
	foreach ($path as $a) {
		if ($s != '') $s .= '  /  ';
		$s .= str_replace("\n", '', $a);
	}
	// output pseudo-link (without href) with hierarchically indented term and full path via title/alt-rollover  
	return str_repeat('&nbsp;&nbsp;&nbsp;', substr_count($content, "\n")-1)
				 . '<a title="' . $s . '" alt="' . $s . '">' . $a . '</a>';
}
