# --------------------------------------------------------------------------- #
# $Id: pllshared.py,v 1.27 2004/01/13 22:11:54 pandr Exp $
# --------------------------------------------------------------------------- #

# This is for stuff that is shared between pointless.py and pllbasics.py

import pllog
from string import *
import gettext
import re
from libpointless import LOCALEDIR
import types, exceptions
import copy

## HACK to work-around some problems in the old version of gettext.py
try:
	t = gettext.translation('pointless', LOCALEDIR)
	gt = t.gettext
except: 
	def gt(message):
		return (message)

# --------------------------------------------------------------------------- #

class EnumException(exceptions.Exception):
    pass

class Enumeration:
    def __init__(self, name, enumList):
        self.__doc__ = name
        lookup = { }
        reverseLookup = { }
        i = 0
        uniqueNames = [ ]
        uniqueValues = [ ]
        for x in enumList:
            if type(x) == types.TupleType:
                x, i = x
            if type(x) != types.StringType:
                raise EnumException, "enum name is not a string: " + x
            if type(i) != types.IntType:
                raise EnumException, "enum value is not an integer: " + i
            if x in uniqueNames:
                raise EnumException, "enum name is not unique: " + x
            if i in uniqueValues:
                raise EnumException, "enum value is not unique for " + x
            uniqueNames.append(x)
            uniqueValues.append(i)
            lookup[x] = i
            reverseLookup[i] = x
            i = i + 1
        self.lookup = lookup
        self.reverseLookup = reverseLookup
    def __getattr__(self, attr):
        if not self.lookup.has_key(attr):
            raise AttributeError
        return self.lookup[attr]
    def whatis(self, value):
		return self.reverseLookup[value]

eAdjustmentType = Enumeration("eAdjustmentType",
	[ "ADJUSTED",
	"FLUSH_LEFT",
	"FLUSH_RIGHT",
	"CENTERED"
	]
)

class v2:
	"""Two dimensional vector of floats"""
	def __init__(self, x, y):
		self.x = x
		self.y = y

class v3:
	"""Three dimensional vector of floats with overloaded operators"""
	def __init__(self, x, y, z):
		self.x = x
		self.y = y
		self.z = z
	def __add__(self, other):
		return v3(self.x + other.x, self.y + other.y, self.z + other.z)
	def __sub__(self, other):
		return v3(self.x - other.x, self.y - other.y, self.z - other.z)
	def __neg__(self):
		return v3(-self.x, -self.y, -self.z)
	def __str__(self):
		return "v3(%s,%s,%s)" % (self.x.__str__(), self.y.__str__(), 
			self.z.__str__())

class size:
	"""Two dimensional vector cast as range"""
	def __init__(self, min, max):
		self.min = min
		self.max = max
		assert(min<=max)
	def set(self, min, max):
		assert(min<=max)
		self.min, self.max = min, max
	def __str__(self):
		return "[%.3f, %.3f]" % (self.min, self.max)
	def size(self): return self.max - self.min

# --------------------------------------------------------------------------- #

class PllParseError(Exception):
	def __init__(self, args=None, line_no=0):
		errmsg = gt("Line %i: %s")%(line_no, args)
		self.args = errmsg

class PllCommandError(Exception):
	def __init__(self, args=None):
		self.args = args

# --------------------------------------------------------------------------- #

class PlSize:
	ePixels = 1
	ePlSize = 2
	def __init__(self, value):
		if value[-2:] == 'px':
			self.value = atof(value[:-2])
			self.unit  =  PlSize.ePixels
		else:
			self.value = atof(value)
			self.unit  = PlSize.ePlSize
	def pixels(self, scale):
		if self.unit == PlSize.ePixels:
			return self.value
		elif self.unit == PlSize.ePlSize:
			return self.value * scale
	def plsize(self, scale):
		if self.unit == PlSize.ePixels:
			return self.value / scale
		elif self.unit == PlSize.ePlSize:
			return self.value


class Color:

	color_table = {
		"black" : [0.0, 0.0, 0.0, 1.0],
		"white" : [1.0, 1.0, 1.0, 1.0],
		"red" 	: [0.7, 0.0, 0.0, 1.0],
		"green" : [0.0, 0.7, 0.0, 1.0],
		"blue"	: [0.0, 0.0, 0.7, 1.0],
		"yellow": [1.0, 1.0, 0.3, 1.0],
		"cyan"	: [0.0, 0.7, 0.7, 1.0],
		"magenta" :  [0.7, 0.0, 0.7, 1.0],
		"dgray"	: [0.3, 0.3, 0.3, 1.0],
		"lgray"	: [0.7, 0.7, 0.7, 1.0],
		"lred"	: [1.0, 0.3, 0.3, 1.0],
		"lgreen" : [0.3, 1.0, 0.3, 1.0],
		"lblue"	: [0.3, 0.3, 1.0, 1.0],
		"brown"	: [0.7, 0.7, 0.0, 1.0],
		"lcyan"	: [0.3, 1.0, 1.0, 1.0],
		"lmagenta" : [1.0, 0.3, 1.0, 1.0]
	}

	def __init__(self, rgba=(1.0, 1.0, 1.0, 1.0)):
		self.rgba = rgba
		assert(len(self.rgba) == 4), rgba
	def __repr__(self): return ("rgba="+str(self.rgba))
	def set_by_string(self, string, extra_args = []):
		if string[0] in '\'"' and string[-1] == string[0]:
			## color specification, e.g. "Black"
			string = string[1:-1].lower()
		if string.isalpha() and Color.color_table.has_key(string):
			self.rgba = Color.color_table[string]
			return 1
		if string[0:2] == "0x" and ((len(string) == 8) or (len(string) == 10)):
			## color specification 0x0000ff or 0x0000ffff: , e.g. 0x05050F
			r, g, b = [int(x,16)/255.0 for x in string[2:4], string[4:6], string[6:8]]
			if len(string) == 10:
				a = int(string[8:10], 16)/255.0
			else:
				a = 1.0
			self.rgba = [r, g, b, a]
			return 1

		if len(extra_args) >= 3:
			## color specification: ffff, e.g. 0.05, 0.5, 0.75, 1.0
			largs = [string]+ extra_args[0:3]
			extra_args[:] = extra_args[3:]
			self.rgba = [float(x) for x in largs]
			return 1
		self.error_msg = ("Unable to handle color specification %s" \
			% string)
		return 0

	def set(self, r, g, b, a):
		self.rgba = [r, g, b, a]

re_comment    = re.compile(r'^#.*')
re_command    = re.compile('^=([A-Za-z_][A-Za-z0-9_-]*)(\.[A-Za-z]*|)(\(|)[ \t]*')
re_begin      = re.compile('^begin-[A-Za-z_]')
re_linewrap   = re.compile(r'^\\\s*$')
re_escapes    = re.compile(r'^\\([=\\{} ])')
re_blockstart = re.compile(r'^{')
re_blockend   = re.compile(r'^}')
re_letters    = re.compile(r'^[^\s=]')
re_space      = re.compile(r'^\s')
re_newline    = re.compile(r'^\n')
re_eof        = re.compile(r'^$')

class Command:
	def __init__(self, name, execute=None, slidestep=0, transform=0, 
		break_condition=re_newline, vars = None, empty = 0):
		l = name.split(":")
		if len(l) == 2:
			self.name, self.argfmt = l
		else:
			self.name = name
			self.argfmt = ""
		self.exe = execute
		self.slidestep = slidestep
		self.transform  = transform
		self.break_condition = break_condition
		self.vars = vars
		self._attrs = [{}]
		self.empty = empty
	def execute(self, args, parser, vars):
		assert(self.exe)
		if self.vars:
			return self.exe(args, parser, vars)
		else:
			return self.exe(args, parser)
	def parse_args(self, argstring):
		args = []
		largs = argstring.split(",")
		for fmt in self.argfmt:
			if not fmt:
				pllog.warn("command %s called with too many arguments", self.name)
				return []
			if len(largs) == 0:
				pllog.warn("command %s called with too few arguments", self.name)
				return []
			arg = (largs[0]).strip()
			largs = largs[1:] ## grr, an ugly default (just a single element) pop 
			if fmt == 'i':
				if arg[0:2] == "0x":
					args.append(atoi(arg,16))
				else:
					args.append(atoi(arg))
			elif fmt == 'f':
				if arg:
					args.append(atof(arg))
				else:
					pllog.warn("command %s called with too few arguments", self.name)
					return []
			elif fmt == 's':
				if arg[0] == '"' and arg[-1] == '"':
					args.append(arg[1:-1])
				else:
					pllog.warn("command %s called with wrong argument"% self.name)
					return []
			elif fmt == 'S':
				if arg:
					args.append(PlSize(arg))
				else:
					pllog.warn("command %s called with too few arguments", self.name)
					return []
			elif fmt == 'c':
				color = Color()
				if not color.set_by_string(arg, largs):
					pllog.warn(color.error_msg)
					args.append(Color())
				else:
					args.append(color)
			else:
				pllog.warn("Unknown format value %s" % fmt)
				return []
		return args
	def set_attrs(self, hash):
		assert(self.vars)
		for k in hash.keys():
			self._attrs[-1][k] = hash[k]
	def set_base_attrs(self, hash):
		assert(self.vars)
		for k in hash.keys():
			self._attrs[0][k] = hash[k]
	def push_attrs(self):
		if not self.vars: return
		self._attrs.append(copy.copy(self._attrs[0]))
	def pop_attrs(self):
		if not self.vars: return
		self._attrs.pop()
	def __getitem__(self, key):
		assert(self.vars)
		return self.get_attr(key)
	def get_attr(self, key):
		assert(self.vars)
		#for i in range(len(self._attrs)):
		#	if self._attrs[-1-i].has_key(key): return self._attrs[-1-i][key]
		if self._attrs[-1].has_key(key): return self._attrs[-1][key]
		return None
	def get_attrs(self):
		assert(self.vars)
		return self._attrs[-1]

# --------------------------------------------------------------------------- #

