<?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
 */

abstract class WV_Service_Property {
	abstract protected function onNew(WV_Property $property);
	abstract protected function onDeleted(WV_Property $property);
	abstract protected function onChangedDatatype(WV_Property $oldVersion, WV_Property $newVersion);
	abstract protected function onChangedParameters(WV_Property $oldVersion, WV_Property $newVersion);
	abstract protected function onChanged(WV_Property $oldVersion, WV_Property $newVersion);

	public function loadFromConfig($key, array $oldData = null) {
		$data = sly_Core::config()->get($key, null);

		if ($data === null) {
			return array();
		}

		return $this->loadFromArray($data, $oldData);
	}

	public function loadFromArray(array $data, array $oldData = null) {
		$oldData    = $oldData === null ? $data : $oldData;
		$properties = array();

		// create properties if nothing has changed, else compare the lists

		if ($oldData === $data) {
			foreach ($oldData as $name => $property) {
				$properties[$name] = $this->createProperty($name, $property);
			}
		}
		else {
			$properties = $this->compareStates($oldData, $data);
		}

		return $properties;
	}

	protected function compareStates($oldState, $newState) {
		$properties = array();

		// iterate over the new list to use its order of properties

		foreach ($newState as $name => $property) {
			$newObj = $this->createProperty($name, $property);

			// has a new property been added?
			if (!isset($oldState[$name])) {
				$this->onNew($newObj);
			}

			// has a property been changed?
			elseif ($oldState[$name] !== $property) {
				$oldObj = $this->createProperty($name, $oldState[$name]);

				if ($oldObj->getDatatype() !== $newObj->getDatatype()) {
					$this->onChangedDatatype($oldObj, $newObj);
				}
				elseif ($oldObj->getParams() !== $newObj->getParams()) {
					// The datatype can inspect the parameters much better than we
					// ever could, so let's ask it whether the parameters are really
					// different. This gets important when YAML converts a float like
					// 12.00 to an int (12).

					// returns true if the params are compatible, else an array with actions to take
					$status = $oldObj->datatypeCall('checkParamCompatibility', array($oldObj->getParams(), $newObj->getParams()));

					if ($status !== true) {
						$this->onChangedParameters($oldObj, $newObj);
					}
				}
				else {
					$this->onChanged($oldObj, $newObj);
				}
			}

			$properties[$name] = $newObj;
		}

		// check for deleted properties

		foreach ($oldState as $name => $property) {
			if (!isset($newState[$name])) {
				$obj = $this->createProperty($name, $property);
				$this->onDeleted($obj);
			}
		}

		return $properties;
	}

	protected function createProperty($name, array $property) {
		return new WV_Property($name, $property);
	}
}
