# --------------------------------------------------------------------------- #
# $Id: pllbasics.py,v 1.94 2004/01/11 22:01:45 pandr Exp $
# --------------------------------------------------------------------------- #

from pllshared import *
import re
import string
import os
import ploptions
import pllog

pll = None

def register():
	"""pll.add_command takes 3 parameters, first parameter is this name 
	of the command in the PLL language. A command COM may take a number 
	of arguments which will be written as COM:x_1x_2..., where x_i denotes 
	the type of parameter i. The second parameter to the pll.add_command
	is the implementation of the command (will refer to a function in
	this source file), additional parameters are described in the
	implementation of the Command class.
	"""
	## grouping/controlling appearance
	pll.add_command("par",    _par)
	pll.add_command("row",    _row, vars=1)
	pll.add_command("metarow",_metarow)
	pll.add_command("cel",    _cel, vars=1, empty=1)
	pll.add_command("br",     _br, empty=1)
	pll.add_command("center", _center)
	pll.add_command("step", _step, empty=1)
	pll.add_command("nostep", _nostep, empty=1)
	## language
	pll.add_command("import:s",  _import)
	pll.add_command("include:s",  _include)
	pll.add_command("macro:s", _macro, transform=1)
	pll.add_command("comment", lambda args,parser: "", transform=1)
	pll.add_command("verb", _verb, transform=1)
	## graphics
	pll.add_command("hrule:S",  _hrule)
	pll.add_command("vrule:S",  _vrule)
	pll.add_command("background_image:s",  _background_image)
	pll.add_command("background_image_tile:s",  _background_image_tile)
	pll.add_command("background_image_stretch:s", _background_image_stretch)
	pll.add_command("background_color:c",  _background_color)
	pll.add_command("background_vertex_colors:cccc",  _background_vertex_colors)
	pll.add_command("image:s",    _image)
	pll.add_command("absolute_image:sfff",    _absolute_image)
	pll.add_command("gaimage:s",  _gaimage)
	## glue 
	pll.add_command("hskip:S",  _hskip, empty=1)
	pll.add_command("vskip:S",  _vskip, empty=1)
	pll.add_command("smallskip",  _smallskip, empty=1)
	pll.add_command("medskip",  _medskip, empty=1)
	pll.add_command("bigskip",  _bigskip, empty=1)
	## font 
	pll.add_command("make_font_family:sssss", _make_font_family, empty=1)
	pll.add_command("font_family:s", _font_family, empty=1)
	pll.add_command("rm",         _normalfont, empty=1)		## tex name
	pll.add_command("roman",      _normalfont, empty=1)
	pll.add_command("bf",         _boldfont, empty=1)		## tex name
	pll.add_command("bold",       _boldfont, empty=1)
	pll.add_command("it",         _italicfont, empty=1)		## tex name
	pll.add_command("italic",     _italicfont, empty=1)
	pll.add_command("tt",         _monofont, empty=1)		## tex name 
	pll.add_command("mono",       _monofont, empty=1)
	pll.add_command("setsize:S",  _setsize, empty=1)
	pll.add_command("setbasesize:S",  _setbasesize, empty=1)
	pll.add_command("setfactor:sf",  _setfactor, empty=1)
	pll.add_command("setfont:s",  _setfont, empty=1)
	pll.add_command("normal",     _normal, empty=1)
	pll.add_command("large",      _large, empty=1)
	pll.add_command("huge",      _huge, empty=1)
	pll.add_command("small",      _small, empty=1)
	pll.add_command("tiny",      _tiny, empty=1)
	pll.add_command("setdropshadow:S",  _setdropshadow, empty=1)
	## color 
	pll.add_command("setcolor:c",  _setcolor, empty=1)
	pll.add_command("red",   lambda args,parser: _setcolor([Color(rgba=(1,0,0,1))],parser), empty=1)
	pll.add_command("green", lambda args,parser: _setcolor([Color(rgba=(0,1,0,1))],parser), empty=1)
	pll.add_command("blue",  lambda args,parser: _setcolor([Color(rgba=(0,0,1,1))],parser), empty=1)
	pll.add_command("yellow", lambda args,parser: _setcolor([Color(rgba=(1,1,0,1))],parser), empty=1)
	pll.add_command("white", lambda args,parser: _setcolor([Color(rgba=(1,1,1,1))],parser), empty=1)
	pll.add_command("black", lambda args,parser: _setcolor([Color(rgba=(0,0,0,1))],parser), empty=1)
	## misc
	pll.add_command("unicode:i",  _unicode, empty=1)
	## deprecated
	pll.add_command("setitemmark",  _depr, empty=1)
	pll.add_command("setsubitemmark",  _depr, empty=1)
	pll.add_command("setsubitemmark",  _depr, empty=1)

# --------------------------------------------------------------------------- #
# grouping/controlling appearance
# --------------------------------------------------------------------------- #

def _slide(args, parser, vars):
	pll.new_slide()
	controller = pll.slide_controller({'move_in':vars['move_in'],
		'move_out':vars['move_out'],'in_delay':vars['in_delay']})
	result = [controller]
	_normal(None, None)
	pll.fs.push()
	_huge(None, None)
	title = parser()
	pll.fs.pop()
	if not len(title):
		return result
	x = lambda : title
	result += _par(None, parser=x)
	linepos = -pll.fs.current().font.underline_position
	linewidth = pll.fs.current().font.underline_thickness
	if linewidth < 1.0: linewidth = 1
	result += [pll.vertical_bl_glue(linepos, force=1)]
	result += [pll.stretchlist([pll.rectangle(0,linewidth,Color(rgba=(1,0,0,1)))])]
	return result

slide = Command("slide", _slide, vars=1)
slide.set_attrs({'move_in':(0.0, 0.0, 0.0), 'move_out':(0.0, 0.0, 0.0), 
	'in_delay':0.8})

def _par(args, parser):
	baselineskip = pll.fs.current().font.baselineskip
	return [
		pll.vertical_bl_glue(baselineskip*1.5),
		pll.paragraphlist(parser()),
	]

def _cel(args, parser, vars):
	borderinfo = vars['borders']
	bordercolor = vars['bordercolor']
	cellpadding = vars['cellpadding']
	return [pll.cellmark(cellpadding, borderinfo, bordercolor)]

def _row(args, parser, vars):
	borderinfo = vars['borders']
	bordercolor = vars['bordercolor']
	cellpadding = vars['cellpadding']
	return [pll.row(parser(), cellpadding, borderinfo, bordercolor)]

def _metarow(args, parser):
	pll.setmetarow(pll.row(parser()))
	return []

def _table(args, parser, vars):
	borderinfo = vars['borders']
	bordercolor = vars['bordercolor']
	cellpadding = vars['cellpadding']
	baselineskip = pll.fs.current().font.baselineskip
	pll.setmetarow(None)
	table = pll.table(parser(), cellpadding, borderinfo, bordercolor)
	return [pll.vertical_bl_glue(baselineskip*1.5), 
		table ]

table = Command("table", _table, vars=1)
table.set_attrs({'cellpadding':3})
table.set_attrs({'borders':''})
table.set_attrs({'bordercolor':'0xffffff'})

def _depr(args, parser):
	raise PllCommandError("Command has been removed. Please see ChangeLog for details.");

def _br(args, parser):
	return [pll.linebreak()]

def _center(args, parser):
	baselineskip = pll.fs.current().font.baselineskip
	return [pll.vertical_bl_glue(baselineskip*1.5), 
		pll.paragraphlist(parser(),
		adjustment=eAdjustmentType.CENTERED)]

def _itemhandler(parser, mark, prefix):
	result = pll.nodelist([])
	if prefix:
		result += [prefix]
	result += [mark]	
	result += [pll.interword_glue()]
	result += [pll.paragraphlist(parser())]
	baselineskip = pll.fs.current().font.baselineskip
	return [pll.vertical_bl_glue(baselineskip*1.5), result] 

def _item(args, parser, vars):
	mark = pll.eval(vars['mark'])
	indent = vars['indent']
	return _itemhandler(parser,mark,pll.interword_glue(scale=indent))

item = Command("item", _item, slidestep = 1, vars = 1)
item.set_attrs({'mark':'=unicode(0x2022)', 'indent':0.0})

subitem = Command("subitem", _item, slidestep = 1, vars = 1)
subitem.set_attrs({'mark':'=unicode(0x2022)', 'indent':3.0})

subsubitem = Command("subsubitem", _item, slidestep = 1, vars = 1)
subsubitem.set_attrs({'mark':'=unicode(0x2022)', 'indent':6.0})

def _nostep(args, parser):
	pll.skip_next_step()
	return []

def _step(args, parser):
	pll.next_slide_step()
	return [pll.item_step()]

# --------------------------------------------------------------------------- #
# glue
# --------------------------------------------------------------------------- #

def _vskip(args, parser):
	if args:
		return [pll.vertical_glue(args[0].pixels(pll.scale))]
	else:
		pllog.warn("missing arguments to command =vskip")
		return []

def _hskip(args, parser):
	if args:
		return [pll.horizontal_glue(args[0].pixels(pll.scale))]
	else:
		pllog.warn("missing arguments to command =hskip")
		return []

def _smallskip(args, parser):
	return [pll.vertical_glue(3*pll.scale)]

def _medskip(args, parser):
	return [pll.vertical_glue(6*pll.scale)]

def _bigskip(args, parser):
	return [pll.vertical_glue(12*pll.scale)]

def _hrule(args, parser):
	if args:
		linepos = -pll.fs.current().font.underline_position
		rectangle = pll.rectangle(0, args[0].pixels(pll.scale), 
			pll.fs.current().color)
		return [pll.vertical_bl_glue(linepos, force=1),
			pll.stretchlist([rectangle])]
	else:
		pllog.warn("missing arguments to command =hrule")
		return []

def _vrule(args, parser):
	if args:
		rectangle = pll.rectangle(args[0].pixels(pll.scale),
			0, pll.fs.current().color)
		return [rectangle]
	else:
		pllog.warn("missing arguments to command =vrule")
		return []

# --------------------------------------------------------------------------- #
# graphics
# --------------------------------------------------------------------------- #
	
def _background_image(args, parser):
	if args:
		filename = args[0]
		image = pll.image(filename, center=0)
		if image:
			pll.background.add(image)
		return []
	else:
		pllog.warn("missing args to _background_image")
		return []	

def _background_image_tile(args, parser):
	filename = args[0]
	b = pll.backgroundimage(0,filename)
	if b: pll.background.add(b)
	return []

def _background_image_stretch(args, parser):
	filename = args[0]
	b = pll.backgroundimage(1,filename)
	if b: pll.background.add(b)
	return []

def _background_color(args, parser):
	if args:
		size = pll.get_slide_size()
		b = pll.rectangle(size.x.size(), size.y.size(), args[0])
		if b:
			pll.background.add(b)
			b.set_pos(v3(size.x.min, size.y.min, 0.0))
	else:
		pllog.warn("missing args to _background_color")
	return []

def _background_vertex_colors(args, parser):
	size = pll.get_slide_size()
	if args:
		b = pll.rectangle(size.x.size(), size.y.size(), 
			args[0], args[1], args[2], args[3])
	else:
		pllog.warn("missing args to _background_vertex_colors")
		b = None
	if b:
		pll.background.add(b)
		b.set_pos(v3(size.x.min, size.y.min, 0.0))
	return []

def _absolute_image(args, parser):
	if args:
		filename = args[0]
		im = pll.image(filename, center=0)
		(x_max, y_max) = ((im.get_size().x).max, (im.get_size().y).max)
		(x_slide, y_slide) = ((pll.slide_size.x).max, (pll.slide_size.y).max)
		x_slide_max = x_slide-0.06*x_slide ## real_width-2.0*left_margin
		y_slide_max = y_slide-0.06*y_slide ## real_height-2*top_margin
		if (args[1] + x_max > x_slide_max) or (args[1] < 0):
			pllog.warn("Unable to fit image into the current screen dimensions")
		if (args[2] + y_max > y_slide_max) or (args[2] < 0):
			pllog.warn("Unable to fit image into the current screen dimensions")
		return [ pll.absolute_glue(v3(args[1],-args[2]-y_max,args[3])), im ]
	else:
		pllog.warn("missing args to _absolute_image")
		return []	

def _image(args, parser):
	if args:
		filename = args[0]
		return [ pll.vertical_glue(0), pll.image(filename, center=0) ]
	else:
		pllog.warn("missing args to _image")
		return []	

def _gaimage(args, parser):
	if args:
		filename = args[0]
		return [ pll.vertical_glue(0), pll.gaimage(filename, center=0) ]
	else:
		pllog.warn("missing args to _gaimage")
		return []	

# --------------------------------------------------------------------------- #
# Font handling
# --------------------------------------------------------------------------- #

class FontFamily:
	def __init__(self, normal, bold, italic, mono):
		self.normal = normal
		self.bold   = bold
		self.italic = italic
		self.mono   = mono

font_families = {
	'default': FontFamily(
		ploptions.default_fontname,
		ploptions.default_fontname,
		ploptions.default_fontname,
		ploptions.default_fontname)
	}
current_font_family = font_families['default']

def _make_font_family(args, parser):
	familyname, normal, bold, italic, mono = args
	font_families[familyname] = FontFamily(normal, bold, italic, mono)
	return []

def _font_family(args, parser):
	global current_font_family
	familyname, = args
	if not font_families.has_key(familyname):
		#Fixme How to report errors?
		print "Unknown family '%s'" % familyname
		return []
	current_font_family = font_families[familyname]
	_normalfont(None, None)
	return []

def _normalfont(args, parser):
	pll.fs.set_font(current_font_family.normal)
	return []

def _boldfont(args, parser):
	pll.fs.set_font(current_font_family.bold)
	return []

def _italicfont(args, parser):
	pll.fs.set_font(current_font_family.italic)
	return []

def _monofont(args, parser):
	pll.fs.set_font(current_font_family.mono)
	return []
		
def _setsize(args, parser):
	if args:
		pll.fs.set_size(args[0].pixels(pll.scale))
	else:
		pllog.warn("missing args to _setsize")
	return []

def _setbasesize(args, parser):
	if args:
		pll.basesize = args[0]
	else:
		pllog.warn("missing args to _setbasesize")
	return []

def _setfactor(args, parser):
	size, factor = args
	if size == "tiny":
		pll.tinyfactor = factor
	elif size == "small":
		pll.smallfactor = factor
	elif size == "large":
		pll.largefactor = factor
	elif size == "huge":
		pll.hugefactor = factor
	return []

def _setfont(args, parser):
	if args:
		pll.fs.set_font(args[0])
	else:
		pllog.warn("missing args to _setfont")
	return []

def _normal(args, parser):
	pll.fs.set_size(pll.basesize.pixels(pll.scale))
	return []

def _large(args, parser):
	pll.fs.set_size(pll.basesize.pixels(pll.scale)*pll.largefactor)
	return []

def _huge(args, parser):
	pll.fs.set_size(pll.basesize.pixels(pll.scale)*pll.hugefactor)
	return []

def _small(args, parser):
	pll.fs.set_size(pll.basesize.pixels(pll.scale)*pll.smallfactor)
	return []

def _tiny(args, parser):
	pll.fs.set_size(pll.basesize.pixels(pll.scale)*pll.tinyfactor)
	return []

def _setcolor(args, parser):
	assert(isinstance(args[0], Color)), args[0]
	pll.fs.current().color = args[0]
	return []

def _setdropshadow(args, parser):
	pll.fs.set_dropshadow(int(args[0].pixels(pll.scale)))
	return []

# --------------------------------------------------------------------------- #
# Language
# --------------------------------------------------------------------------- #

def _import(args, parser):
	if args:
		pll.import_module(args[0])
	else:
		pllog.warn("missing args to _import")
	return []

def _include(args, parser):
	if args:
		pll.include_file(args[0])
	else:
		pllog.warn("missing args to _include")
	return []

def _verb(args, text):
	text = re.sub('[{}\\= ]',r'\\\g<0>',text)
	text = re.sub('\n', '=br ', text)
	text = '=begin-par\n'+text+'\n=end-par\n'
	return text

def _macro(args, text):
	if args:
		name = args[0]
		pll.add_command(
			name,
			lambda args, t : __expand(args, t, text),
			transform=1,
			break_condition=re.compile('^')
		)
	else:
		pllog.warn("missing args to _macro")
	return ""

# --------------------------------------------------------------------------- #
# Misc
# --------------------------------------------------------------------------- #

def _unicode(args, parser):
	if args:
		code = args[0]
		return [pll.letter(code)]
	else:
		pllog.warn("missing args to _unicode")
		return []

# --------------------------------------------------------------------------- #
# Auxilary classes and functions
# --------------------------------------------------------------------------- #

def __expand(args, text, content):
	## Internal (to pllbasics.py) function, hence the __ prefix
	num = 0
	for a in args:
		num += 1
		content = re.sub('#'+str(num), str(a), content)
	return content

