# -*- coding: utf-8 -*-

# Copyright (C) 2003-2006 by Igor E. Novikov
# Copyright (C) 1997, 1998, 2001 by Bernhard Herzog 
#
# This library is covered by GNU Library General Public License.
# For more info see COPYRIGHTS file in sK1 root directory.


from types import StringType, TupleType, FunctionType

from app import Publisher
from app.conf.const import CHANGED, SELECTION
from app.events.warn import warn, warn_tb, INTERNAL

#
#	Command Class
#


class Command(Publisher):

	def __init__(self, cmd_class, object):
		self.cmd_class = cmd_class
		self.object = object

	def __getattr__(self, attr):
		try:
			return getattr(self.cmd_class, attr)
		except AttributeError:
			if attr == 'button_name':
				return self.menu_name
		raise AttributeError, attr

	def get_method(self, path):
		if callable(path):
			return path
		method = self.object
		if type(path) != TupleType:
			path = (path,)
		for name in path:
			method = getattr(method, name)
		return method

	def Invoke(self, args = ()):
		if type(args) != TupleType:
			args = (args,)
		try:
			apply(self.get_method(self.command), self.args + args)
		except:
			warn_tb(INTERNAL)

	def Update(self):
		# XXX: bitmaps and key_strokes should probably be also changeable
		changed = self.set_name(self.get_name())
		changed = self.set_sensitive(self.get_sensitive()) or changed
		changed = self.set_value(self.get_value()) or changed
		if changed:
			self.issue(CHANGED)

	def get_name(self):
		if self.name_cb:
			method = self.get_method(self.name_cb)
			if method:
				return method()
		return self.menu_name

	def set_name(self, menu_name = None):
		changed = self.menu_name != menu_name
		if changed:
			self.menu_name = menu_name
		return changed

	def get_sensitive(self):
		if self.sensitive_cb:
			method = self.get_method(self.sensitive_cb)
			if method:
				return method()
			else:
				warn(INTERNAL, 'no method for sensitive_cb (%s)',
						self.sensitive_cb)
				return 0
		return 1

	def set_sensitive(self, sensitive):
		changed = self.sensitive != sensitive
		if changed:
			self.sensitive = sensitive
		return changed

	def get_value(self):
		if self.value_cb:
			method = self.get_method(self.value_cb)
			if method:
				return method()
		return self.value

	def set_value(self, value):
		changed = self.value != value
		if changed:
			self.value = value
		return changed

	def GetKeystroke(self):
		return self.key_stroke

	def GetValue(self):
		return self.value

	def IsOn(self):
		return self.value == self.value_on

	def InContext(self):
		return 1

	def set_bitmap(self, bitmap):
		if bitmap:
			changed = self.bitmap != bitmap
			self.bitmap = bitmap
			return changed
		return 0

	def __repr__(self):
		return 'Command: %s' % self.name



class CommandClass:

	cmd_class = Command

	# default attributes
	menu_name = '???'
	bitmap = None
	image = 'none16'
	key_stroke = None

	name_cb = None
	sensitive_cb = None
	sensitive = 1
	value_cb = None
	value = 0
	value_on = 1
	value_off = 0

	is_command = 1
	is_check = 0
	invoke_with_keystroke = 0
	invoke_with_event = 0


	callable_attributes = ('name_cb', 'sensitive_cb', 'value_cb')

	def __init__(self, name, command, subscribe_to = None, args = (),
					is_check = 0, **rest):
		self.name = name
		self.command = command
		self.subscribe_to = subscribe_to
		if type(args) != TupleType:
			self.args = (args,)
		else:
			self.args = args
		for key, value in rest.items():
			setattr(self, key, value)

		if is_check:
			self.is_check = 1
			self.is_command = 0


	def InstantiateFor(self, object):
		cmd = self.cmd_class(self, object)
		if self.subscribe_to:
			if type(self.subscribe_to) == TupleType:
				attrs = self.subscribe_to[:-1]
				for attr in attrs:
					object = getattr(object, attr)
				subscribe_to = self.subscribe_to[-1]
			else:
				subscribe_to = self.subscribe_to
			object.Subscribe(subscribe_to, cmd.Update)
		return cmd

	def __repr__(self):
		return 'CommandClass: %s' % self.name



class ObjectCommand(Command):

	def get_method(self, path):
		if type(path) == type(""):
			return self.object.document.GetObjectMethod(self.object_class,path)
		return Command.get_method(self, path)

	def Invoke(self, args = ()):		
		if type(args) != TupleType:
			args = (args,)
		try:
			apply(self.object.document.CallObjectMethod,
					(self.object_class, self.menu_name, self.command) \
					+ self.args + args)
		except:
			warn_tb(INTERNAL)

	def get_sensitive(self):
		if self.object.document.CurrentObjectCompatible(self.object_class):
			return Command.get_sensitive(self)
		return 0

	def GetKeystroke(self):
		return self.key_stroke

	def GetValue(self):
		return self.value

	def InContext(self):
		return self.object.document.CurrentObjectCompatible(self.object_class)

	def __repr__(self):
		return 'ObjectCommand: %s' % self.name


class ObjectCommandClass(CommandClass):

	cmd_class = ObjectCommand
	object_class = None

	def SetClass(self, aclass):
		if self.object_class is None:
			self.object_class = aclass


#
#
#

class Commands:

	def Update(self):
		for item in self.__dict__.values():
			item.Update()

	def __getitem__(self, key):
		return getattr(self, key)

	def Get(self, name):
		try:
			return getattr(self, name)
		except AttributeError:
			for item in self.__dict__.values():
				if item.__class__ == Commands:
					cmd = item.Get(name)
					if cmd:
						return cmd
			else:
				return None
#
#
#

class Keymap:

	def __init__(self):
		self.map = {}

	def AddCommand(self, command):
		key_stroke = command.GetKeystroke()
		if key_stroke:
			if type(key_stroke) == StringType:
				key_stroke = (key_stroke,)
			for stroke in key_stroke:
				if self.map.has_key(stroke):
					pass
					# XXX: should be user visible if keybindings can be
					# changed by user
# 					warn(INTERNAL, 'Warning: Binding %s to %s replaces %s', command.name, stroke, self.map[stroke].name)
				self.map[stroke] = command

	def MapKeystroke(self, stroke):
		if self.map.has_key(stroke):
			return self.map[stroke]


#
#
#

def AddCmd(list, name, menu_name, method = None, **kw):
	if type(name) == FunctionType:
		name = name.func_name
	if method is None:
		method = name
	elif type(method) == FunctionType:
		method = method.func_name
	kw['menu_name'] = menu_name
	kw['subscribe_to'] = SELECTION
	cmd = apply(ObjectCommandClass, (name, method), kw)
	list.append(cmd)
