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

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

#
#	PropertyPanel for plugin properties
#

#
# The parameters are described by a sequence of tuples like this
#
#	(NAME, TYPE, VALUE, RANGE, LABEL)
#
# LABEL is text suitable for a label in a dialog box. NAME is the
# internal name for that property. The constructor of the plugin object
# must accept a keyword argument NAME. Also, the plugin object must have
# a method CAPNAME to read the current value of that property and a
# method SetParameters accepting a dictionary of parameters. CAPNAME is
# a `Capitalized' version of the lowercase NAME (if NAME is
# `the_parameter', CAPNAME is `TheParameter')
#
# TYPE describes the type of the parameter, VALUE is the initial value
# and RANGE describes the valid values.
#
# Supported types:
#
#	type	range
#
#	int	(MIN, MAX)	The minimal and maximal value. MIN and/or MAX
#				may be None indicating no limit (apart from the
#				builtin limits).
#	float	(MIN, MAX)	same as for int.
#
#	length	(MIN, MAX)	A length in pt. The user can change the unit.
#
#	text	ignored		A text string (the range should be None for
#				now)
#

from string import split, join, capitalize

from app.events.warn import warn, USER
from app.conf.const import SELECTION
from app import _

from Tkinter import Label, IntVar, DoubleVar, StringVar, RIGHT, E
from tkext import MyEntry
from miniscroll import MiniScroller

from sketchdlg import PropertyPanel
from lengthvar import create_length_widgets



def capwords(ident):
	return join(map(capitalize, split(ident, '_')), '')

class Parameter:

	def __init__(self, panel, name, value, range, label):
		self.name = name
		self.value = value
		self.range = range
		self.label = label
		self.var = None
		self.panel = panel


	def build_widgets(self, master, row):
		label = Label(master, text = self.label)
		label.grid(row = row, column = 0, sticky = E)

	def __call__(self, *args):
		if args:
			self.var.set(args[0])
		else:
			return self.var.get()

parameter_types = {}

class NumberParameter(Parameter):

	def is_valid(self):
		value = self.var.get()
		min, max = self.range
		if min is not None and min > value:
			return 0
		if max is not None and max < value:
			return 0
		return 1

	def init_var(self):
		lo, hi = self.range
		value = self.value
		if lo is None:
			if hi is not None:
				if value > hi:
					value = hi
		else:
			if hi is None:
				if value < lo:
					value = lo
			else:
				value = max(lo, min(hi, value))
		if value != self.value:
			warn(USER,
					'Initial value in parameter %s of plugin %s out of range',
					self.name, self.panel.title)
		self.var.set(value)

	def var_changed(self, *rest):
		if self.is_valid():
			self.panel.set_parameter(self)

	def build_widgets(self, master, row, build_entry = 1):
		self.init_var()
		Parameter.build_widgets(self, master, row)
		if build_entry:
			entry = MyEntry(master, textvariable = self.var, justify = RIGHT,
							width = 6, command = self.var_changed)
			entry.grid(row = row, column = 1, sticky = 'ew')
			min, max = self.range
			scroll = MiniScroller(master, variable = self.var,
									min = min, max = max, step = 1)
			scroll.grid(row = row, column = 2, sticky = 'news')


class IntParameter(NumberParameter):

	def build_widgets(self, master, row):
		self.var = IntVar(master)
		NumberParameter.build_widgets(self, master, row)

parameter_types['int'] = IntParameter

class FloatParameter(NumberParameter):

	def build_widgets(self, master, row):
		self.var = DoubleVar(master)
		NumberParameter.build_widgets(self, master, row)

parameter_types['float'] = FloatParameter

class LengthParameter(NumberParameter):

	def build_widgets(self, master, row):
		self.var, entry, scroll, menu = create_length_widgets(master, master,
																self.var_changed)
		NumberParameter.build_widgets(self, master, row, build_entry = 0)
		entry.grid(row = row, column = 1, sticky = 'ew')
		scroll.grid(row = row, column = 2, sticky = 'ns')
		menu.grid(row = row, column = 3)

parameter_types['length'] = LengthParameter

class TextParameter(Parameter):

	def build_widgets(self, master, row):
		Parameter.build_widgets(self, master, row)
		self.var = StringVar(master)
		self.var.set(self.value)
		entry = MyEntry(master, textvariable = self.var, width = 0,
						command = self.var_changed)
		entry.grid(row = row, column = 1, columnspan = 3, sticky = 'ew')

	def var_changed(self, *rest):
		self.panel.set_parameter(self)

parameter_types['text'] = TextParameter

class PluginPanel(PropertyPanel):

	def __init__(self, master, main_window, doc, info):
		self.var_auto_update = IntVar(master)
		self.var_auto_update.set(1)
		self.info = info
		self.title = info.menu_text
		self.vars = []
		name = 'dlg' + info.class_name
		PropertyPanel.__init__(self, master, main_window, doc, name = name)

	def build_dlg(self):
		top = self.top
		row = 0

		for row in range(len(self.info.parameters)):
			name, type, value, prange, label = self.info.parameters[row]
			try:
				#print name, type, value, prange, label
				var = parameter_types[type](self, name, value, prange, label)
				var.build_widgets(top, row)
				self.vars.append(var)
			except KeyError:
				warn(USER, 'Unknown plugin parameter type %s' % type)
				continue
		row = row + 1
		top.columnconfigure(0, weight = 0)
		top.columnconfigure(1, weight = 1)
		top.columnconfigure(2, weight = 0)
		top.columnconfigure(3, weight = 0)


		frame = self.create_std_buttons(top)
		frame.grid(row = row, columnspan = 4, sticky = 'ew')

	def set_parameter(self, var):
		if not self.current_is_plugin():
			return
		doc = self.document
		doc.BeginTransaction(_("Set %s") % var.label) # NLS
		try:
			try:
				kw = {var.name: var()}
				doc.AddUndo(self.current_object().SetParameters(kw))
			except:
				doc.AbortTransaction()
		finally:
			doc.EndTransaction()

	def can_apply(self):
		return 1

	def do_apply(self):
		if self.current_is_plugin():
			doc = self.document
			doc.BeginTransaction(_("Set Parameters"))
			try:
				try:
					kw = {}
					for var in self.vars:
						kw[var.name] = var()
					doc.AddUndo(self.current_object().SetParameters(kw))
				except:
					doc.AbortTransaction()
			finally:
				doc.EndTransaction()
		else:
			# create that object
			dict = {}
			for var in self.vars:
				dict[var.name] = var()
			obj = apply(self.info.Constructor(), (), dict)
			text = 'Create ' + self.info.menu_text # NLS
			self.main_window.canvas.PlaceObject(obj, text)


	def current_is_plugin(self):
		o = self.document.CurrentObject()
		return o is not None and o.is_Plugin and \
				o.class_name == self.info.class_name

	def current_object(self):
		return self.document.CurrentObject()

	def init_from_doc(self):
		self.Update()
		self.issue(SELECTION)

	def Update(self):
		if self.current_is_plugin():
			object = self.current_object()
			for var in self.vars:
				var(getattr(object, capwords(var.name))())

	def update_from_object_cb(self, obj):
		print 'update_from_object_cb', obj
		while not obj.is_Plugin and not obj.is_Layer:
			obj = obj.parent
		if obj.is_Plugin and obj.class_name == self.info.class_name:
			for var in self.vars:
				print 'update_from_object_cb', var
				var(getattr(obj, capwords(var.name))())

	def close_dlg(self):
		self.vars = []
		PropertyPanel.close_dlg(self)
