<?php
class Project_PutController extends Library_Controller {
	public function __call($methodName, $args) {
		//inicializace objektu
		$objectInfo = $this->_parseActionName($methodName);
		$response = new Library_Response();
		$tableProjects = new Application_Model_Projects();
		
		//kontrola dat
		try {
			//konotrla odeslanych dat
			if (!isset($_REQUEST["project"])) {
				//data nebyla odeslana
				$response->setStatus("DATA_NOT_SENT", 403);
				throw new Zend_Exception("Data not sent to server");
			}
			
			$data = $_REQUEST["project"];
			
			//nacteni projektu
			$project = $tableProjects->find($objectInfo["id"])->current();
			
			if (!$project) {
				//projekt neexistuje
				$response->setStatus("PROJECT_NOT_FOUND", 404);
				throw new Zend_Exception("Project id " . $objectInfo["id"] . " not found");
			}
			
			//nacteni seznamu spolupracovniku a kontrola opravneni pristupu
			$colabours = $project->findDependentRowset("Application_Model_ProjectsColabours", "project");
			
			$colabourIds = array();
			
			foreach ($colabours as $colabour) {
				$colabourIds[] = $colabour["user_id"];
			}
			
			//konotrla opravneni pristupu
			if ($project->user_id != $this->_user->id && $this->_user->id != 1 && !in_array("PROJECT_MANAGER", $this->_user->getRoles()) && !in_array($this->_user->id, $colabourIds)) {
				//uzivatel neni opravnen k teto metode
				$response->setStatus("NOT_PERMITED", 403);
				throw new Zend_Exception("User id " . $this->_user->id . " is not permited to change project id " . $project->directory_id);
			}
		} catch (Zend_Exception $e) {
			$this->_sendToRender($response, $objectInfo["format"], "put");
			return;
		}
		
		//update obecnych informaci
		if (isset($data["project_name"]) || isset($data["description"])) {
			//obecne informace se maji updatovt
			if ($this->_user->id != $project->user_id && $this->_user->id != 1 && !in_array("PROJECT_MANAGER", $this->_user->getRoles())) {
				$response->setStatus("NOT_PERMITED", 403);
				$this->_sendToRender($response, $objectInfo["format"], "put");
				return;
			}
			
			//nastaveni dat
			if (isset($data["project_name"])) {
				$project->project_name = $data["project_name"];
			}
			
			if (isset($data["description"])) {
				$project->description = $data["description"];
			}
			
			$project->save();
		}
		
		if (is_uploaded_file($_FILES["project"]["tmp_name"]["content"])) {
			//existuje zadost o update dokumentu v projektu
			if (!$this->_updateProject($objectInfo, $project, $response)) {
				$this->_sendToRender($response, $objectInfo["format"], "put");
				return;
			}
		}
		
		$response->setOk();
		$this->view->project_id = $project->id;
		$this->_sendToRender($response, $objectInfo["format"], "put");
	}
	
	protected function _updateProject($objectInfo, $project, $response) {
		//otevreni zip archivu
		$zip = new ZipArchive();
		$zip->open($_FILES["project"]["tmp_name"]["content"]);
		
		//rozbaleni zipu do docasneho adredare
		$tmpName = tempnam(APPLICATION_PATH. "/../tmp/project/", "dir_");
		unlink($tmpName);
		
		mkdir($tmpName);
		
		$zip->extractTo($tmpName);
		
		//nacteni zakladniho souboru
		$fp = fopen($tmpName . "/.project-" . $project->directory_id, "r");
		
		if (!$fp) {
			$response->setStatus("ZIP_IS_CORRUPTED", 500);
			return false;
		}
		
		$index = array();
		
		while (!feof($fp)) {
			//nacteni radku
			$row = fgets($fp, 120);
			$row = trim($row);
			
			//rzodeleni na hash cesty a uuid
			list($pathHash, $uuid) = explode(" ", $row);
			
			if (strlen($pathHash) != 40 || strlen($uuid) != 40) {
				//data jsou nekorektni
				continue;
			}
			
			$index[$pathHash] = $uuid;
		}
		
		//nacteni korenoveho adresare projektu
		$root = $project->findParentRow("Application_Model_DocumentsDirectories", "directory");
		
		//vytvoreni zakladni cesty
		$basePath = $tmpName . "/" . $root->directory_name;
		
		$this->_checkDir($basePath, $root);
		
		return true;
	}
	
	/**
	 * zkontroluje a porovna obsah adredare na disku a v systemu
	 * @param string $path cesta k fyzickemu adredari
	 * @param Zend_Db_Table_Row $directory radek obsahujici informace o adresari v systemu
	 */
	protected function _checkDir($path, Zend_Db_Table_Row_Abstract $directory) {
		//nacteni podrizenych dokumentu a adredaru v systemu
		$directories = $directory->findDependentRowset("Application_Model_DocumentsDirectories", "parent");
		$directoryList = array();		//seznam podadredaru indexovany podle id
		$directoryIndex = array();		//seznam podadresaru indexovany podle jmena
		
		foreach ($directories as $dir) {
			$directoryList[$dir->id] = $dir;
			$directoryIndex[$dir->directory_name] = $dir;
		}
		
		//nacteni dokumentu a jejich lokalnich jmen
		$documents = $directory->findManyToManyRowset("Application_Model_Documents", "Application_Model_DocumentsDirectoriesHasManyDocuments", "directory", "document");
		$documentNames = $directory->findDependentRowset("Application_Model_DocumentsDirectoriesHasManyDocuments", "directory");
		
		//serazeni jmen
		$nameList = array();
		
		foreach ($documentNames as $name) {
			if ($name->document_name) {
				$nameList[$name->document_uuid] = $name->document_name;
			}
		}
		
		//vygenerovani jmen dokumentu
		$documentList = array();		//seznam dokumentu indexovany podle UUID
		$documentIndex = array();		//seznam dokumentu indexovany podle jmena
		
		foreach ($documents as $document) {
			$documentList[$document->uuid] = $document;
			
			//vyhodnoceni jmena
			$name = isset($nameList[$document->uuid]) ? $nameList[$document->uuid] : $document->document_name;
			
			//zapsani do jmenneho indexu
			$documentIndex[$name] = $document;
		}
		
		//prochazeni fyzickeho adresare
		$physDirs = array();		//pole obshaujici seznam fyzickych adredaru
		$physDocs = array();		//pole obsahujici seznam fyzickych dokumentu
		
		$dir = dir($path);
		
		while ($fragment = $dir->read()) {
			//kontorla . a ..
			if ($fragment == "." || $fragment == "..") {
				continue;
			}
			
			//vyhodnoceni jesli se jedna o adredar nebo dokument
			if (is_file($path . "/" . $fragment)) {
				//objekt je dokument
				$this->_solveDocument($documentIndex, $documentList, $fragment, $path, $directory);
				
				//pridani do seznamu fyzickych dokumentu
				$physDocs[] = $fragment;
			} else {
				//objekt je adresar
				$this->_solveDir($directoryIndex, $directoryList, $fragment, $directory);
				
				//pridani do seznamu fyzickych adredaru
				$physDirs[] = $fragment;
			}
		}
		
		$tableAssocs = new Application_Model_DocumentsDirectoriesHasManyDocuments();
		
		//smazani smazanych dokumentu
		foreach ($documentIndex as $name => $document) {
			//kontrola jestli dokument existuje
			if (!in_array($name, $physDocs)) {
				//dokument neexistuje, smaze se
				$tableAssocs->delete(array(
					$tableAssocs->getAdapter()->quoteInto("document_uuid like ?", $document->uuid)
					, "document_directory_id = " . $directory->id
				));
			} 
		}
		
		//smazani neexistujicich adresaru
		foreach ($directoryIndex as $name => $dir) {
			if (!in_array($name, $physDirs)) {
				//adresar neexstuje
				$this->_removeSysDir($dir, $tableAssocs);
			}
		}
	}
	
	/**
	 * provede overeni jestli fyzicky adredar existuje ve virtualni strukture a pripadne ho vytvori
	 * @param array $dirIndex seznam podadredaru rodice klicovany dle jmena
	 * @param array $dirList seznam podadresaru rodice klicovany dle ID
	 * @param string $fragment jmeno adresare
	 * @param Zend_Db_Table_Row_Abstract $parent rodicovsky adresar
	 */
	protected function _solveDir(& $dirIndex, & $dirList, $fragment, Zend_Db_Table_Row_Abstract $parent) {
		//pokud adresar existuje, nic se provadet nebude
		if (isset($dirIndex[$fragment])) {
			return;
		}
		
		//vytvori se novy adredar
		$tableDirectories = new Application_Model_DocumentsDirectories();
		$row = $tableDirectories->createRow();
		
		//nastavnei hodnot
		$row->parent_id = $parent->id;
		$row->directory_name = $fragment;
		$row->user_id = $this->_user->id;
		$row->group_id = 0;
		$row->depth = $parent->depth + 1;
		
		//ulozeni
		$row->save();
		
		//zapsani adredare do seznamu
		$dirIndex[$fragment] = $row;
		$dirList[$row->id] = $row;
	}
	
	/**
	 * zkontorluje jestli fyzicky dokument existuje v adredarove strukture
	 * pokud ne, vytvori ho
	 * pokud ano, zkontroluje zmeny a pripadne provede update
	 * 
	 * @param array $docIndex seznam fyzickych dokumentu v rodicovskem adredari klicovany dle jmena
	 * @param array $docList seznam fyzickych dokumentu v rodicovskem adresari klicovany dle UUID
	 * @param string $fragment jmeno fyzickeho dokumentu
	 * @param string $basePath cesta k dokumentu
	 * @param Zend_Db_Table_Row_Abstract $parent rodicovsky systemovy adresar
	 */
	protected function _solveDocument(& $docIndex, & $docList, $fragment, $basePath, Zend_Db_Table_Row_Abstract $parent) {
		//kontorla, jestli dokument v adresari existuje
		$tableDocuments = new Application_Model_Documents();
		
		if (isset($docIndex[$fragment])) {
			//dokument existuje, zkontroluje se obsah
			$document = $docIndex[$fragment];
			
			$newSha = sha1_file($basePath . "/" . $fragment);
			$oldSha = sha1_file(CONTENT_PATH . "/" . $document->uuid);

			//pokud se dokument nezmenil, nic se nebude delat
			if (strcmp($newSha, $oldSha) == 0) {
				return;
			}
			
			//vytvoreni noveho zaznamu
			$newUuid = $document->save();
			
			//prejmenovani stareho obsahu
			rename(CONTENT_PATH . "/" . $document->uuid, CONTENT_PATH . "/" . $newUuid);
			
			//prekopirovani noveho obsahu
			copy($basePath . "/" . $fragment, CONTENT_PATH . "/" . $document->uuid);
		} else {
			//dokument neexistuje, vytvori se
			
			//nastaveni obecnych dat
			$document["document_name"] = $fragment;
			$document["user_id"] = $this->_user->id;
			$document["group_id"] = 0;
			$document["mime_type"] = "application/octet-stream";
			$document["size"] = filesize($basePath . "/" . $fragment);
			
			//vytvoreni dokumentu
			$documentRow = $tableDocuments->createRow($document);
			$documentRow->save();
			
			//nalezeni puvodniho dokumentu
			$tableHistory = new Application_Model_DocumentsHistory();
			$history = $tableHistory->fetchRow($tableHistory->getAdapter()->quoteInto("document_last_uuid like ?", $documentRow->uuid));
			
			//ulozeni obsahu do souboru
			copy($basePath . $fragment, CONTENT_PATH . "/" . $history->document_first_uuid);
			
			//vytvoreni symlinku
			symlink(CONTENT_PATH . "/" . $history->document_first_uuid, CONTENT_PATH . "/" . $row->uuid);
			
			//vytvoreni asociace na adredar
			$tableDocDir = new Application_Model_DocumentsDirectoriesHasManyDocuments();
			
			$data = array("document_uuid" => $documentRow->uuid, "document_directory_id" => $parent->id);
			$row = $tableDocDir->createRow($data);
			$row->save();
			
			//zapsani do listu
			$docList[$documentRow->uuid] = $documentRow;
			$docIndex[$fragment] = $documentRow;
		}
	}
	
	/**
	 * rekurzivne smaze adresar z fyzickeho disku
	 * @param string $path cesta k adredari
	 */
	protected function _removeDir($path) {
		//vytvoreni instance adredare
		$dir = dir($path);
		
		while ($filename = $dir->read()) {
			//konotrla . a ..
			if ($filename == "." || $filename == "..") {
				continue;
			}
			
			//kontrola typu objektu
			if (is_file($path . "/" . $filename)) {
				//objekt je soubor
				unlink($path . "/" . $filename);
			} else {
				
			}
		}
	}
	
	/**
	 * rekurzivne smaze systemovy adresar
	 * @param Zend_Db_Table_Row_Abstract $directory adresar ke smazani
	 * @param Application_Model_DocumentsDirectoriesHasManyDocuments instance tabulky asociaci
	 */
	protected function _removeSysDir(Zend_Db_Table_Row_Abstract $directory, Application_Model_DocumentsDirectoriesHasManyDocuments $tableAssocs = null) {
		//kontorla existence instance tabbulky asociaci
		if (!$tableAssocs) {
			$tableAssocs = new Application_Model_DocumentsDirectoriesHasManyDocuments();
		}
		
		//smazani asociaci
		$tableAssocs->delete("document_directory_id = " . $directory->id);
		
		//smazani adredaru
		$children = $directory->findDependentRowset($directory->getTable(), "parent");
		
		foreach ($children as $child) {
			$this->_removeSysDir($directory, $tableAssocs);
		}
		
		$directory->delete();
	}
}