<?php
// wcf imports
require_once(WCF_DIR.'lib/system/event/EventListener.class.php');
require_once(WCF_DIR.'lib/data/tag/Tag.class.php');
require_once(WCF_DIR.'lib/data/tag/TagEngine.class.php');

// tagging3 imports
require_once(WCF_DIR.'lib/util/TaggingReloadedUtil.class.php');

/**
 * Displays the tab for tagging - this is base for wbb tagging reloaded message form listener
 *
 * @author	Torben Brodt
 * @package	de.easy-coding.wcf.taggingreloaded
 * @license	GNU Lesser General Public License <http://opensource.org/licenses/lgpl-3.0.html>
 */
class TaggingReloadedMessageFormListener implements EventListener {
	protected $eventObj;
	protected $className;
	protected $tags3 = array();
	
	/**
	 * @var Tag[]
	 */
	protected $existingTagsUser = array();

	/**
	 * @var Tag[]
	 */
	protected $existingTagsObject = array();

	/**
	 * @see EventListener::execute()
	 */
	public function execute($eventObj, $className, $eventName) {

		// enabled?		
		if(!MODULE_TAGGING) {
			return;
		}

		$this->eventObj = $eventObj;
		$this->className = $className;
		
		if(method_exists($this, $eventName)) {
			$this->$eventName();
		}
	}

	/**
         * @see Form::assignVariables()
         */
	protected function assignVariables () {
		WCF::getTPL()->assign(array(
			'tags3' => $this->tags3,
			'tags' => $this->eventObj->tags
		));

		WCF::getTPL()->append('specialStyles', '<script type="text/javascript" src="'.RELATIVE_WCF_DIR.'js/Wheeltags.class.js"></script>');
		WCF::getTPL()->append('additionalInformationFields', WCF::getTPL()->fetch('messageFormTaggingReloaded'));
	}
	
	/**
         * @see Form::readFormParameters()
         */
	protected function readFormParameters() {
	
		// handle reloaded input
		if (isset($_REQUEST['taggingname']) && isset($_REQUEST['taggingval'])) {
			if(count($_REQUEST['taggingname']) > count($_REQUEST['taggingval'])) {
				$_REQUEST['taggingval'] = array_slice($_REQUEST['taggingval'], 0, count($_REQUEST['taggingname']));
			} else if(count($_REQUEST['taggingname']) < count($_REQUEST['taggingval'])) {
				$_REQUEST['taggingname'] = array_slice($_REQUEST['taggingname'], 0, count($_REQUEST['taggingval']));
			}
			$this->tags3 = array_combine($_REQUEST['taggingname'], $_REQUEST['taggingval']);
		}

		// handle classic input
		if (!isset($_REQUEST['wheeltags_enabled']) || !($_REQUEST['wheeltags_enabled'])) {
			// copy reference
			$tags3 = $this->tags3;
			$this->tags3 = array();
			
			// proceed with classic
			if (isset($_REQUEST['tags']) && !empty($_REQUEST['tags'])) {
				foreach(TaggingUtil::splitString($_REQUEST['tags']) as $tag) {
					// use weights from modern input (even if hidden)
					if(array_key_exists($tag, $tags3)) {
						$this->tags3[$tag] = $tags3[$tag];
					} else if(array_key_exists($tag, $this->tags3)) {
						$this->tags3[$tag] *= 1.33; // increase with 33%
					} else {
						$this->tags3[$tag] = 100;
					}
				}
			}
		}
		
		// conversion from post to Tag
		foreach($this->tags3 as $name => $weight) {
			if(strlen($name) < 3) {
				unset($this->tags3[$name]);
				continue;
			}

			$this->tags3[$name] = new Tag(null, array(
				'name' => $name,
				'weight' => $weight
			));
		}
		
		// workaround to be called before validate from woltlab
		$this->eventObj->tags = TaggingUtil::buildString(array_keys($this->tags3));
	}
	
	/**
	 * workaround to be called after readFormData from woltlab
         * @see Form::validate()
         */
	protected function validate () {
		$this->eventObj->tags = TaggingUtil::buildString(array_keys($this->tags3));
	}
	
	public function deleteFromTagging3($userID, $tags, Tagged $object, $languageIDArray) {
		if(!count($tags)) return;
		
		$ids = array();
		foreach($tags as $tag) {
			$ids[] = $tag->tagID;
		}
		
		$sql = "DELETE FROM 	wcf".WCF_N."_tagging3
			WHERE 		taggableID = ".$object->getTaggable()->getTaggableID()."
					AND languageID IN (".implode(',', $languageIDArray).")
					AND objectID = ".$object->getObjectID()."
					AND tagID IN (".implode(",", $ids).")
					AND userID = ".intval($userID);
		WCF::getDB()->sendQuery($sql);
		$result = WCF::getDB()->sendQuery($sql);
	}
	
	/**
	 * this is a reimplemenation of TagEngine::deleteObjectTags with a slite modification.
	 * not all tags are deleted. just some tagIDs
	 */
	public function deleteFromSystem($tags, Tagged $object, $languageIDArray) {
		if(!count($tags)) return;
		
		$ids = array();
		foreach($tags as $tag) {
			$ids[] = $tag->tagID;
		}
		
		$sql = "DELETE FROM 	wcf".WCF_N."_tag_to_object
			WHERE 		taggableID = ".$object->getTaggable()->getTaggableID()."
					AND languageID IN (".implode(',', $languageIDArray).")
					AND objectID = ".$object->getObjectID()."
					AND tagID IN (".implode(",", $ids).")";
		WCF::getDB()->sendQuery($sql);
		$result = WCF::getDB()->sendQuery($sql);
	}
	
	public function addToSystem($tags, Tagged $object, $languageID) {
		if(!count($tags)) return;
		
		$tags = array_keys($tags);
		try {
			TagEngine::getInstance()->addTags($tags, $object, $languageID);
		} catch(Exception $e) {
			// please handle
		}
	}
	
	/**
	 * this is a reimplementation of TagEngine::addTags with a slite modification to write to our table and to update existing tables with weight
	 */
	public function addToTagging3($userID, $tags, Tagged $object, $languageID) {
		if(!count($tags)) return;
		
		// store tagging3 database
		$tagIDs = array();
		foreach ($tags as $tag) {
			$tagID = $tag->tagID ? $tag->tagID : Tag::test($tag->name, $languageID);
			if (!$tagID) $tagID = Tag::insert($tag->name, $languageID);
			
			if (empty($tag->userID)) $tag->userID = $userID;
			$tagIDs[$tagID] = $tag;
		}
		
		$sql = "REPLACE INTO	wcf".WCF_N."_tagging3
					(objectID, tagID, taggableID, languageID, userID, weight)
			VALUES ";
		foreach ($tagIDs as $tagID => $tag) {
			$sql .= "(" . $object->getObjectID() . ", " . $tagID . ", " . $object->getTaggable()->getTaggableID() . ", 
				".$languageID.", ".$tag->userID.", ".$tag->weight."),";
		}
		$sql = StringUtil::substring($sql, 0, StringUtil::length($sql) - 1);
		$result = WCF::getDB()->sendQuery($sql);
	}
	
	/**
	 * save
	 */
	protected function tagging3Save($userID, $tagged, $languageID) {
		$languageIDArray = array($languageID);
		
		// remove old links
		if(count($this->existingTagsUser) || count($this->existingTagsObject)) {
		
			// tagging3: remove the link to the current user
			$deleteFromTagging3 = TaggingReloadedUtil::diff($this->existingTagsUser, $this->tags3);
		
			// woltlab system: when no other user has this tag, remove the link from tag to object
			$deleteFromSystem = TaggingReloadedUtil::diff($deleteFromTagging3, $this->existingTagsObject);
			
			// tagging3: ids are known.. delete is easy
			$this->deleteFromTagging3($userID, $deleteFromTagging3, $tagged, $languageIDArray);
			
			// woltlab system: ids are known.. delete is easy
			$this->deleteFromSystem($deleteFromSystem, $tagged, $languageIDArray);
		}
		
		// tagging3: add user specific tags
		$addToTagging3 = TaggingReloadedUtil::diff($this->tags3, $this->existingTagsUser);
		
		// woltlab system: add link from tag to object
		$addToSystem = TaggingReloadedUtil::diff($addToTagging3, $this->existingTagsObject);
		
		// woltlab system: save
		$this->addToSystem($addToSystem, $tagged, $languageID);
		
		// tagging3: save
		// attention: use $this->tags3 instead of the diff, because we want to update all weights
		$this->addToTagging3($userID, $this->tags3, $tagged, $languageID);
	}
	
	/**
	 * load the existing tags of this user from database right before being saved
	 * in save method, we will watch for for this variable
	 */
	protected function tagging3Load($userID, $tagged, $languageID) {
		$this->existingTagsUser = $this->getTagsByObject($userID, $tagged, $languageID, true);
		$this->existingTagsObject = $this->getTagsByObject($userID, $tagged, $languageID, false);
	}

	/**
	 * @return Tag[]
	 */
	protected function getTagsByObject($userID, Tagged $tagged, $languageID, $even = true) {
		$taggableID = $tagged->getTaggable()->getTaggableID();
		$objectID = $tagged->getObjectID();
		$sign = $even ? '=' : '!=';
	
		$sql = "SELECT		tag.*,
					userID,
					weight
			FROM (
				SELECT		tagID,
						userID,
						weight
				FROM		wcf".WCF_N."_tagging3
				WHERE		taggableID = ".intval($taggableID)."
				AND		languageID = ".intval($languageID)."
				AND		objectID = ".intval($objectID)."
				AND 		userID $sign ".intval($userID)."
			) x
			INNER JOIN	wcf".WCF_N."_tag tag USING(tagID)
			";
		$result = WCF::getDB()->sendQuery($sql);
		$tags = array();
		while ($row = WCF::getDB()->fetchArray($result)) {
			$tags[$row['name']] = new Tag(null, $row);
		}
		return $tags;
	}
}
?>
