#! /usr/bin/env python3
# -*- coding: utf-8 -*-

#	Copyright 2011-2013, Marten de Vries
#	Copyright 2011, Milan Boers
#
#	This file is part of OpenTeacher.
#
#	OpenTeacher is free software: you can redistribute it and/or modify
#	it under the terms of the GNU General Public License as published by
#	the Free Software Foundation, either version 3 of the License, or
#	(at your option) any later version.
#
#	OpenTeacher is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#	GNU General Public License for more details.
#
#	You should have received a copy of the GNU General Public License
#	along with OpenTeacher.  If not, see <http://www.gnu.org/licenses/>.

class SettingDict(dict):
	def __init__(self, value, executeCallback, addedNow, *args, **kwargs):
		super().__init__(value, *args, **kwargs)

		self._executeCallback = executeCallback
		self._addedNow = addedNow

	def __setitem__(self, name, value):
		super().__setitem__(name, value)
		if name == "value" and "callback" in self:
			self._executeCallback(self["callback"])

class SettingsModule:
	def __init__(self, moduleManager, *args, **kwargs):
		super().__init__(*args, **kwargs)
		self._mm = moduleManager

		self.type = "settings"
		self.requires = (
			self._mm.mods(type="dataStore"),
		)

	def initialize(self):
		"""Connects to data store, should be called before doing
		   anything else but only once in each program run. Normally,
		   that's handled by the execute module.

		"""
		self._modules = set(self._mm.mods(type="modules")).pop()
		store = set(self._mm.mods(type="dataStore")).pop().store

		try:
			self._settings = store["org.openteacher.settings.settings"]
		except KeyError:
			self._settings = store["org.openteacher.settings.settings"] = {}

		#replace the dicts by SettingDicts
		for key, value in self._settings.items():
			#False because it was added in a previous session
			self._settings[key] = SettingDict(value, self._executeCallback, addedNow=False)

		self.active = True

	def registerSetting(self, internal_name, **setting):
		"""Adds a setting. internal_name should be unique and describe
		   what the setting contains, we **strongly recommmend** to use the
		   'reverse domain' naming	strategy because of the first property.
		   (E.g. ``com.example.moduleName.settingName``).

		   The other arguments, describing the setting, should be:

		   * name,
		   * type="short_text",
		    * boolean
		    * short_text
		    * long_text
		    * number
		    * password
		    * option
		    * multiOption
		    * language
		    * profile
		    ... are available.
		   * defaultValue
		   * minValue=None
		   * maxValue=None
		   * category=None
		   * subcategory=None
		   * advanced=False
		   * callback=None

		   The callback option should have this format::

		    {
		        "args": ("active",),
		        "kwargs": {"type": "callback"},
		        "method": "callbackMethod",
		    }

		   Where args and kwargs are the same as in the following:
		   ``self._mm.mods(*args, **kwargs)``

		   The following argument should be included when type="option" or type="multiOption":

		   * options=[]
		    * options should have this format: ``("label", data)``

		   This method returns a setting dict with the same properties as
		   described above, with the difference that defaultValue is
		   missing and that the 'value' key is added containing the actual
		   current value of the setting. You're free to modify the object,
		   as long as its values are valid.

		   When a setting argument isn't given (e.g. category), then it
		   also isn't in the setting dict that is returned, so for the
		   non-obligatory ones (the one with default values above) check
		   for a ``KeyError`` and if there is one, threat it like the default
		   value is the current data.

		   If a callback is added, it's called when the value is changed
		   automatically by this module.

		"""
		if internal_name in self._settings:
			#copy the value
			setting["value"] = self._settings[internal_name]["value"]
		else:
			#use the default value
			setting["value"] = setting.pop("defaultValue")
		#wrap it, True because it's added now
		wrappedSetting = SettingDict(setting, self._executeCallback, addedNow=True)
		#store
		self._settings[internal_name] = wrappedSetting

		return wrappedSetting

	def setting(self, internal_name):
		"""Method to return a setting from the internal name."""

		return self._settings[internal_name]

	@property
	def registeredSettings(self):
		"""Returns a list of all registered settings. The way to access
		   the settings if you are providing a settings dialog/other
		   interface like that.

		"""
		#only settings registered this session
		return [
			setting
			for setting in self._settings.values()
			if getattr(setting, "_addedNow", False)
		]

	def _executeCallback(self, callback):
		obj = self._modules.default(*callback["args"], **callback["kwargs"])
		getattr(obj, callback["method"])() #execute

def init(moduleManager):
	return SettingsModule(moduleManager)
