<?php


/**
 *
 * @author Peter Csubak (chuby)
 * @copyright Peter Csubak (chuby)
 */
/** @Target("method") */
class Trigger extends Annotation{
	public $eventType = "--anyType--";
	public $senderType = "--anyType--";
	public $key = ""; // egyenlő a lockolt event zárjával, vagy, ha ez így nem adható meg, akkor
					  // akkor a metódusok közt kell legyen egy getEventKey() nevű metódus
					  // ami megmondja a kulcsot az eseményhez, így biztos, hogy csak a jó kezelő kapja meg
					  // az eseményt.
}

class EventHandler{
	
	private static $eventsToExport;
	
	/**
	 * Az exportálandó események tömbjébe, az utolsó helyen bővítve hozzáadja a megadott eseményt.
	 * (Csak a sendEvent metódus használja.)
	 * @param Event $e A hozzáadandó esemény.
	 */
	private static function addExportableEvent($e){
		if (!is_array(self::$eventsToExport)){
			self::$eventsToExport = array();
		}
		self::$eventsToExport[count(self::$eventsToExport)] = $e;
	}
	
	/**
	 * Lekérdezi a regisztrált komponensek és triggerelt objektumok listáját, majd 
	 * ezt adja vissza. (Csak a sendEvent metódus használja.)
	 * @return array Az összes regisztrált komponens, és Triggerelt objektum.
	 */
	private static function getCompsAndTriggeredObjs(){
		$res = array();
		$i = 0;
		if (SessionPeaHandler::getInstance()->getComponents() != null){
			foreach (SessionPeaHandler::getInstance()->getComponents() as $comp){
				$res[$i++] = $comp;
			}
		}
		
		if (SessionPeaHandler::getInstance()->getTriggeredObjects() != null){
			foreach (SessionPeaHandler::getInstance()->getTriggeredObjects() as $tr){
				$res[$i++] = $tr;
			}
		}
		
		return $res; 
	}

	/**
	 * Eseményszórással elküld egy eseményt, az összes arra feliratkozottnak. Azaz az összes olyan 
	 * objektumnak, amely regisztrálva van mint triggerelt objektum, vagy mint komponens. Illetve,
	 * ha az adott esemény egy exportálandó esemény, akkor azt exportálja, illetve az exportálandó
	 * események sorba helyezi. (Ezek az xml készítés közben fognak exportálódni ténylegesen.)
	 * Az eseményszórás folyamata a következő: A metódus, megkeresi a potenciális fogadóobjektumok
	 * publikus metódusait, majd leellenőrzi, hogy a Trigger annotációval annotálva vannak-e. Amennyiben
	 * igen leellenőrzi, hogy az annotáció melyik eseményeket fogadja el, amennyiben --anyType--
	 * az elfogadható események megnevezése, akkor az összes eseményt megpróbálja illeszteni, amennyiben 
	 * ez nem így van, akkor csak akkor folytatja az illesztést, ha az esemény típusa megfelel a fogadóáltal
	 * meghatározott típusnak. Ezután ehhez hasonlóan a küldő típusát is illeszti a metódus. Majd ha ez is 
	 * pozitív eredményű, akkor végül ellenőrzi az esemény zárolását, és a fogadó kulcsát. Ezt a kulcsot
	 * meg lehet adni a Trigger annotációban, is illetve, ha ez így nem adható meg, akkor a fogadó objektumnak
	 * kell legyen egy getEventKey nevű metódusa, ami megadja a kulcsot. Ez a metódus nem kap paramétert, és
	 * semmiképpen nem kaphatja meg az eseményt, ugyanis a kulcs még ha változékony is, akkor is csak az 
	 * objektum belső állapotától, és nem az esemény típusától függhet.
	 * A metódus nem dob kivételeket, így nem megfelelő paraméterezés esetén nem csinál semmit. (Ez kényelmesebb,
	 * mint a hívás helyén a kivételkezeléssel foglalkozni.)
	 * @param Event $e A küldeni kívánt esemény.
	 */
	public static function sendEvent($e){
		if ($e instanceof Event){
			$type = get_class($e);
			$senderType = get_class($e->getSender());
			$objs = self::getCompsAndTriggeredObjs();
			foreach ($objs as $obj){
				$clazz = new ReflectionClass(get_class($obj));				
				$meths = $clazz->getMethods();
				foreach ($meths as $meth){
					if ($meth->isPublic()){
						$ref = new ReflectionAnnotatedMethod(get_class($obj), $meth->getName());
						if ($ref->hasAnnotation("Trigger")){
							$annots = $ref->getAllAnnotations("Trigger");
							foreach($annots as $ann){
								if (($ann->eventType == "--anyType--") || ($ann->eventType == $type)){
									if ((($ann->senderType == "--anyType--") || ($ann->senderType == $senderType)) && 
										( ((!$clazz->hasMethod("getEventKey")) && $e->isUnlocking($ann->key)) || 
										($clazz->hasMethod("getEventKey") && ($e->isUnlocking($obj->getEventKey()))) ) &&
										($ref->getNumberOfRequiredParameters() == 1)){
											$ref->invoke($obj, $e);
										}
								}
							}
						}
					}
				}
			}
			
			if ($e instanceof ExportableEvent){
				self::addExportableEvent($e);
			}
		}
	}
	
	/**
	 * Visszaadja az exportálandó eseményeket egy tömbben.
	 * @return array Az exportálandó események.
	 */
	public static function getExportableEvents(){
		return self::$eventsToExport;
	}
	
}
?>