<?php
// Dépendances //
include_once('Module.inter.php');
include_once('StatusObject.inter.php');
include_once('BasicStatus.class.php');
include_once('Output.inter.php');

/**
 * Flag qui détermine qu'un module écoute sur toutes les commandes
 */

define('ALL_COMMANDE', '@');

/**
 * Expression régulière pour savoir si un nom de commande est valide
 */

define('FILTER_PARAMETRE', "/^[a-z]{1,30}$/");

/**
 * Classe qui gère l'appel au module approprié
 * 
 * @author Olivier Arteau
 */

class PAFServer {
	private static $instance = null;
	private $modules = array();
	private $outputInstance = null;
	
	// Variable à utiliser pour dire qu'aucun message/changement n'a été fait par un module //
	public static $NO_ACTION_TAKEN = null; 
	
	private function __construct() {
		if (self::$NO_ACTION_TAKEN == null)
			self::$NO_ACTION_TAKEN = BasicStatus::getInstance(BasicStatus::$EMPTY_STATUS);
	}
	
	public static function getInstance() {
		if(self::$instance == null)
			self::$instance = new PAFServer();
		
		return self::$instance;
	}
	
	/**
	 * Départ du code en appelant les modules qui ont la commande envoyé enregistré.
	 * Si aucun module ne gère cette commande directement, un rapport d'erreur est envoyé,
	 * sinon la commande est envoyée au module qui ont la commande ALL_COMMANDE enregistré.
	 * 
	 * @param (array) $data Tableau des données envoyés en paramètre. 
	 */
	
	public function start(array $data) {
		
		/*
		 * Vérification que le paramètre 'cmd' est existant et valide 
		 */
		  
		if (!isset($data['cmd']))
			throw new Exception('Impossible de trouver le paramètre "cmd"');
		
		if (preg_match(FILTER_PARAMETRE, $data['cmd']) == 0)
			throw new Exception('Le paramètre "cmd" est invalide');
		
		/*
		 * Itération de tous les modules enregistrés, pour exécuter ceux qui ont la commande envoyé enregistré.
		 * Ceux qui ont la commande ALL_COMMANDE enregistré, sont mis dans un array temporaire, si aucune commande n'a été envoyé encore.
		 */
		
		$commandeEnvoyer = false; // Flag qui détermine si un module a pris en charge la commande déjà //
		$listeCommandeAEnvoyer = array();
		$listeCommandeEnvoyer = array();
		$status = self::$NO_ACTION_TAKEN;
		
		// Parcours de la liste des modules //
		for ($iterModule=0,$limitModule=count($this->modules); $iterModule<$limitModule; $iterModule++) {
			$module = $this->modules[$iterModule];
			$commandesEnregistre = $module->getCommandesEnregistre();
			
			$envoyerCommandeFin = false;
			// Parcours de la liste des commandes supportés par un module //
			for ($iterCommande=0,$limitCommande=count($commandesEnregistre); $iterCommande<$limitCommande; $iterCommande++) {
				// Si une des commandes supportés est la même que celle à exécuter //
				if ($data['cmd'] == $commandesEnregistre[$iterCommande]) {
					// Si une modification a été prise par la commande, on envoie la modification à tous les modules qui ont déjà été exécuté //
					if (($tempStatus = $module->executerCommande($data, $status)) !== self::$NO_ACTION_TAKEN) {
						$status = $tempStatus;
						if (count($listeCommandeEnvoyer) > 0) {
							for ($i=0; $i<count($listeCommandeEnvoyer); $i++) {
								$listeCommandeEnvoyer[$i]->onStatusChange($data, $status);
							}
						}
					}
					
					array_push($listeCommandeEnvoyer, $module);
					$commandeEnvoyer = true;
					$envoyerCommandeFin = false;
					break;
				} else if ($commandesEnregistre[$iterCommande] == ALL_COMMANDE) {
					if ($commandeEnvoyer) { // Si incertain que la commande est valide on mets dans un array temporaire //
						$envoyerCommandeFin = true;
					} else { // Sinon on exécute //
						$module->executerCommande($data, $status);
						array_push($listeCommandeEnvoyer, $module);
						$envoyerCommandeFin = false; // Si doublon de ALL_COMMANDE //
						break;
					}
				}
			}
			
			if ($envoyerCommandeFin)
				array_push($listeCommandeAEnvoyer, $module);
		}
		
		// Si la commande est supporté, les commandes qui avaient le flag ALL_COMMANDE sont appelés //
		if ($commandeEnvoyer) {
			for ($i=0,$limit=count($listeCommandeAEnvoyer); $i<$limit; $i++) {
				$listeCommandeAEnvoyer[$i]->executerCommande($data, $status);
			}
			
			// On finalise le tout //
			for ($i=0,$limit=count($listeCommandeEnvoyer); $i<$limit; $i++) {
				$listeCommandeEnvoyer[$i]->finalize($data, $status);
			}
		}
		
		// Fin de l'analyse des modules, appel de la classe d'output pour finaliser //
		if (($obj = $this->getOutputInstance()) != null) {
			$obj->addHeader('X-Powered', 'PAF');
			$obj->addHeader('X-Author', 'Olivier Arteau');
			$obj->finish();
		}
	}
	
	/**
	 * Fonction que doit appeller un module pour signaler son existance.
	 * 
	 * @param (Module) $module Instance du module qui doit être enregistré
	 */
	
	public function enregistrerModule(Module $modules) {
		array_push($this->modules, $modules);
	}
	
	/**
	 * Définit l'instance qui est utilisé pour la gestion des sorties
	 * 
	 * @param (Output) $instance
	 */
	
	public function setOutputInstance(Output $instance) {
		$this->outputInstance = $instance;
	}
	
	/**
	 * Retourne l'instance qui est utilisé pour la gestion des sorties
	 * 
	 * @return (Output) $instance
	 */
	
	public function getOutputInstance() {
		return $this->outputInstance;
	}
}
?>