<?php

class WPTaxonomy {

	// This will look like (1 => 'post_category', 2 => 'link_category') etc.
	var $taxonomies;
	
	function WPTaxonomy() {
		// Gets the taxonomies out of the options table.
		$this->taxonomies = get_option('_wp_taxonomies');
	}

	/**
	 * Adds a new taxonomy type and updates the options.
	 */
	function add_taxonomy($taxonomy) {
		global $wpdb;
		$wpdb->query("INSERT INTO $wpdb->taxonomies (taxonomy) VALUES ('$taxonomy')");
		$index = $wpdb->get_var("SELECT taxonomy_id FROM $wpdb->taxonomies WHERE taxonomy = '$taxonomy' LIMIT 1");
		$this->taxonomies[$index] = $taxonomy;
		update_option('_wp_taxonomies', $this->taxonomies);
	}

	/**
	 * Removes a taxonomy type.
	 */
	function remove_taxonomy($taxonomy) {
		// Removes an entry from wpdb->taxonomies.
		// What do we do with terms that are associated in this taxonomy?
		// Updates the _wp_taxonomies option.
	}

	/**
	 * Adds a new term to the database.  Optionally marks it as an alias of an existing term.
	 * @param string $term The term to add.
	 * @param int|string $alias_of The id or slug of the new term's alias.
	 */
	function add_term($term, $alias_of = '') {
		global $wpdb;
		$term_slug = sanitize_title($term);		
		if ($alias_of) {
			$clause = (is_int($alias_of)) ? "term_id = $alias_of" : "term_slug = '$alias_of'";
			$alias = $wpdb->fetch_row("SELECT term_id, term_group FROM $wpdb->terms WHERE $clause");
			if ($alias->term_group)
				// The alias we want is already in a group, so let's use that one.
				$term_group = $alias->term_group;
			} else {
				// The alias isn't in a group, so let's create a new one and firstly add the alias term to it.
				$term_group = $wpdb->get_var("SELECT MAX() term_group FROM $wpdb->terms GROUP BY term_group") + 1;
				$wpdb->query("UPDATE $wpdb->terms SET term_group = $term_group WHERE term_id = $alias->term_id");
			}
		} else {
			$term_group = 0;
		}
		// Now add or replace the term.  This works because we have a UNIQUE key on term_slug.
		$wpdb->query("REPLACE INTO $wpdb->terms (term_name, term_slug, term_group) VALUES ($term, $term_slug, $term_group)");
	}
	
	/**
	 * Returns the index of a defined term, or 0 (false) if the term doesn't exist.
	 */
	function is_defined_term($term) {
		global $wpdb;
		return $wpdb->get_var("SELECT term_id FROM $wpdb->terms WHERE term_slug = '$term'");
	}
	
	/**
	 * Given an array of terms, returns those that are defined term slugs.  Ignores integers.
	 * @param array $terms The term slugs to check for a definition.
	 */
	function get_defined_terms($terms) {
		global $wpdb;
		foreach ($terms as $term) {
			if (!is_int($term)) {
				$searches[] = $term;
			}
		}
		$terms = "'" . implode("', '", $searches) . "'";
		return $wpdb->get_col("SELECT term_slug FROM $wpdb->terms WHERE term_slug IN ($terms)");
	}
	
	/**
	 * Returns the index of the specified taxonomy, or false if it doesn't exist.
	 * 
	 * This function can return 0 or false - evaluate the return value using ===
	 */
	function get_taxonomy_index($taxonomy) {
		$flipped_taxonomies = array_flip($this->taxonomies);
		return (isset($flipped_taxonomies[$taxonomy])) ? $flipped_taxonomies[$taxonomy] : false;
	}
	
	/**
	 * Adds a term taxonomy entry or increments the count if it already exists.
	 * @param int|string $term The term to add.
	 * @param int|string $taxonomy The context.
	 * @param string $description An optional description of the relationship.
	 * @param int $parent The parent of the term in this context.
	 */
	function add_term_taxonomy($term, $taxonomy, $description = '', $parent = 0) {
		if (!is_int($taxonomy)) {
			// Get ourselves an integer taxonomy, if the taxonomy name was used.
			$taxonomy = $this->get_taxonomy_index($taxonomy);
		}
		$clause = (is_int($term)) ? "t.term_id = $term" : "t.term_slug = '$term'";
		if ($count = $wpdb->query("SELECT tt.count FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy_id = $taxonomy AND $clause")) {
      // We'll be updating whatever's there, and incrementing the count.
      $count++;
			$wpdb->query("UPDATE $wpdb->term_taxonomy SET count = $count");
		} else {
      // There was no existing entry for this term and taxonomy type, so add a new one, using the supplied description and parent, with a count of 1.
      $wpdb->query("INSERT INTO $wpdb->term_taxonomy (term_id, taxonomy_id, term_description, parent, count) SELECT term_id, $taxonomy, '$description', $parent, 1 FROM $wpdb->terms AS t1 WHERE $clause");
		}
	}	
	
	/**
	 * Relates an object (post, link etc) to a term and taxonomy type.  Creates the term and taxonomy
	 * relationship if it doesn't already exist.  Creates a term if it doesn't exist (using the term_slug).
	 * @param array|int|string $term The slug or id of the term.
	 * @param int $object_id The object to relate to.
	 * @param array|int|string $taxonomies The context(s) in which to relate the term to the object.
	 */
	function add_term_relationship($terms, $object_id, $taxonomies) {
		global $wpdb;
		
		if (!is_array($taxonomies)) {
			$taxonomies = array($taxonomies);
		}
		
		foreach ($taxonomies as $taxonomy) {
			if (!is_int($taxonomy)) {
				// Get ourselves an integer taxonomy, if the taxonomy name was used.
				$taxonomy = $this->get_taxonomy_index($taxonomy);
			}
			$buffered_taxonomies[] = $taxonomy;			
		}
		$taxonomies = $buffered_taxonomies;
		
		if (!is_array($terms)) {
			$terms = array($terms);
		}
		
		$defined_terms = $this->get_defined_terms($terms);

		foreach ($terms as $term) {
			if (!is_int($term)) {
				if (!isset($defined_terms[$term])) {
					$new_terms[] = $term;
				}
				$term_slugs[] = $term;
			} else {
				$term_ids[] = $term;
			}
		}
		
		if (isset($new_terms) {
			// At least one of the terms specified doesn't exist, so add any new ones, using the term_slug as the name.
			$this->add_terms($new_terms);
		}

		$term_clause = (isset($term_ids)) 'tt.term_id IN (' . implode(', ', $term_ids) . ')' ? : '';
		if (isset($term_slugs)) {
			if ($term_clause) {
				$term_clause .= ' OR ';
			}
			$term_clause .= "t.term_slug IN ('" . implode("', '", $term_slugs) . "')";
			$term_join = "INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id";
		} else {
			$term_join = '';
		}
		
		// Now add or increment the term taxonomy relationships.  This is inefficient at the moment.
		foreach ($taxonomies as $taxonomy) {
			foreach ($terms as $term) {
				$this->add_term_taxonomy($term, $taxonomy);
			}
		}
		
		$taxonomies = "'" . implode("', '", $taxonomies) . "'";
		
		// Finally, relate the term and taxonomy to the object.
		$wpdb->query("INSERT INTO $wpdb->term_relationships(object_id, term_taxonomy_id) SELECT $object_id, term_taxonomy_id FROM $wpdb->term_taxonomy AS tt $term_join WHERE ($term_clause) AND tt.taxonomy_id IN ($taxonomies)");
	}
}

?>