<?php #lib/20_routes.php

namespace Links;

/*class Element {
	protected $parent;

	protected $left;
	protected $right;
	protected $fields = [];
	protected $keys = [];
	
	public function __construct($parent, $data) {
		$this->parent = $parent;
		$definitions = $parent->Definitions();
		
		$classLeft = "\\Resources\\".$definitions["left"]["resourceClass"];
		$this->left = $classLeft::;
		foreach ($definitions["ids"] as $id => $className) {
			$class = "\\Resources\\".$className;
			$this->ids[$id] = isset($data[$id]) ? $class::Get()->RefByData($data[$id]) : null;
		}
		$this->fields = array_combine(array_keys($definitions["fields"]), array_column($definitions["fields"], "value"));
		$this->keys = array_combine(array_keys($definitions["keys"]), array_column($definitions["keys"], "reference"));
		
		$this->Patch($data);
	}
	
	public function ID($key) {return $this->ids[$key];}

	public function Patch($data) {
		// Overwrite field values if data exists
		foreach ($this->fields as $field => &$value) {
			if (isset($data[$field])) {
				$value = $data[$field];
		} }
		unset($value); // If removed, $value would still point to last element of fields
		
		$definitions = $this->parent->Definitions();
		foreach ($this->keys as $key => &$reference) {
			if (isset($data[$key])) {
				$class = "\\Resources\\".$definitions["keys"][$key]["resourceClass"];
				$reference = is_array($data[$key]) ? $class::Get()->RefByData($data[$key]) : $class::Get()->RefById($data[$key]);
		} }
		unset($reference); // If removed, $reference would still point to last element of keys
	}
	
	public function Store() {
		$ids = [];
		$fields = [];
		$values = [];
		$types = "";
		
		$definitions = $this->parent->Definitions();
		if (!empty($definitions["fields"]) || !empty($definitions["keys"])) {
		
			foreach ($definitions["fields"] as $field => $def) {
				$fields[] = $field." = ?";
				$values[] = $this->fields[$field];
				$types.= $def["type"];
			}
			foreach ($definitions["keys"] as $key => $def) {
				$fields[] = $key;
				$values[] = is_null($this->keys[$key]) ? null : $this->keys[$key]->ID();
				$types.= $def["type"];
			}
			foreach ($this->ids as $id => $reference) {
				$ids[] = $id." = ?";
				$values[] = $reference->ID();
				$types.= "s";
			}
			
			$qry = "UPDATE ".$this->parent->Table()." SET ".implode(", ", $fields)." WHERE ".implode(" AND ", $ids);
	
			if ($stmt = \DB::Get()->prepare($qry)) {
			  $stmt->bind_param($types, ...$values);
	      if ($stmt->execute()) {
	      	$this->parent->UpdateChecksum();
	        return true;
	    } }
	    \Response::Get()->DbError();
	    return false;
		}
		return true;
	}
	
	public function Json($depth = null) {
		if (is_null($depth)) {
			$depth = \Request::DetailDepth();
		}
		$ret = [];
		foreach ($this->ids as $id => $reference) {
			$ret[$key] = is_null($reference) ? null : ($depth>0 ? $reference->Json($depth-1) : $reference->ID());
		}
		foreach ($this->fields as $field => $value) {
			$ret[$field] = $value;
		}
		foreach ($this->keys as $key => $reference) {
			$ret[$key] = is_null($reference) ? null : ($depth>0 ? $reference->Json($depth-1) : $reference->ID());
		}
		return $ret;
	}
}*/

abstract class Link {
	protected static $instances = [];
	protected $leftIndex = [];
	protected $rightIndex = [];
	
	public static function Get() {
		$class = get_called_class();
		if (!isset($instances[$class])) {
			self::$instances[$class] = new $class();
		}
		return self::$instances[$class];
	}
	
	private function __construct() {}
	
	public function Table() {return $this->names["table"];}
	public function Short() {return $this->names["short"];}
	public function TableWithShort() {return $this->names["table"]." ".$this->names["short"];}
	
	public function RefList($id, $target, $dataOrIds = null) {
		if ($target==$this->definitions["left"]["ident"]) {
			// We come from right
			if (!isset($this->rightIndex[$id])) {
				$this->rightIndex[$id] = is_array($dataOrIds) ? $this->connect("left", $dataOrIds) : $this->load("left", $id);
			}
			return $this->rightIndex[$id];
		} else {
			// We come from left
			if (!isset($this->leftIndex[$id])) {
				$this->rightIndex[$id] = is_array($dataOrIds) ? $this->connect("right", $dataOrIds) : $this->load("right", $id);
			}
			return $this->leftIndex[$id];
		}
	}
	
	public function Unset($id, $target, $targetId) {
		$idIsRight = $target==$this->definitions["left"]["ident"];
		
		$qry = "DELETE FROM ".$this->Table()." WHERE ".$this->definitions["left"]["ident"]." = ? AND ".$this->definitions["right"]["ident"]." = ?";
		if ($stmt = \DB::Get()->prepare($qry)) {
			if ($idIsRight) {
      	$stmt->bind_param("ss", $targetId, $id);
			} else {
				$stmt->bind_param("ss", $id, $targetId);
			}
      if ($stmt->execute()) {
        if (1==$stmt->affected_rows) {
          if ($idIsRight) {
		      	$this->indexRight[$id] = $this->load("left", $id);
					} else {
						$this->indexLeft[$id] = $this->load("right", $id);
					}
          return true;
        } else if (0==$stmt->affected_rows) {
        	\Response::Get()->Message("Fehler: Es wurde nichts entfernt!");
        } else {
        	\Response::Get()->Message("Fehler: Es wurden mehrere Einträge entfernt!");
    } } }
    \Response::Get()->DbError();
    return false;
	}
	
	public function Set($id, $target, $targetId) {
		$idIsRight = $target==$this->definitions["left"]["ident"];
		
		$qry = "INSERT INTO ".$this->Table()." (".$this->definitions["left"]["ident"].",".$this->definitions["right"]["ident"].") VALUES (?, ?)";
		if ($stmt = \DB::Get()->prepare($qry)) {
			if ($idIsRight) {
      	$stmt->bind_param("ss", $targetId, $id);
			} else {
				$stmt->bind_param("ss", $id, $targetId);
			}
      if ($stmt->execute()) {
      	if ($idIsRight) {
	      	$this->indexRight[$id] = $this->load("left", $id);
				} else {
					$this->indexLeft[$id] = $this->load("right", $id);
				}
        return true;
    } }
    \Response::Get()->DbError();
    return false;
  }
	
	protected function connect($target, $dataOrIds) {
		$targetClass = "\\Resources\\".$this->definitions[$target]["resourceClass"];
		
		$ret = [];
		foreach ($dataOrIds as $dataOrId) {
			if (is_array($dataOrId)) {
				$ret[] = $targetClass::Get()->RefByData($dataOrId);
			} else {
				$ret[] = $targetClass::Get()->RefById($dataOrId);
			}
		}
		return $ret;
	}
	
	protected function load($target, $id) {
		$source = "left" == $target ? "right" : "left";
		$sourceField = $this->definitions[$source]["ident"];
		$targetField = $this->definitions[$target]["ident"];
		$targetClass = "\\Resources\\".$this->definitions[$target]["resourceClass"];
		
		$qry = "SELECT ".$targetField." FROM ".$this->Table()." WHERE ".$sourceField." = ?";
		
		if ($stmt = \DB::Get()->prepare($qry)) {
		  if (!is_null($id)) {
				$stmt->bind_param("s", $id);
			}
      if ($stmt->execute()) {
      	$res = $stmt->get_result();
      	$ret = [];
      	while ($row = $res->fetch_assoc()) {
	      	$ret[] = $targetClass::Get()->RefById($row[$targetField]);
    		}
    		return $ret;
    } }
    \Response::Get()->DbError();
    return [];
	}
}
