<?php
/**
 * class presentation
 *
 * Description for class presentation
 *
 * @author:
*/
namespace core;

use PDO;
use core\encryption;
use core\image;
use core\pathData;
use core\url;
use core\user;

class presentation {
	private $userId = null;
	
	private $presentationExists = false;
	public $presentationId = null;
	private $presentationOwner = null;
	public $presentationName = "";
	public $presentationTempo = "";
	private $presentationResults = "";
	private $presentationCode = "";

	private $slideTypes = null;

	private $slideExists = false;
	public $slideId = null;
	private $slideDateUpdated = "";
	private $slideType = "";
	private $slideName = "";
	private $slidePreview = "";
	private $slideImage = "";
	private $slideVideo = "";
	private $slideConfig = "";
	private $slideReactions = array();
	private $slideReactionsResults = array();
	private $slideOptions = array();
	private $slideResults = array();
	private $slideUsers = 0;
	private $slideUsersReactions = 0;

	public function __construct($presentationId = "", $slideId = "") {
		global $__database;

		$this->userId = user::identifier();

		if(!empty($presentationId)) {
			$query = $__database->prepare("SELECT `presentation_id`, `user_id`, `name`, `tempo`, `results`, `accessKey` FROM `presentations` WHERE `presentation_id`=:id");
			$query->bindParam(":id", $presentationId);
			$query->execute();
			if($record = $query->fetch()) {
				$this->presentationExists = true;

				$this->presentationId = $record["presentation_id"];
				$this->presentationOwner = $record["user_id"];
				$this->presentationName = $record["name"];
				$this->presentationTempo = $record["tempo"];
				$this->presentationResults = $record["results"];
				$this->presentationCode = $record["accessKey"];
			}
		}

		if(!empty($slideId)) {
			$this->slideId = $slideId;

			$this->loadSlideData();
		}
	}

	public function loadSlideData() {
		global $__database;

		$query = $__database->prepare("SELECT `board_id`, `date_updated`, `type`, `name`, `preview`, `image`, `video`, `config`, `reactions` FROM `boards` WHERE `board_id`=:id AND `presentation_id`=:presentation_id");
		$query->bindParam(":id", $this->slideId);
		$query->bindParam(":presentation_id", $this->presentationId);
		$query->execute();
		if($record = $query->fetch()) {
			$this->slideExists = true;
			
			$this->slideId = $record["board_id"];
			$this->slideDateUpdated = $record["date_updated"];
			$this->slideType = $record["type"];
			$this->slideName = $record["name"];
			$this->slidePreview = $record["preview"];
			$this->slideImage = $record["image"];
			$this->slideVideo = $record["video"];
			$this->slideConfig = $record["config"];

			$reactions = array();
			$reactionsArray = json_decode($record["reactions"], true);
			
			if(!empty($reactionsArray) && count($reactionsArray) > 0) {
				foreach($reactionsArray as $reaction) {
					$reactions[$reaction] = $reaction;

					$this->slideReactionsResults[$reaction] = 0;
				}
			}

			$this->slideReactions = $reactions;

			if(count($reactions) > 0) {
				$query = $__database->prepare("SELECT `reaction`, COUNT(`reaction_id`) AS `numReaction` FROM `boards_reactions` WHERE `board_id`=:id AND `reaction` IN (".implode(",", array_keys($this->slideReactions)).") GROUP BY `reaction`");
				$query->bindParam(":id", $this->slideId);
				$query->execute();
				while($record = $query->fetch()) {
					if(isset($this->slideReactionsResults[$reaction])) {
						$this->slideReactionsResults[$record["reaction"]] = $record["numReaction"];
						$this->slideUsersReactions += $record["numReaction"];
					}
				}
			}

			$this->slideOptions = array();

			$query = $__database->prepare("SELECT `option_id` AS `id`, `text`, `correct`, `image` FROM `boards_options` WHERE `board_id`=:id ORDER BY `order` ASC");
			$query->bindParam(":id", $this->slideId);
			$query->execute();
			while($record = $query->fetch()) {
				$this->slideOptions[] = $record;
			}

			$slideResults = $this->loadSlideResults($this->slideId, $this->presentationId, $this->slideType, $this->slideConfig, $this->slideOptions);

			$this->slideUsers = $slideResults["users"];
			$this->slideResults = $slideResults["slide"];
		}
	}

	public function loadSlideResults($slideId, $presentationId, $slideType, $slideConfig, &$slideOptions = array()) {
		$results = array("users" => 0, "slide" => array(), "results" => array());
		
		if($slideType == "empty") {
			return $results;
		}

		$type = $slideType;
		$slideType = $this->typesGet($slideType);
		if(isset($slideType["result"]["php"]) && !empty($slideType["result"]["php"]) && isset($slideType["result"]["model"]) && !empty($slideType["result"]["model"])) {
			require_once $slideType["result"]["php"];

			$className = "\\".$slideType["result"]["model"];
			$result = new $className($slideId, $presentationId, $type, $slideConfig, $slideOptions);

			$slideResults = $result->result();
			
			$results["users"] = $slideResults["users"];
			$results["results"] = $slideResults["results"];

			$slideResults = $slideResults["results"];
			if(count($slideResults) > 0) {
				if(count($slideOptions) > 0) {
					foreach($slideOptions as $optionKey => $option) {
						if(!isset($slideResults[$option["id"]])) {
							continue;
						}

						$slideOptions[$optionKey]["result"] = $slideResults[$option["id"]];
					}

					if(isset($slideResults["__slide__"])) {
						$results["slide"] = $slideResults["__slide__"];
					}
				} else {
					$results["slide"] = $slideResults;
				}
			}
		}

		return $results;
	}

	public function exists() {
		return $this->presentationExists;
	}

	public function existsOwner() {
		if(!$this->presentationExists || $this->userId != $this->presentationOwner) {
			return false;
		}
		
		return true;
	}

	public function countListOwner($find) {
		global $__database;
		
		try {
			$query = $__database->prepare("SELECT COUNT(`presentation_id`) AS `num_rows` FROM `presentations` WHERE `user_id`=:user_id".(!empty($find) ? " AND `name` LIKE :search" : ""));
			$query->bindParam(":user_id", $this->userId);
			if(!empty($find)) {
				$find = "%".$find."%";
				$query->bindParam(":search", $find);
			}
			$query->execute();
			if($record = $query->fetch(PDO::FETCH_ASSOC)) {
				return $record["num_rows"];
			}
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return 0;
	}
	
	public function listOwner($find, $page, $limit, $sort, $sortType) {
		global $__database;

		$list = array();

		$start = ($page - 1) * $limit;

		switch($sort) {
			case "name":
				$sort = "p.name";
				break;
			case "slides":
				$sort = "COUNT(DISTINCT b.board_id)";
				break;
			case "users":
				$sort = "COUNT(DISTINCT r.user_id)";
				break;
			case "date_updated":
				$sort = "`date_updated`";
				break;
			case "date_created":
			default:
				$sort = "p.date_created";
		}

		switch($sortType) {
			case "asc":
			case "desc":
				break;
			default:
				$sortType = "asc";	
		}

		try {
			$query = $__database->prepare("
			SELECT p.presentation_id, p.date_created, IF(MAX(b.date_updated) > p.date_updated, MAX(b.date_updated), p.date_updated) AS `date_updated`, p.name, p.tempo, COUNT(DISTINCT b.board_id) AS `num_boards`, COUNT(DISTINCT r.user_id) AS `num_users` 
			FROM `presentations` AS `p` 
			LEFT JOIN `boards` AS `b` ON p.presentation_id=b.presentation_id AND b.type!='empty' 
			LEFT JOIN `results` AS `r` ON p.presentation_id=r.presentation_id 
			WHERE p.user_id=:user_id".(!empty($find) ? " AND p.name LIKE :search" : "")." 
			GROUP BY p.presentation_id 
			ORDER BY {$sort} {$sortType} LIMIT {$start},{$limit}");
			$query->bindParam(":user_id", $this->userId);
			if(!empty($find)) {
				$find = "%".$find."%";
				$query->bindParam(":search", $find);
			}
			$query->execute();
			while($record = $query->fetch(PDO::FETCH_ASSOC)) {
				$record["date_created"] = date("d.m.Y, H:i", strtotime($record["date_created"]));
				$record["date_updated"] = date("d.m.Y, H:i", strtotime($record["date_updated"]));
				
				$record["url_edit"] = url::action("prezentacje", "edytuj", $record["presentation_id"]);
				$record["url_copy"] = url::action("prezentacje", "kopiuj", $record["presentation_id"]);
				$record["url_remove"] = url::action("prezentacje", "usun", $record["presentation_id"]);
				
				$list[] = $record;
			}
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return $list;
	}
	
	public function create($name, $tempo) {
		global $__database;

		$this->presentationId = $__database->newGuid();

		try {
			$__database->beginTransaction();

			$code = $__database->newPresentationCode();

			$query = $__database->prepare("INSERT INTO `presentations` (`presentation_id`, `user_id`, `name`, `tempo`, `accessKey`) VALUES (:presentation_id, :user_id, :name, :tempo, :code)");
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->bindParam(":user_id", $this->userId);
			$query->bindParam(":name", $name);
			$query->bindParam(":tempo", $tempo);
			$query->bindParam(":code", $code);
			$query->execute();

			$query = $__database->prepare("INSERT INTO `boards` (`board_id`, `presentation_id`, `type`, `order`) VALUES (UUID(), :presentation_id, 'empty', 1)");
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->execute();

			$__database->commit();

			return $this->presentationId;
		} catch(PDOException $error) {
			$__database->rollBack();

			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	public function copy($name, $tempo) {
		global $__database;

		$presentationId = $__database->newGuid();

		try {
			$boards = array();
			$boards_options = array();

			$query = $__database->prepare("SELECT `board_id`, `type`, `name`, `preview`, `image`, `video`, `config`, `reactions`, `order` FROM `boards` WHERE `presentation_id`=:presentation_id");
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->execute();
			while($record = $query->fetch()) {
				$boards[$record["board_id"]] = $record;
			}

			if(count($boards) > 0) {
				$query = $__database->prepare("SELECT `board_id`, `text`, `correct`, `image`, `order` FROM `boards_options` WHERE `board_id` IN ('".implode("', '", array_keys($boards))."')");
				$query->execute();
				while($record = $query->fetch()) {
					if(!isset($boards_options[$record["board_id"]])) {
						$boards_options[$record["board_id"]] = array();
					}

					$boards_options[$record["board_id"]][] = $record;
				}
			}

			$__database->beginTransaction();

			$code = $__database->newPresentationCode();

			$query = $__database->prepare("INSERT INTO `presentations` (`presentation_id`, `user_id`, `name`, `tempo`, `accessKey`) VALUES (:presentation_id, :user_id, :name, :tempo, :code)");
			$query->bindParam(":presentation_id", $presentationId);
			$query->bindParam(":user_id", $this->userId);
			$query->bindParam(":name", $name);
			$query->bindParam(":tempo", $tempo);
			$query->bindParam(":code", $code);
			$query->execute();

			if(count($boards) > 0) {
				foreach($boards as $board_id => $board) {
					$new_board_id = $__database->newGuid();

					$query = $__database->prepare("INSERT INTO `boards` (`board_id`, `presentation_id`, `type`, `name`, `preview`, `image`, `video`, `config`, `reactions`, `order`) VALUES (:board_id, :presentation_id, :type, :name, :preview, :image, :video, :config, :reactions, :order)");
					$query->bindParam(":board_id", $new_board_id);
					$query->bindParam(":presentation_id", $presentationId);
					$query->bindParam(":type", $board["type"]);
					$query->bindParam(":name", $board["name"]);
					$query->bindParam(":preview", $board["preview"]);
					$query->bindParam(":image", $board["image"]);
					$query->bindParam(":video", $board["video"]);
					$query->bindParam(":config", $board["config"]);
					$query->bindParam(":reactions", $board["reactions"]);
					$query->bindParam(":order", $board["order"]);
					$query->execute();

					if(isset($boards_options[$board_id]) && count($boards_options[$board_id]) > 0) {
						foreach($boards_options[$board_id] as $option) {
							$query = $__database->prepare("INSERT INTO `boards_options`(`option_id`, `board_id`, `text`, `correct`, `image`, `order`) VALUES (UUID(), :board_id, :text, :correct, :image, :order)");
							$query->bindParam(":board_id", $new_board_id);
							$query->bindParam(":text", $option["text"]);
							$query->bindParam(":correct", $option["correct"]);
							$query->bindParam(":image", $option["image"]);
							$query->bindParam(":order", $option["order"]);
							$query->execute();
						}
					}
				}
			}

			$__database->commit();

			//$this->presentationId = $presentationId;

			return $presentationId;
		} catch(PDOException $error) {
			$__database->rollBack();

			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	public function modification($name, $tempo, $results) {
		global $__database;

		try {
			$query = $__database->prepare("UPDATE `presentations` SET `name`=:name, `tempo`=:tempo, `results`=:results WHERE `presentation_id`=:presentation_id");
			$query->bindParam(":name", $name);
			$query->bindParam(":tempo", $tempo);
			$query->bindParam(":results", $tempo);
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->execute();

			return true;
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	public function modificationName($name) {
		global $__database;

		try {
			$query = $__database->prepare("UPDATE `presentations` SET `name`=:name WHERE `presentation_id`=:presentation_id");
			$query->bindParam(":name", $name);
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->execute();

			return true;
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	private function addSpaceInCode($code) {
		$code = str_split($code, 3);
		return implode(" ", $code);
	}

	public function countSlideQuiz() {
		global $__database;

		$countQuiz = 0;

		try {
			$query = $__database->prepare("SELECT COUNT(`board_id`) AS `countSlides` FROM `boards` WHERE `presentation_id`=:id AND `type`='question.008_Quiz'");
			$query->bindParam(":id", $this->presentationId);
			$query->execute();
			if($record = $query->fetch()) {
				$countQuiz = $record["countSlides"];
			}
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}
		
		return $countQuiz;
	}

	public function getOwner($renderShow = false) {
		global $__database;

		$object = array(
			"id" => $this->presentationId,
			"name" => $this->presentationName,
			"tempo" => $this->presentationTempo,
			"results" => $this->presentationResults,
			"code" => $this->addSpaceInCode($this->presentationCode),
			"boards" => array()
			);

		try {
			$idx = 1;

			$query = $__database->prepare("SELECT * FROM `boards` WHERE `presentation_id`=:id ORDER BY `order` ASC");
			$query->bindParam(":id", $this->presentationId);
			$query->execute();
			while($record = $query->fetch()) {
				$record["index"] = $idx;
				$record["type"] = $record["type"] != "empty" ? encryption::encode($record["type"]) : $record["type"];
				
				if(!empty($record["preview"])) {
					$record["preview"] = pathData::image(pathData::SLIDE_PREVIEW, $record["preview"], "public/images/empty/slidePreview.png");
				}

				$object["boards"][$record["board_id"]] = $record;
				$idx++;
			}

			if(count($object["boards"]) > 0) {
				$query = $__database->prepare("SELECT `option_id` AS `id`, `board_id`, `text`, `correct`, `image` FROM `boards_options` WHERE `board_id` IN ('".implode("','", array_keys($object["boards"]))."') ORDER BY `board_id` ASC, `order` ASC");
				$query->execute();
				while($record = $query->fetch()) {
					if(!isset($object["boards"][$record["board_id"]]["options"])) {
						$object["boards"][$record["board_id"]]["options"] = array();
					}

					$object["boards"][$record["board_id"]]["options"][] = $record;
				}

				if($renderShow == true) {
					foreach($object["boards"] as $boardId => $board) {
						$type = encryption::decode($board["type"]);
						
						$slideType = $this->typesGet($type);

						if($slideType == false || !isset($slideType["play"]["view"]) || empty($slideType["play"]["view"])) {
							continue;
						}

						$board["config"] = json_decode($board["config"], true);
						
						$reactions = array();
						$reactionsArray = json_decode($board["reactions"], true);
						
						$board["reactionsResults"] = array();

						if(!empty($reactionsArray) && count($reactionsArray) > 0) {
							foreach($reactionsArray as $reaction) {
								$reactions[$reaction] = $reaction;
								$board["reactionsResults"][$reaction] = 0;
							}
						}
						
						$board["reactions"] = $reactions;

						if(count($reactions) > 0) {
							$query = $__database->prepare("SELECT `reaction`, COUNT(`reaction_id`) AS `numReaction` FROM `boards_reactions` WHERE `board_id`=:id AND `reaction` IN (".implode(",", array_keys($reactions)).") GROUP BY `reaction`");
							$query->bindParam(":id", $boardId);
							$query->execute();
							while($record = $query->fetch()) {
								if(isset($board["reactionsResults"][$reaction])) {
									$board["reactionsResults"][$record["reaction"]] = $record["numReaction"];
								}
							}
						}

						$slideResults = $this->loadSlideResults($boardId, $this->presentationId, $type, $board["config"], $board["options"]);
						
						$board["users"] = $slideResults["users"];
						$board["results"] = $slideResults["slide"];

						$object["boards"][$boardId]["htmlPlay"] = $this->slideRenderTemplate($slideType["play"]["view"], $board);
					}
				}
			}
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}
		
		return $object;
	}

	public function removeOwner() {
		global $__database;

		try {
			$query = $__database->prepare("DELETE FROM `presentations` WHERE `presentation_id`=:presentation_id AND `user_id`=:user_id");
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->bindParam(":user_id", $this->userId);
			$query->execute();
			if($query->rowCount() > 0) {
				return true;
			}
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	public function show($step) {
		$slide = $this->slideGetStep($step);
		if($slide === false) {
			return false;
		}

		$slideType = $this->typesGet($slide["type"]);

		return $this->slideRenderTemplate($slideType["show"]["view"], $slide);
	}

	public function showSlide($slideId, $replyUserId = null) {
		global $__template;

		$slide = $this->slideGetId($slideId, $replyUserId);
		if($slide === false) {
			return false;
		}
		
		$slideType = $this->typesGet($slide["type"]);
		
		$__template->setVariable("presentationId", $this->presentationId);
		$__template->setVariable("presentationTempo", $this->presentationTempo);
		$__template->setVariable("userId", $replyUserId);
		return array("slide" => $slide, "html" => $this->slideRenderTemplate($slideType["show"]["view"], $slide));
	}

	public function result($step) {
		$slide = $this->slideGetStep($step);
		if($slide === false) {
			return false;
		}

		$slideType = $this->typesGet($slide["type"]);

		$slide["results"] = $this->slideResults($slide, $slideType);
		
		return $this->slideRenderTemplate($slideType["result"]["view"], $slide);
	}

	public function runPlay() {
		global $__database;

		try {
			$query = $__database->prepare("UPDATE `presentations` SET `isOpen`='1' WHERE `presentation_id`=:presentation_id AND `user_id`=:user_id");
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->bindParam(":user_id", $this->userId);
			$query->execute();
			if($query->rowCount() > 0) {
				return true;
			}
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	public function stopPlay() {
		global $__database;

		try {
			$query = $__database->prepare("UPDATE `presentations` SET `isOpen`='0' WHERE `presentation_id`=:presentation_id AND `user_id`=:user_id");
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->bindParam(":user_id", $this->userId);
			$query->execute();
			if($query->rowCount() > 0) {
				return true;
			}
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	/*************
	 TYPY SLAJDOW
	*************/
	public function __call($method, $arguments) {
		if(substr($method, 0, 5) == "types") {
			$this->typeInit();

			$method = "type".substr($method, 5);
		}
		
		if(method_exists($this, $method)) {
			return call_user_func_array(array($this, $method), $arguments);
		}

		print "<br /><b>Fatal error</b>:  Call to undefined method {$method} in <b>".__FILE__."</b><br />";
		exit;
	}

	private function typeInit() {
		if(!empty($this->slideTypes)) {
			return false;
		}

		$this->slideTypes = array("question" => array(), "general" => array());

		$blackList = array(".", "..");

		$pathQuestion = pathData::SLIDE_TYPE_QUESTION;
		if(file_exists($pathQuestion)) {
			if($handle = opendir($pathQuestion)) {
				while(false !== ($file = readdir($handle))) {
					$pathExtension = $pathQuestion.$file."/";
					if(!is_dir($pathExtension) || in_array($file, $blackList)) {
						continue;
					}
					
					$this->typeAdd("question", $file, $pathExtension);
				}

				closedir($handle);
			}
		}

		$pathGeneral = pathData::SLIDE_TYPE_GENERAL;
		if(file_exists($pathGeneral)) {
			if($handle = opendir($pathGeneral)) {
				while(false !== ($file = readdir($handle))) {
					$pathExtension = $pathGeneral.$file."/";
					if(!is_dir($pathExtension) || in_array($file, $blackList)) {
						continue;
					}
					
					$this->typeAdd("general", $file, $pathExtension);
				}

				closedir($handle);
			}
		}

		ksort($this->slideTypes["question"]);
		ksort($this->slideTypes["general"]);
	}

	private function typeAdd($group, $name, $path) {
		$pathConfig = $path."settings.ini";
		$pathFormPHP = $path."form/form.php";
		$pathFormView = $path."views/form.tpl";
		$pathFormScript = $path."public/script/form.js";
		$pathPreviewView = $path."views/preview.tpl";
		$pathPlayView = $path."views/play.tpl";
		$pathShowView = $path."views/show.tpl";
		$pathShowForm = $path."form/result.php";
		$pathResultView = $path."views/result.tpl";
		$pathResultPHP = $path."model/result.php";
		$pathImageCache = pathData::CACHE_SLIDE_TYPE_IMAGE;

		if(!file_exists($pathConfig)) {
			return false;
		}

		$settings = config::get($pathConfig);
		if(!isset($settings["active"]) || !(bool)$settings["active"]) {
			return false;
		}
		
		$type = array(
			"id" => encryption::encode($group.".".$name),
			"disabled" => $settings["disabled"] == "1" ? true : false,
			"name" => $settings["name"],
			"image" => "",
			"svg" => $settings["svg"],
			"form" => array(
				"php" => "",
				"view" => "",
				"script" => ""
			),
			"preview" => array(
				"view" => ""
			),
			"play" => array(
				"view" => ""
			),
			"show" => array(
				"view" => "", 
				"form" => ""
			),
			"result" => array(
				"php" => "",
				"view" => "",
					"model" => isset($settings["resultModel"]) ? $settings["resultModel"] : ""
			)
		);

		if(!empty($settings["image"]) && file_exists($path.$settings["image"])) {
			$pathImage = $path.$settings["image"];
			$imageName = "type_".sha1($name.file_get_contents($pathImage)).".".pathinfo($settings["image"], PATHINFO_EXTENSION);
			
			if(!file_exists($pathImageCache.$imageName)) {
				copy($pathImage, $pathImageCache.$imageName);
			}

			$type["image"] = pathData::image($pathImageCache, $imageName);
		}

		if(file_exists($pathFormPHP)) {
			$type["form"]["php"] = $pathFormPHP;
		}

		if(file_exists($pathFormView)) {
			$type["form"]["view"] = $pathFormView;
		}

		if(file_exists($pathFormScript)) {
			$type["form"]["script"] = $pathFormScript;
		}

		if(file_exists($pathPreviewView)) {
			$type["preview"]["view"] = $pathPreviewView;
		}

		if(file_exists($pathPlayView)) {
			$type["play"]["view"] = $pathPlayView;
		}

		if(file_exists($pathShowView)) {
			$type["show"]["view"] = $pathShowView;
		}

		if(file_exists($pathShowForm)) {
			$type["show"]["form"] = $pathShowForm;
		}

		if(file_exists($pathResultPHP)) {
			$type["result"]["php"] = $pathResultPHP;
		}

		if(file_exists($pathResultView)) {
			$type["result"]["view"] = $pathResultView;
		}
		
		$this->slideTypes[$group][$name] = $type;
		return true;
	}

	private function typeGet($type) {
		list($group, $type) = explode(".", $type);
		
		if(!isset($this->slideTypes[$group]) || !isset($this->slideTypes[$group][$type])) {
			return false;
		}

		return $this->slideTypes[$group][$type];
	}

	public function typeList() {
		return $this->slideTypes;
	}

	/******************
	 KARTY PREZENTACJI 
	******************/
	public function getSlideOwner() {
		$object = array();
	}

	public function slideCreate() {
		global $__database;

		$last = $this->slideGetLast();
		if($last !== false && $last["type"] == "empty") {
			return false;
		}

		$slideId = $__database->newGuid();
		$order = $last["order"] + 1;

		try {
			$query = $__database->prepare("INSERT INTO `boards` (`board_id`, `presentation_id`, `type`, `order`) VALUES (:id, :presentation_id, 'empty', :order)");
			$query->bindParam(":id", $slideId);
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->bindParam(":order", $order);
			$query->execute();

			return $slideId;
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	public function slideCopy() {
		global $__database;

		$slideId = $__database->newGuid();

		try {
			$__database->beginTransaction();

			$board = array();

			$query = $__database->prepare("SELECT `board_id`, `type`, `name`, `preview`, `image`, `video`, `config`, `reactions`, `order` FROM `boards` WHERE `presentation_id`=:presentation_id AND `board_id`=:board_id");
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->bindParam(":board_id", $this->slideId);
			$query->execute();
			if($record = $query->fetch()) {
				$board = $record;
				$boardsToUpdate = array();

				$query = $__database->prepare("SELECT `board_id`, `order` FROM `boards` WHERE `presentation_id`=:presentation_id AND `order`>:order");
				$query->bindParam(":presentation_id", $this->presentationId);
				$query->bindParam(":order", $board["order"]);
				$query->execute();
				while($record = $query->fetch()) {
					$boardsToUpdate[] = $record;
				}

				if(count($boardsToUpdate) > 0) {
					foreach($boardsToUpdate as $item) {
						$order = $item["order"] + 1;
						
						$query = $__database->prepare("UPDATE `boards` SET `order`=:order WHERE `presentation_id`=:presentation_id AND `board_id`=:board_id");
						$query->bindParam(":presentation_id", $this->presentationId);
						$query->bindParam(":board_id", $item["board_id"]);
						$query->bindParam(":order", $order);
						$query->execute();
					}
				}

				$order = $board["order"] + 1;

				$query = $__database->prepare("INSERT INTO `boards` (`board_id`, `presentation_id`, `type`, `name`, `preview`, `image`, `video`, `config`, `reactions`, `order`) VALUES (:board_id, :presentation_id, :type, :name, :preview, :image, :video, :config, :reactions, :order)");
				$query->bindParam(":board_id", $slideId);
				$query->bindParam(":presentation_id", $this->presentationId);
				$query->bindParam(":type", $board["type"]);
				$query->bindParam(":name", $board["name"]);
				$query->bindParam(":preview", $board["preview"]);
				$query->bindParam(":image", $board["image"]);
				$query->bindParam(":video", $board["video"]);
				$query->bindParam(":config", $board["config"]);
				$query->bindParam(":reactions", $board["reactions"]);
				$query->bindParam(":order", $order);
				$query->execute();

				$query = $__database->prepare("INSERT INTO `boards_options` (`option_id`, `board_id`, `text`, `correct`, `image`, `order`) SELECT UUID() AS `option_id`, '{$slideId}' AS `board_id`, `text`, `correct`, `image`, `order` FROM `boards_options` WHERE `board_id`=:board_id");
				$query->bindParam(":board_id", $this->slideId);
				$query->execute();
				//$query->__debug();
			}

			$__database->commit();

			return array("id" => $slideId, "type" => ($board["type"] != "empty" ? encryption::encode($board["type"]) : $board["type"]), "preview" => pathData::image(pathData::SLIDE_PREVIEW, $board["preview"], "public/images/empty/slidePreview.png"));
		} catch(PDOException $error) {
			$__database->rollBack();

			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	public function slideSave() {
		$slideType = $this->typesGet($this->slideType);
		if($slideType == false) {
			return false;
		}

		if(isset($slideType["form"]["php"]) && !empty($slideType["form"]["php"])) {
			require $slideType["form"]["php"];
			
			$form = new \form\slide_extend_form();
			if($form->valid()) {
				$form->save($this->slideId);

				return true;
			}
		}

		return false;
	}

	public function slideRemove() {
		global $__database;

		try {
			$query = $__database->prepare("DELETE FROM `boards` WHERE `board_id`=:id AND `presentation_id`=:presentation_id");
			$query->bindParam(":id", $this->slideId);
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->execute();

			return true;
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	public function slideType($type = "") {
		global $__database;

		$slideContent = $this->slideGet();

		$startType = $slideContent["type"];
		
		if(!empty($type)) {
			$this->slideType = encryption::decode($type);
		}

		$slideType = $this->typesGet($this->slideType);
		if($slideType == false) {
			return false;
		}

		$object = array("type" => $this->slideType, "form" => "", "script" => "", "preview" => "", "lock" => ($slideContent["users"] > 0 ? true : false));
		
		$form = null;
		if(isset($slideType["form"]["php"]) && !empty($slideType["form"]["php"])) {
			require_once $slideType["form"]["php"];
			
			$form = new \form\slide_extend_form($slideContent);

			if($startType != $this->slideType) {
				$startData = array();
				if($form != null && method_exists($form, "startData")) {
					$startData = $form->startData($slideContent);
				}

				$options = isset($startData["options"]) && $startData["options"] == false ? false : true;

				if(isset($startData["config"])) {
					$slideContent["config"] = $startData["config"];
				}

				if($options) {
					$slideContent["options"] = array();
				}

				$form->setData($slideContent);
			}
		}

		if(isset($slideType["form"]["view"]) && !empty($slideType["form"]["view"])) {
			$object["form"] = $this->slideRenderTemplate($slideType["form"]["view"], $form);
		}

		if(isset($slideType["form"]["script"]) && !empty($slideType["form"]["script"])) {
			$object["script"] = file_get_contents($slideType["form"]["script"]);
		}

		if($startType != $this->slideType) {
			$startData = array();
			if($form != null && method_exists($form, "startData")) {
				$startData = $form->startData($slideContent);
			}

			$config = json_encode(isset($startData["config"]) ? $startData["config"] : array());
			$options = isset($startData["options"]) && $startData["options"] == false ? false : true;

			$query = $__database->prepare("UPDATE `boards` SET `date_updated`=NOW(), `type`=:type, `config`=:config WHERE `board_id`=:id AND `presentation_id`=:presentation_id");
			$query->bindParam(":type", $this->slideType);
			$query->bindParam(":config", $config);
			$query->bindParam(":id", $this->slideId);
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->execute();

			if($options) {
				$query = $__database->prepare("DELETE FROM `boards_options` WHERE `board_id`=:board_id");
				$query->bindParam(":board_id", $this->slideId);
				$query->execute();
			}
		}

		if(isset($slideType["preview"]["view"]) && !empty($slideType["preview"]["view"])) {
			if($startType != $this->slideType) {
				$this->loadSlideData();
				$slideContent = $this->slideGet();
			}

			$object["preview"] = $this->slideRenderTemplate($slideType["preview"]["view"], $slideContent);
		}

		return $object;
	}

	public function slidePreview() {
		$slideContent = $this->slideGet();

		$slideType = $this->typesGet($this->slideType);
		if($slideType == false) {
			return "";
		}

		if(isset($slideType["preview"]["view"]) && !empty($slideType["preview"]["view"])) {
			return $this->slideRenderTemplate($slideType["preview"]["view"], $slideContent);
		}

		return "";
	}

	public function slidePlay($update = false) {
		global $__template;

		$slideContent = $this->slideGet();
		
		$slideType = $this->typesGet($this->slideType);
		if($slideType == false) {
			return "";
		}

		if(isset($slideType["play"]["view"]) && !empty($slideType["play"]["view"])) {
			$__template->setVariable("slideUpdate", $update);
			return $this->slideRenderTemplate($slideType["play"]["view"], $slideContent);
		}

		return false;
	}

	public function slideShow() {
		$slideType = $this->typesGet($this->slideType);
		if($slideType == false || !isset($slideType["preview"]["view"]) || empty($slideType["preview"]["view"])) {
			return false;
		}

		return $this->slideRenderTemplate($slideType["preview"]["view"], $this->slideGet());
	}

	public function slideResults($slide, $slideType) {
		if($slideType == false || !isset($slideType["result"]["php"]) || empty($slideType["result"]["php"])) {
			return array();
		}
		
		$result = new slideResults($slide, $slideType);
		return $result->getResult();
	}

	public function slideQuizResults() {
		$slide = $this->slideGet();
		if($slide === false || $slide["type"] != "question.008_Quiz") {
			return false;
		}
		
		$slideType = $this->typesGet($slide["type"]);

		$return = array("chart" => "", "rank" => "");

		if(isset($slideType["play"]["view"]) && !empty($slideType["play"]["view"])) {
			$templateChart = str_replace("play.tpl", "playResultChart.tpl", $slideType["play"]["view"]);
			$templateRank = str_replace("play.tpl", "playResultRank.tpl", $slideType["play"]["view"]);

			$return["chart"] = $this->slideRenderTemplate($templateChart, $slide);
			$return["rank"] = $this->slideRenderTemplate($templateRank, $slide);
			$return["users"] = count($slide["results"]["list"]);
		}

		return $return;
	}

	public function slideQuiz($userId) {
		global $__database;

		$countQuiz = $this->countSlideQuiz();
		
		if($countQuiz < 1) {
			return false;
		}

		$slidesType = $this->listSlidesType();
		if(!isset($slidesType[$this->slideId])) {
			return false;
		}

		$slideType = $slidesType[$this->slideId];

		$replyTime = -1;
		$replyPoints = -1;
		$replyCorrect = false;

		$query = $__database->prepare("SELECT r.time, r.points, o.correct FROM `results` AS `r` LEFT JOIN `boards_options` AS `o` ON r.board_id=o.board_id AND r.reply=o.option_id WHERE r.board_id=:board_id AND r.user_id=:user_id");
		$query->bindParam(":board_id", $this->slideId);
		$query->bindParam(":user_id", $userId);
		$query->execute();
		if($record = $query->fetch()) {
			$replyTime = $record["time"];
			$replyPoints = $record["points"];
			$replyCorrect = $record["correct"];
		}

		return array("step" => $slideType["quizIndex"], "stepMax" => $countQuiz, "replyTime" => $replyTime, "replyPoints" => $replyPoints, "replyCorrect" => $replyCorrect);
	}

	public function listSlidesType() {
		global $__database;

		$list = array();
		$idx = 1;
		$quizIdx = 1;
		$quizPrevious = array();

		$query = $__database->prepare("SELECT `board_id`, `type` FROM `boards` WHERE `presentation_id`=:presentation_id ORDER BY `order` ASC");
		$query->bindParam(":presentation_id", $this->presentationId);
		$query->execute();
		while($row = $query->fetch()) {
			$quizIndex = 0;

			if($row["type"] == "question.008_Quiz") {
				$quizIndex = $quizIdx;
				$quizIdx++;
			}

			$list[$row["board_id"]] = array(
				"id" => $row["board_id"],
				"index" => $idx,
				"type" => $row["type"],
				"quizIndex" => $quizIndex,
				"quizPrevious" => $quizPrevious
			);

			if($row["type"] == "question.008_Quiz") {
				$quizPrevious[] = $row["board_id"];
			}

			$idx++;
		}

		return $list;
	}

	public function slideResultsRemove() {
		global $__database;

		try {
			$query = $__database->prepare("DELETE FROM `results` WHERE `presentation_id`=:presentation_id AND `board_id`=:board_id");
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->bindParam(":board_id", $this->slideId);
			$query->execute();

			$query = $__database->prepare("DELETE FROM `boards_reactions` WHERE `board_id`=:board_id");
			$query->bindParam(":board_id", $this->slideId);
			$query->execute();

			return true;
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return false;
	}

	private function slideRenderTemplate($tplPath, $model) {
		global $__template;

		$__template->setVariable("_MODEL", $model);
		return $__template->slide_render($tplPath);
	}

	public function slideImage($postImage) {
		global $__database;

		$unique = date("Ymd-His-u", strtotime($this->slideDateUpdated));
		$fileName = "{$this->presentationId}-{$this->slideId}-{$unique}.png";
		$filePath = pathData::SLIDE_PREVIEW;

		if(!empty($this->slidePreview) && $this->slidePreview == $fileName && file_exists($filePath.$fileName)) {
			return pathData::image($filePath, $fileName);
		}

		list($type, $image) = explode(",", $postImage);
		$imageContent = base64_decode($image);
		
		$imageInfo = getimagesizefromstring($imageContent);
		if($imageInfo["mime"] != "image/png") {
			return false;
		}
		
		if(file_put_contents($filePath.$fileName, $imageContent) !== false) {
			$image = new image($filePath.$fileName);
			$image->setBackground(255, 255, 255);
			if(!$image->save($filePath.$fileName)) {
				return false;
			}
			
			$query = $__database->prepare("UPDATE `boards` SET `preview`=:preview WHERE `board_id`=:id AND `presentation_id`=:presentation_id");
			$query->bindParam(":preview", $fileName);
			$query->bindParam(":id", $this->slideId);
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->execute();

			return pathData::image($filePath, $fileName);
		}

		return false;
	}

	public function slideGet($id = "") {
		global $__database;

		if(empty($id)) {
			$id = $this->slideId;
		}

		$object = array(
			"id" => $this->slideId,
			"board_id" => $this->slideId,
			"type" => $this->slideType,
			"name" => $this->slideName,
			"preview" => $this->slidePreview,
			"image" => $this->slideImage,
			"video" => $this->slideVideo,
			"config" => json_decode($this->slideConfig, true),
			"reactions" => $this->slideReactions,
			"reactionsResults" => $this->slideReactionsResults,
			"options" => $this->slideOptions,
			"results" => $this->slideResults,
			"users" => $this->slideUsers,
			"usersReactions" => $this->slideUsersReactions
			);
		
		return $object;
	}

	public function slideGetLast() {
		global $__database;

		$object = array();

		try {
			$query = $__database->prepare("SELECT `board_id`, `type`, `order` FROM `boards` WHERE `presentation_id`=:id ORDER BY `order` DESC");
			$query->bindParam(":id", $this->presentationId);
			$query->execute();
			if(!$record = $query->fetch()) {
				return false;
			}

			$object = $record;
		} catch(PDOException $error) {
			throw new databaseException($error->getMessage(), $error->getCode());
		}

		return $object;
	}

	public function slideGetStep($step) {
		global $__database;

		$object = array(
			"board_id" => "",
			"type" => "",
			"name" => "",
			"image" => "",
			"video" => "",
			"config" => array(),
			"options" => array()
			);

		try {
			$query = $__database->prepare("SELECT `board_id`, `type`, `name`, `image`, `video`, `config` FROM `boards` WHERE `presentation_id`=:id ORDER BY `order` ASC LIMIT {$step}, 1");
			$query->bindParam(":id", $this->presentationId);
			$query->execute();
			if($record = $query->fetch()) {
				$object["board_id"] = $record["board_id"];
				$object["name"] = $record["name"];
				$object["type"] = $record["type"];
				$object["image"] = $record["image"];
				$object["video"] = $record["video"];
				$object["config"] = json_decode($record["config"], true);
				
				$query = $__database->prepare("SELECT `option_id`, `text`, `image` FROM `boards_options` WHERE `board_id`=:id ORDER BY `order` ASC");
				$query->bindParam(":id", $object["board_id"]);
				$query->execute();
				while($record = $query->fetch()) {
					$object["options"][] = $record;
				}

				return $object;
			}
		} catch(PDOException $error) {

		}

		return false;
	}

	public function slideGetId($slideId, $replyUserId = null) {
		global $__database;

		$object = array(
			"board_id" => "",
			"type" => "",
			"name" => "",
			"image" => "",
			"video" => "",
			"config" => array(),
			"options" => array(),
			"optionsCorrect" => 0,
			"reactions" => array(),
			"nextSlide" => false,
			"userReply" => false,
			"userReaction" => false
			);

		try {
			$query = $__database->prepare("SELECT `board_id`, `type`, `name`, `image`, `video`, `config`, `reactions`, `order` FROM `boards` WHERE `board_id`=:board_id AND `presentation_id`=:presentation_id");
			$query->bindParam(":board_id", $slideId);
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->execute();
			if($record = $query->fetch()) {
				$object["board_id"] = $record["board_id"];
				$object["name"] = $record["name"];
				$object["type"] = $record["type"];
				$object["image"] = $record["image"];
				$object["video"] = $record["video"];
				$object["config"] = json_decode($record["config"], true);
				
				$reactions = array();
				$reactionsArray = json_decode($record["reactions"], true);

				if(is_array($reactionsArray) && count($reactionsArray) > 0) {
					foreach($reactionsArray as $reaction) {
						$reactions[$reaction] = $reaction;
					}
				}

				$object["reactions"] = $reactions;
				$order = $record["order"];
				$countCorrect = 0;

				$query = $__database->prepare("SELECT `option_id`, `text`, `correct`, `image` FROM `boards_options` WHERE `board_id`=:id ORDER BY `order` ASC");
				$query->bindParam(":id", $object["board_id"]);
				$query->execute();
				while($record = $query->fetch()) {
					if($record["correct"] == "1") {
						$countCorrect++;
					}

					$object["options"][] = $record;
				}

				$object["optionsCorrect"] = $countCorrect;

				$query = $__database->prepare("SELECT `board_id` FROM `boards` WHERE `presentation_id`=:presentation_id AND `order`>:order ORDER BY `order` ASC");
				$query->bindParam(":presentation_id", $this->presentationId);
				$query->bindParam(":order", $order);
				$query->execute();
				if($record = $query->fetch()) {
					$object["nextSlide"] = $record["board_id"];
				}

				if($replyUserId != null) {
					$query = $__database->prepare("SELECT COUNT(`result_id`) AS `numRecords` FROM `results` WHERE `board_id`=:board_id AND `user_id`=:user_id");
					$query->bindParam(":board_id", $slideId);
					$query->bindParam(":user_id", $replyUserId);
					$query->execute();
					if($record = $query->fetch()) {
						if($record["numRecords"] > 0)  {
							$object["userReply"] = true;
						}
					}

					$query = $__database->prepare("SELECT COUNT(`reaction_id`) AS `numRecords` FROM `boards_reactions` WHERE `board_id`=:board_id AND `user_id`=:user_id");
					$query->bindParam(":board_id", $slideId);
					$query->bindParam(":user_id", $replyUserId);
					$query->execute();
					if($record = $query->fetch()) {
						if($record["numRecords"] > 0)  {
							$object["userReaction"] = true;
						}
					}
				}

				return $object;
			}
		} catch(PDOException $error) {

		}

		return false;
	}

	public function slideSort($sort) {
		global $__database;

		$sortIndex = 1;
		
		try {
			$__database->beginTransaction();

			foreach($sort as $item) {
				$query = $__database->prepare("UPDATE `boards` SET `order`=:order WHERE `board_id`=:id AND `presentation_id`=:presentation_id");
				$query->bindParam(":order", $sortIndex);
				$query->bindParam(":id", $item);
				$query->bindParam(":presentation_id", $this->presentationId);
				$query->execute();
				
				$sortIndex++;
			}

			$query = $__database->prepare("UPDATE `boards` SET `order`=:order WHERE `presentation_id`=:presentation_id AND `type`='empty'");
			$query->bindParam(":order", $sortIndex);
			$query->bindParam(":presentation_id", $this->presentationId);
			$query->execute();
			
			$__database->commit();

			return true;
		} catch(PDOException $error) {
			$__database->rollBack();
		}

		return false;
	}
}
?>