<?php
/*
 * Copyright (c) 2013, webvariants GbR, http://www.webvariants.de
 *
 * This file is released under the terms of the MIT license. You can find the
 * complete text in the attached LICENSE file or online at:
 *
 * http://www.opensource.org/licenses/mit-license.php
 */

/**
 * Basisklasse für alle Datentypen
 *
 * Diese Basisklasse definiert die Schnittstelle aller Datentypen, die das AddOn
 * benutzen kann. Damit ist sichergestellt, dass sich jeder Datentyp unabhängig
 * vom Controller in einen max. 65k langen String serialisieren kann und die
 * volle Kontrolle über die Gestaltung der eigenen Formularfelder erhält.
 *
 * Begriffe:
 *
 * - Parameter: Paramater sind Angaben, die die Darstellung oder die Constraints
 *    eines Datentyps festlegen, z.B. minimale und maximale Länge, Anzeige von X
 *    Elementen bei einer select-Box oder ähnliches. Ein Datentyp kann die
 *    Parameter völlig frei definieren, vor dem Speichern in einer Metainformation
 *    werden die Parameter mittels serialize() behandelt.
 *
 * - Standardwert: Einer oder mehrere Werte, die getrennt von den Parametern in
 *    der Datenbank abgelegt werden. Bei einem String bietet sich ein einfacher
 *    String an, bei einer Auswahl kann dies aber auch ein Array sein. Auch das
 *    kann ein Datentyp für sich selber regeln. Der Standardwert wird ebenfalls
 *    mit serialize() behandelt.
 *
 * @ingroup objects
 */
abstract class _WV_Datatype_Base {
	/**
	 * Typ des deserialisierten Werts ermitteln
	 *
	 * Gibt an, ob ein Datentyp als Ergebnis eine assoziative Liste zurückgibt.
	 * Für alle Typen, die gar keine Liste zurückgegeben, sollte hier false
	 * zurückgegeben werden. Anhand dieser Angabe kann der MetaProvider doppelte
	 * Objekte in zwei Metadaten entweder anhand der Schlüssel (assoziativ) oder
	 * anhand der Werte (normal) finden und entfernen.
	 *
	 * @return bool  true, wenn keine oder eine numerische Liste zurückgegeben wird, sonst false
	 */
	public function usesAssociativeResults() {
		return false;
	}

	/**
	 * Werte aus Parameter extrahieren
	 *
	 * Wenn ein Datentyp in seinen Parametern eine Reihe von möglichen Werten für
	 * die Metadaten definiert (z.B. durch eine Werteliste oder eine enthaltene
	 * SQL-Abfrage), so sollte ein Datentyp beim Aufruf dieser Methode eine Liste
	 * mit allen möglichen Werten zurückgeben. Datentypen, die keine Listen
	 * enthalten (und somit keine erlaubten Werte vorgeben) sollten hier eine
	 * leere Liste zurückgeben (Standard-Implementierung).
	 *
	 * @param  array $params  die Datentyp-Parameter
	 * @return array          Liste von erlaubten (möglichen) Werten
	 */
	public function extractValuesFromParams(array $params) {
		return array();
	}

	/**
	 * Deserialisieren (Nachbearbeiten) von Metadaten
	 *
	 * Diese Methode übernimmt den Wert eines Metadatums und extrahiert aus dem
	 * Wert die ggf. noch serialisierten Einzelwerte. Datentypen, die einen
	 * einfachen Wert enthalten, können diesen ungefiltert zurückgeben.
	 * Listentypen sollen hier die serialisierte Liste entsprechend ihres eigenen
	 * Formats wieder in ein Array konvertieren und zurückgeben.
	 *
	 * Wird für $value ein null übergeben, so gibt diese Methode für die
	 * Parameter des Datentyps nicht die zurück, die der Artikel angenommen hat,
	 * sondern einfach alle.
	 *
	 * In der Standard-Implementierung wird der Wert direkt zurückgegeben.
	 *
	 * @param  string $value   der (noch serialisierte) Wert eines Metadatums aus der Datenbank
	 * @param  array  $params  die Datentyp-Parameter
	 * @return mixed           eine intuitive Entsprechung der Daten für den Nutzer des MetaProviders
	 */
	public function deserializeValue($value, array $params) {
		return $value;
	}

	/**
	 * Serialisieren von Metadaten
	 *
	 * Diese Methode übernimmt den Wert eines Metadatums, wie ihn ein Programmierer verwenden
	 * würde und serialisiert ihn, damit er in der Datenbank gespeichert werden kann. Innerhalb
	 * von MetaInfoEx wird diese Methode nicht genutzt, allerdings wird sie bei varisale
	 * zum Speichern von Werten (mittels ArrayAccess) genutzt.
	 *
	 * In der Standard-Implementierung wird der Wert direkt zurückgegeben.
	 *
	 * @since  3.1.2
	 * @param  mixed  $value   der intuitive Wert eines Metadatums (z.B. ein Array, ein Objekt, ...)
	 * @param  array  $params  die Datentyp-Parameter
	 * @return string          der serialisierte Wert, der in die Datenbank gelegt werden kann
	 */
	public function serializeValue($value, array $params) {
		return $value;
	}

	/**
	 * Inkompatible Datensätze verarbeiten
	 *
	 * Diese Methode löscht oder korrigiert durch das Bearbeiten des Datentyps
	 * inkompatibel gewordene Datensätze. Dabei werden die Werte der Objekte
	 * auf die neue Maximallänge gekürzt.
	 *
	 * @param array $oldParameters  die alten Parameter
	 * @param array $newParameters  die neuen Parameter
	 */
	public function getIncompatibilityUpdateStatement(array $oldParameters, array $newParameters) {
		return array();
	}

	/**
	 * Änderung am Datentyp
	 *
	 * Diese Methode wird aufgerufen, wenn eine Metainfo im Backend bearbeitet
	 * wurde. Ihre Aufgabe ist es, dem Controller mitzuteilen, ob die
	 * vorgenommenen Änderungen an den Parametern dazu führen, dass die alten
	 * Metadaten unbrauchbar wurden.
	 *
	 * Die Methode wird nur aufgerufen, wenn sich die Parameter geändert haben.
	 *
	 * Bspws. werden Metadaten unbrauchbar, wenn eine Liste von normal auf
	 * SQL-basiert umgeschaltet wurde.
	 *
	 * In dieser Implementierung gibt die Methode immer true zurück und
	 * ignoriert die formalen Parameter.
	 *
	 * @param  array $oldParameters  die alten Parameter
	 * @param  array $newParameters  die neuen Parameter
	 * @return bool                  true, wenn die Metadaten erhalten bleiben sollen, false, wenn sie gelöscht werden sollen
	 */
	public function checkParamCompatibility(array $oldParameters, array $newParameters) {
		return true;
	}

	/**
	 * Filterung von Daten
	 *
	 * Diese Methode dient dazu, bei einer Suchenach Metadaten über die API nach
	 * bestimmten Werten zu filtern. Ein Datentyp bekommt dazu von der API einen
	 * Suchwert und den "Live"-Wertaus der Datenbank und muss der API sagen, ob
	 * der Suchwert entsprechend den Filterparametern in dem Datenbankwert
	 * enthalten ist.
	 *
	 * Die möglichen Filterparameter sind in der Klasse WV2 definiert. Ein
	 * Datentyp muss nicht alle möglichen Filterparameter unterstützen. In seiner
	 * Dokumentation sollte er aber erwähnen, welche Parameter er unterstützt.
	 *
	 * Die Standard-Implementierung prüft auf einfache Gleichheit der Werte.
	 *
	 * @param  string $valueToSearch      der gesuchte Wert ("needle")
	 * @param  string $valueFromDatabase  der Wert aus der Datenbank, in dem gesucht wird ("haystack")
	 * @param  array  $params             die Parameter des Datentyps
	 * @param  int    $filterParams       die Suchparameter
	 * @return bool                       true, wenn der Suchwert enthalten ist, sonst false
	 */
	public function isValueContained($valueToSearch, $valueFromDatabase, array $params, $filterParams) {
		return $valueToSearch == $valueFromDatabase;
	}

	public function serializeForm(WV_IProperty $property) {
		$multi = $property->isMultilingual();
		$list  = sly_Form_Helper::parseFormValue('property_'.$property->getID().'_value', '', $multi);
		$list  = $multi ? $list : array($list);

		// call datatype specific filter function
		$list = $property->datatypeCall('serializeFormFilter', array($list, $property));

		return $multi ? $list : reset($list);
	}

	public function isValueEmpty($value, array $params) {
		return $value === null || $value === '' || (is_array($value) && count($value) === 0);
	}

	/**
	 * Deserialisiert die Parameter
	 *
	 * @param  string $params  die serialisierten Parameter
	 * @return array           ein assoziatives Array mit allen Eigenschaften (min, max, css, width, ...)
	 */
	protected function deserializeParams(array $params, $prefix = '') {
		$params = array_merge($this->getDefaultParams(), $params);

		if (strlen($prefix) === 0) {
			return $params;
		}

		$result = array();

		foreach ($params as $key => $value) {
			$newKey = $prefix.ucfirst($key);
			$result[$newKey] = $value;
		}

		return $result;
	}

	protected function decodeFormValue(WV_IProperty $property, $value) {
		if ($value === false) {
			$default = $this->getDefaultValue($property);
			$value   = $property->isMultilingual() ? array($default) : $default;
		}
		elseif (!$property->isMultilingual()) {
			$value = reset($value);
		}

		return $value;
	}

	public function getDefaultValue(WV_IProperty $property) {
		return $this->serializeValue($property->getDefault(), $property->getParams());
	}

	abstract protected function getDefaultParams();
}
