#!/usr/bin/env python

import formatter
import htmllib
import urllib
import urlparse
import re
import md5
import os
import sys

import pygame

class table:
	def __init__(self, ancestor, name):
		self.name = name
		self.children = []
		self.x = 0
		self.y = 0
		self.w = 0
		self.h = 0
		self.content = None
		if ancestor is not None:
			self.parent = ancestor
			if ancestor.align == "horizontal":
				self.align = "vertical"
			else:
				self.align = "horizontal"
			ancestor.children.append(self)
		else:
			self.parent = None
			self.align = "vertical"

	def dump(self, x = 0):
		p = ""
		for i in range(0, x):
			p += "  "
		p += self.name + " "
		p += str(self.x) + "/" + str(self.y) + " - " + str(self.w) + "/" + str(self.h)
		p += " " + self.align
		print p
		#if self.content:
		#	print "!!! content"
		#	parser.dump(self.content, 0)
		for c in self.children:
			c.dump(x + 1)

	def cons(self):
		givenw = 0
		givenh = 0
		count = 0
		for c in self.children:
			if self.align == "horizontal":
				if c.w > 0:
					givenw += c.w
					count += 1
				c.h = self.h
			if self.align == "vertical":
				if c.h > 0:
					givenh += c.h
					count += 1
				c.w = self.w
		if len(self.children) <= count:
			return
		if self.align == "horizontal":
			n = (self.w - givenw) / (len(self.children) - count)
			for c in self.children:
				if c.w == 0:
					c.w = n
		else:
			n = (self.h - givenh) / (len(self.children) - count)
			for c in self.children:
				if c.h == 0:
					c.h = n
		for c in self.children:
			c.cons()

	def normalize(self, direction):
		if direction == "vertical":
			if len(self.children) == 0:
				print "##### weird!"
				return
			l = len(self.children[0].children)
			max = 0
			for d in range(0, l):
				max = 0
				for c in self.children:
					if d < len(c.children):
						if c.children[d].w > max:
							max = c.children[d].w
				for c in self.children:
					if d < len(c.children):
						c.children[d].w = max
			if self.w == 0:
				self.w = max * l
		elif direction == "horizontal":
			for c in self.children:
				max = 0
				for d in c.children:
					if d.h > max:
						max = d.h
				for d in c.children:
					d.h = max
				c.h = max

	def finish(self):
		y = 0
		for c in self.children:
			x = 0
			for d in c.children:
				d.x = x
				d.w += x
				x = d.w
			c.y = y
			c.h += y
			y = c.h

class stylesheet:
	def __init__(self):
		self.ident = None
		self.element = None
		self.styles = {}

class surface:
	def __init__(self, surface):
		self.surface = surface
		self.original = None
		self.x = -1
		self.y = -1

class renderpart:
	def __init__(self):
		self.x = -1
		self.y = -1
		#self.width = 0
		#self.height = 0
		self.surfaces = []
		self.content = []
		self.attributes = {}
		self.children = []
		self.parent = None
		#self.ready = 0
		self.activated = 0
		self.container = None

class renderobject:
	def __init__(self):
		self.title = ""
		self.root = renderpart()
		self.part = self.root
		self.bodypart = None
		self.stylepart = None
		self.resources = []
		self.containers = []

def validator(str):
	print str

class myparser(htmllib.HTMLParser):
	def __init__(self):
		f = formatter.NullFormatter()
		htmllib.HTMLParser.__init__(self, f)
		self.statelist = []
		self.statestack = []
		self.object = None
		self.container = None
		self.baseurl = ""

		self.tempdir = "/tmp/pyromaniac/"

	def __getattr__(self, name):
		if name[0:3] == "end":
			tag = name[4:]
			self.handle_endtag(tag, None)

	def correcturl(self, url):
		return urlparse.urljoin(self.baseurl, url)

	def download(self, url):
		url = self.correcturl(url)
		localfile = self.tempdir + md5.md5(url).hexdigest()
		try:
			file = open(localfile, "r")
			self.debug("DOWNLOADING (cached): " + url)
		except:
			self.debug("DOWNLOADING (live): " + url)
			try:
				file = urllib.urlopen(url)
			except:
				file = None
				validator("Warning! Resource " + url + " not found!")
		progress(-1)
		if file is not None:
			contents = file.read()
			file.close()
			try:
				os.mkdir(self.tempdir)
			except:
				pass
			local = open(localfile, "w")
			local.writelines(contents)
			local.close()
			return localfile
		return None

	def parse(self, filename, object):
		self.baseurl = filename
		if filename[0:7] == "http://":
			f = self.download(filename)
			filename = f
		try:
			contents = open(filename).read()
		except:
			return
		contents = re.sub("\\n", " ", contents) #### FIXME! TODO!
		contents = re.sub("\\t", " ", contents) #### FIXME! TODO!

		while 1:
			match = re.search("\\ \\ ", contents)
			if match:
				contents = re.sub("\\ \\ ", " ", contents) #### FIXME! TODO!
			else:
				break

		self.object = object

		self.feed(contents)
		self.close()
		print "!!!! linklist: ", self.object.resources

	def debug(self, str):
		print str
		#pass

	def handle_data(self, data):
		#if data == "":
		#	return
		#print "'" + data + "'"
		if len(self.statelist) > 0:
			state = self.statelist[len(self.statelist) - 1]
			self.object.part = self.statestack[len(self.statestack) - 1]
			#self.debug("# ASSIGN " + self.object.part)
		else:
			state = "[invalid]"
			stack = None
		self.debug("DATA " + data + " in " + state)

		#if self.object.attrib_pre == 0:
		#	data = re.sub("\\n", " ", data)

		""" NEW TEXT STUFF! """
		if state == "html":
			return
		if state == "title":
			self.object.title = data
			self.object.part.attributes['content'] = [data]
			return
		if state == "style":
			self.object.part.attributes['content'] = [data]
			return

		if state == "table" or state == "tr":
			return

		tags = ["body", "h1", "h2", "h3", "h4", "h5", "h6", "h7", "li", "hr"]
		tags += ["td", "a", "b", "i", "p", "font", "tt", "sub", "sup", "s"]
		tags += ["strike", "big", "small", "u", "center", "th", "form", "div"]
		tags += ["span"]
		if state in tags:
			if len(self.object.part.children) > 0:
				child = self.object.part.children[-1]
				if child.attributes['__type'] == "__text":
					child.attributes['content'] = [child.attributes['content'][0] + data]
					#print "##### STUFF DATA", data
					return
				#else:
					#print "### bah, no text"
			#else:
				#print "### len is", len(self.object.part.children)

			if data == " ":
				return

			child = renderpart()
			child.parent = self.object.part
			self.object.part.children.append(child)
			child.attributes['__type'] = "__text"
			child.attributes['content'] = [data]
			if state == "span":
				child.attributes['css::color'] = "#106f10"
		else:
			child = renderpart()
			child.parent = self.object.part
			self.object.part.children.append(child)
			child.attributes['__type'] = "__text"
			child.attributes['css::color'] = "#c0c0c0"
			child.attributes['css::strikethrough'] = 1
			child.attributes['content'] = [data]
		return

		if state == "title":
			self.object.title = data
		elif state == "body":
			self.object.part.content.append(data)
		elif state == "h1" or state == "h2" or state == "h3":
			self.object.part.content.append(data)
		elif state == "h4" or state == "h5" or state == "h6":
			self.object.part.content.append(data)
		elif state == "li":
			self.object.part.content.append(data)
		elif state == "td" or state == "th":
			self.object.part.content.append(data)
		elif state == "a":
			self.object.part.content.append(data)
		elif state == "b":
			self.object.part.content.append(data)
		elif state == "i":
			self.object.part.content.append(data)
		elif state == "p":
			self.object.part.content.append(data)
		elif state == "font":
			self.object.part.content.append(data)
		elif state == "tt":
			self.object.part.content.append(data)
		elif state == "sub":
			self.object.part.content.append(data)
		elif state == "sup":
			self.object.part.content.append(data)
		elif state == "s" or state == "strike":
			self.object.part.content.append(data)
		elif state == "big":
			self.object.part.content.append(data)
		elif state == "small":
			self.object.part.content.append(data)
		elif state == "u":
			self.object.part.content.append(data)

	def handle_starttag(self, tag, data, attributes):
		#if self.state == "li" and tag == "li":
		#	tmp = self.object.part
		#	self.handle_endtag(tag, None)
		#	tmp.ready = 1
#		if tag == "br":
#			tmp = self.object.part
#			self.handle_endtag(tag, None)
#			tmp.ready = 1

		self.debug("<" + tag + ">")
		child = renderpart()
		child.parent = self.object.part
		self.object.part.children.append(child)
		child.attributes['__type'] = tag
		for attribute in attributes:
			child.attributes[attribute[0]] = attribute[1]
			#self.debug(tag + " has " + attribute[0] + " = " + attribute[1])

		single = ["br", "img", "input", "hr"]
		if tag not in single:
			self.object.part = child
			self.statelist.append(tag)
			self.statestack.append(child)
			#self.debug("# PUSH " + str(self.object.part))

		state = tag

		self.debug(">> new state could be: " + state)

		resource = None
		if tag == "img":
			if child.attributes.has_key('src'):
				resource = child.attributes['src']
		if tag == "td" or tag == "th" or tag == "body":
			if child.attributes.has_key('background'):
				resource = child.attributes['background']
		if resource is not None:
			#if resource not in self.object.resources:
			self.object.resources.append(resource)

		if tag == "body":
			#self.debug("BODYPART!")
			self.object.bodypart = self.object.part
		if tag == "style":
			self.object.stylepart = self.object.part

		if tag == "table" or tag == "tr" or tag == "td" or tag == "th":
			self.container = table(self.container, tag)
			a = self.object.part.attributes
			if a.has_key('width'):
				w = a['width']
				if w[-1:] == "%":
					w = 800
				self.container.w = int(w)
			if a.has_key('height'):
				h = a['height']
				if h[-1:] == "%":
					h = 800
				self.container.h = int(h)
			if tag == "td" or tag == "th":
				self.container.content = self.object.part
				self.object.part.container = self.container
			print "##### TABLE #####", tag, "for", self.object.part.attributes['__type']

		self.debug(">> new state is now again: " + state)

	def handle_endtag(self, tag, foo):
		#if self.object.part.ready == 1:
		single = ["br", "img", "input", "hr"]
		if tag in single:
			self.debug("** circumvent " + tag)
			return

		self.debug("</" + tag + ">")
		self.statelist = self.statelist[:-1]
		self.statestack = self.statestack[:-1]
		#self.debug("# POP " + self.object.part)

		if self.container is not None:
			if self.container.name == tag:
				old = self.container
				self.container = self.container.parent
				if self.container == None:
					print "##### TABLE FINISHED #####", tag
					self.object.containers.append(old)

		if len(self.statelist) > 0:
			state = self.statelist[len(self.statelist) - 1]
			self.object.part = self.statestack[len(self.statestack) - 1]
			#self.debug("# ASSIGN " + self.object.part)
		else:
			state = "[invalid]"
			stack = None
		self.debug(">> new state is: " + state)

	""" OBSOLETE!!! """
	def optimizer_encapsulate(self, part):
		a = part.attributes
		if a.has_key('__type'):
			print "(optimizer) TYPE", a['__type'], "CONTENT", part.content, "ATT", part.attributes
			if a['__type'] is not "__text":
				for content in part.content:
					if content is not " ":
						p = renderpart()
						p.attributes['__type'] = "__text";
						p.attributes['content'] = [content]
						p.parent = part
						part.children.append(p)
				part.content = []
		for part in part.children:
			self.optimizer_encapsulate(part)

	def optimizer_headings(self, part):
		a = part.attributes
		if a.has_key('__type'):
			if a['__type'] == "h1":
				a['__type'] = "__h"
				a['__headingsize'] = 1
			if a['__type'] == "h2":
				a['__type'] = "__h"
				a['__headingsize'] = 2
			if a['__type'] == "h3":
				a['__type'] = "__h"
				a['__headingsize'] = 3
			if a['__type'] == "h4":
				a['__type'] = "__h"
				a['__headingsize'] = 4
			if a['__type'] == "h5":
				a['__type'] = "__h"
				a['__headingsize'] = 5
			if a['__type'] == "h6":
				a['__type'] = "__h"
				a['__headingsize'] = 6
			if a['__type'] == "h7":
				a['__type'] = "__h"
				a['__headingsize'] = 7

		for part in part.children:
			self.optimizer_headings(part)

	def optimizer_treewalk(self, part, attribute, value):
		#self.debug("(optimizer2) " + part)
		part.attributes[attribute] = value
		for part in part.children:
			self.optimizer_treewalk(part, attribute, value)

	def optimizer_treewalk_add(self, part, attribute, value):
		#self.debug("(optimizer2_add) " + part)
		if part.attributes.has_key(attribute):
			part.attributes[attribute] += value
		else:
			part.attributes[attribute] = 0
		for part in part.children:
			self.optimizer_treewalk_add(part, attribute, value)

	def optimizer_attributes(self, part):
		a = part.attributes
		if part.parent != None:
			clist = part.parent.children
		if a.has_key('__type'):
			#self.debug("(optimizer2): try to optimize " + a['__type'] + " " + a)
			if a['__type'] == "font":
				#print "FONT has these attributes", a
				for childpart in part.children:
					if a.has_key('size'):
						size = a['size']
						if size[0:1] == "+" or size[0:1] == "-":
							#print "relative size!"
							rel = size[0:1] + str(int(size) * 5)
							self.optimizer_treewalk(childpart, "css::fontsizerel", rel)
						elif size != "":
							#print "absolute size! '" + size + "'"
							self.optimizer_treewalk(childpart, "css::font-size", int(size) * 5)
					if a.has_key('color'):
						#print "** COLOR **", childpart, a['color']
						self.optimizer_treewalk(childpart, "css::color", a['color'])
					childpart.parent = part.parent
					#part.parent.children.append(childpart)
					clist.insert(clist.index(part), childpart)
				part.parent.children.remove(part)
				part = part.parent
			if a['__type'] == "__h":
				size = 48
				if a['__headingsize'] == 1:
					size = "35"
				if a['__headingsize'] == 2:
					size = "30"
				if a['__headingsize'] == 3:
					size = "25"
				if a['__headingsize'] == 4:
					size = "20"
				if a['__headingsize'] == 5:
					size = "15"
				if a['__headingsize'] == 6:
					size = "10"
				if a['__headingsize'] == 7:
					#size = "5" ### FIXME: pygame bug
					size = "10"
				childpart = part.children[0]
				if childpart is not None:
					childpart.attributes['__prebreak'] = 1
				childpart = None
				for childpart in part.children:
					self.optimizer_treewalk(childpart, "css::font-size", size)
					childpart.parent = part.parent
					#part.parent.children.append(childpart)
					clist.insert(clist.index(part), childpart)
				#if childpart is not None:
				#	childpart.attributes['__break'] = 1
				newpart = renderpart()
				newpart.attributes['__type'] = "br"
				clist.insert(clist.index(part), newpart)

				part.parent.children.remove(part)
				part = part.parent
			if a['__type'] == "big":
				for childpart in part.children:
					self.optimizer_treewalk(childpart, "css::sizerel", 1);
					#part.parent.children.append(childpart)
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				part.parent.children.remove(part)
				part = part.parent
			if a['__type'] == "small":
				for childpart in part.children:
					self.optimizer_treewalk(childpart, "css::sizerel", -1);
					#part.parent.children.append(childpart)
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				part.parent.children.remove(part)
				part = part.parent

			if a['__type'] == "form":
				for childpart in part.children:
					self.optimizer_treewalk(childpart, "__form", 1);
					#part.parent.children.append(childpart)
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				part.parent.children.remove(part)
				part = part.parent

			css = {
				"i": "css::italic",
				"b": "css::font-weight",
				"tt": "css::fixedwidth",
				"u": "css::underline",
				"s": "css::strikethrough",
				"strike": "css::strikethrough",
				"sup": "css::superscript",
				"sub": "css::subscript",
				"pre": "css:formatted",
				"center": "css::centered"
			}
			if css.has_key(a['__type']):
				for childpart in part.children:
					self.optimizer_treewalk(childpart, css[a['__type']], 1);
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				clist.remove(part)
				part = part.parent

			if a['__type'] == "ol" or a['__type'] == "dl":
				i = 0
				for childpart in part.children:
					if childpart.attributes['__type'] == "li":
						i += 1
						self.optimizer_treewalk(childpart, "css::ordered", i);
						self.optimizer_treewalk_add(childpart, "css::leftmargin", 20);
						clist.insert(clist.index(part), childpart)
						childpart.parent = part.parent
				part.parent.children.remove(part)
				part = part.parent
			if a['__type'] == "ul":
				for childpart in part.children:
					self.optimizer_treewalk(childpart, "css::unordered", 1);
					self.optimizer_treewalk_add(childpart, "css::leftmargin", 20);
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				part.parent.children.remove(part)
				part = part.parent

			if a['__type'] == "a":
				if a.has_key('href'):
					for childpart in part.children:
						self.optimizer_treewalk(childpart, "href", a['href']);
						clist.insert(clist.index(part), childpart)
						childpart.parent = part.parent
				elif a.has_key('name'):
					for childpart in part.children:
						self.optimizer_treewalk(childpart, "name", a['name']);
						clist.insert(clist.index(part), childpart)
						childpart.parent = part.parent
				else:
					validator("A is missing HREF or NAME")
				part.parent.children.remove(part)
				part = part.parent

			if a['__type'] == "tr":
				a['__break'] = 1

			if a['__type'] == "li" or a['__type'] == "dd" or a['__type'] == "dt":
				childpart = renderpart()
				if a.has_key('css::unordered'):
					childpart.attributes['__type'] = "bullet"
					childpart.attributes['css::bullettype'] = "round"
				elif a.has_key('css::ordered'):
					childpart.attributes['__type'] = "__text"
					childpart.attributes['content'] = [str(a['css::ordered']) + "."]
				else:
					validator("LI is not enclosed by OL or UL")
				clist.insert(clist.index(part), childpart)
				childpart.attributes['__prebreak'] = 1
				if a.has_key('css::leftmargin'):
					childpart.attributes['css::leftmargin'] = a['css::leftmargin']
				childpart.parent = part.parent
				childpart = None
				for childpart in part.children:
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				clist.remove(part)
				part = part.parent
				if childpart != None:
					childpart.attributes['__break'] = 1

		for childpart in part.children:
			self.optimizer_attributes(childpart)

	def dump(self, part, level):
		a = part.attributes
		if a.has_key('__type'):
			tag = a['__type']
		else:
			tag = "(unknown)"
		tagline = ""
		for i in range(0, level):
			tagline += " "
		tagline += "<" + tag
		for key in a:
			if key != "__type":
				value = a[key]
				if type(value) == type([]):
					value = "[list:" + value[0] + "]"
				if type(value) == type(0):
					value = str(value)
				tagline += " " + key + "=\"" + value + "\""
		tagline += ">"
		print tagline
		if len(part.surfaces) > 0:
			print "# surfaces", part.surfaces
		if part.container is not None:
			print "() container", part.container
		for childpart in part.children:
			self.dump(childpart, level + 1)
		if len(part.children) > 0:
			tagline = ""
			for i in range(0, level):
				tagline += " "
			tagline += "</" + tag + ">"
			print tagline

	def optimizer_styleapply(self, part, style, active):
		a = part.attributes
		if a.has_key('__type'):
			print "CSS DETECTION: " + part.attributes['__type']
			if a.has_key('class'):
				if a['class'] == style.ident:
					if style.element == None or style.element == a['__type']:
						active = 1
						print "CSS IS ACTIVE!"
			if active:
				for s in style.styles.keys():
					print "CSS APPLY!"
					a["css::" + s] = style.styles[s]

		for childpart in part.children:
			self.optimizer_styleapply(childpart, style, active)

	def optimizer_style(self, part, stylelist):
		if stylelist == None:
			return
		print "*** CASCADING STYLE SHEETS ***"
		for s in stylelist:
			print "NAME", s.ident, "ELEM", s.element, "STYLES", s.styles
			self.optimizer_styleapply(part, s, 0)

	def stylesheetparser(self, part):
		if part == None:
			return
		if not part.attributes.has_key('content'):
			return

		definition = part.attributes['content']
		tokens = definition[0].split(" ")
		state = "ident"
		stylelist = []
		style = None
		attribute = None
		for t in tokens:
			if t == "":
				continue

			if state == "ident":
				state = "element-or-brace"
				style = stylesheet()
				ident = t
				if ident[0] == ".":
					ident = ident[1:]
				style.ident = ident
				stylelist.append(style)
			elif state == "element-or-brace":
				if t == "{":
					state = "attribute"
				else:
					state = "brace"
					style.element = t
			elif state == "brace":
				state = "attribute"
			elif state == "attribute":
				if t == "}":
					state = "ident"
				else:
					state = "value"
					attribute = t[:-1]
			elif state == "value":
				state = "attribute"
				style.styles[attribute] = t[:-1]
			else:
				print "Invalid state! " + state

		return stylelist

def reverse(s):
	rs = ""
	for x in s:
		rs = x + rs
	return rs

def hex2bin(hex):
	number = 0
	factor = 1
	for x in reverse(hex):
		n = x
		if n == 'a' or n == "A":
			n = '10'
		if n == 'b' or n == "B":
			n = '11'
		if n == 'c' or n == "C":
			n = '12'
		if n == 'd' or n == "D":
			n = '13'
		if n == 'e' or n == "E":
			n = '14'
		if n == 'f' or n == "F":
			n = '15'
		number = number + int(n) * factor
		factor = factor * 16
	return number

def getcolor(str):
	map = {
		"blue": "#0000ff",
		"yellow": "#ffff00",
		"teal": "#207090"
	}

	if str.lower() in map.keys():
		rgbcolor = map[str.lower()]
	else:
		if str[0:1] != "#":
			str = "#" + str
		rgbcolor = str
	colors = (hex2bin(rgbcolor[1:3]), hex2bin(rgbcolor[3:5]), hex2bin(rgbcolor[5:7]))
	return colors

progresscount = 0
progresstotal = 0

def progress(p):
	global navpage
	global progresscount
	global progresstotal

	if p < 0:
		progresscount -= 1
	else:
		progresscount = p
		progresstotal = p
	print "-- progress --", progresscount

	if progresstotal > 0:
		bar = pygame.Surface((800, 10))
		bar.fill((0, 0, 140))
		bar.fill((0, 0, 200), (0, 0, 800 - 800 * progresscount / progresstotal, 10))

		navpage.blit(bar, (0, 0))
		screen.blit(navpage, (0, 0))
		pygame.display.update()

def containerrender(part):
	a = part.attributes

	colors = None
	img = None
	if a.has_key('bgcolor'):
		rgbcolor = a['bgcolor']
		colors = getcolor(rgbcolor)
	else:
		colors = (255, 255, 255)
	if a.has_key('background'):
		bg = parser.download(a['background'])
		img = pygame.image.load(bg)
		img = pygame.transform.scale(img, (800, 600))

	x = None
	if a['__type'] == "th" or a['__type'] == "td" or a['__type'] == "body":
		w = 100
		h = 100
		if a['__type'] == "body":
			w = 800
			h = 6000
		if a.has_key('width'):
			wx = a['width']
			if type(wx) == type(""):
				if wx[-1:] == "%":
					wx = "200" ### FIXME!
			w = int(wx)
		if a.has_key('height'):
			hx = a['height']
			if type(hx) == type(""):
				if hx[-1:] == "%":
					hx = "200" ### FIXME!
			h = int(hx)
		if w > 0 and h > 0:
			x = pygame.Surface((w, h))
			x.fill(colors)

			for j in range(0, x.get_height()):
				x.set_at((0, j), (0, 0, 0))
				x.set_at((x.get_width() - 1, j), (0, 0, 0))
			for i in range(0, x.get_width()):
				x.set_at((i, 0), (0, 0, 0))
				x.set_at((i, x.get_height() - 1), (0, 0, 0))

			#s = surface(x)
	return x

class fontobject:
	def __init__(self, font, rfontsize):
		self.font = font
		self.rfontsize = rfontsize

def getfont(part):
	a = part.attributes

	fontsize = 15
	if a.has_key('css::font-size'):
		param = str(a['css::font-size'])
		if len(param) > 2 and param[-2:] == "pt":
			size = param[:-2]
			fontsize = int(size)
		else:
			fontsize = int(param)
	if a.has_key('css::fontsizerel'):
		relsize = str(a['css::fontsizerel'])
		sign = relsize[0:1]
		amount = relsize[1:]
		if sign == "-":
			fontsize -= int(amount)
		elif sign == "+":
			fontsize += int(amount)
	rfontsize = fontsize
	if a.has_key('css::superscript'):
		fontsize = int(fontsize * 0.7)
	if a.has_key('css::subscript'):
		fontsize = int(fontsize * 0.7)
	if fontsize < 10:
		fontsize = 10 # FIXME! pygame bug
	font = pygame.font.Font(None, fontsize)

	if a.has_key('css::font-weight'):
		font.set_bold(1)
	if a.has_key('css::italic'):
		font.set_italic(1)
	if a.has_key('css::underline'):
		font.set_underline(1)

	return fontobject(font, rfontsize)

def getfontcolors(part):
	a = part.attributes

	if a.has_key('css::color'):
		rgbcolor = a['css::color']
		colors = getcolor(rgbcolor)
	else:
		colors = (0, 0, 0)
	if a.has_key('href'):
		colors = (0, 0, 255)

	return colors

def fontrender(part, text, fontobject = None, colors = None):
	a = part.attributes

	if fontobject == None:
		fontobject = getfont(part)
	if colors == None:
		colors = getfontcolors(part)

	font = fontobject.font
	rfontsize = fontobject.rfontsize
	fontsize = font.get_height()

	textline = font.render(text, 1, colors)
	if a.has_key('css::strikethrough'):
		j = textline.get_height() / 2
		for i in range(0, textline.get_width()):
			textline.set_at((i, j), colors)
	if a.has_key('css::superscript') or a.has_key('css::subscript'):
		tmp = pygame.Surface((textline.get_width(), rfontsize))
		tmp.fill((255, 255, 255)) ### ALPHA
		if a.has_key('css::superscript'):
			tmp.blit(textline, (0, 0))
		else:
			tmp.blit(textline, (0, rfontsize - fontsize))
		textline = tmp
	if a.has_key('css::centered'):
		#tmp = pygame.Surface((width, rfontsize)) ### FIXME! centering
		tmp = pygame.Surface((textline.get_width(), rfontsize))
		tmp.fill((255, 255, 255)) ### ALPHA
		tmp.blit(textline, (tmp.get_width() / 2 - textline.get_width() / 2, 0))
		textline = tmp

	return textline

def render(page, part):
	if part == None:
		return
	a = part.attributes
	if a.has_key('__type'):
		#print "TYPE", a['__type'], "CONTENT", part.content, "ATT", part.attributes
		if a['__type'] == "img":
			if a.has_key('src'):
				src = parser.download(a['src'])
				try:
					img = pygame.image.load(src)
					width = img.get_width()
					height = img.get_height()
					if a.has_key('width'):
						width = int(a['width'])
					if a.has_key('height'):
						height = int(a['height'])
					img = pygame.transform.scale(img, (width, height))
				except:
					width = 20
					height = 20
					if a.has_key('width'):
						width = int(a['width'])
					if a.has_key('height'):
						width = int(a['height'])
					img = pygame.Surface((width, height))
					img.fill((255, 0, 0))
				s = surface(img)
				part.surfaces.append(s)
			else:
				validator("IMG is missing SRC")
		#elif a['__type'] == "li":
		#	font = pygame.font.Font(None, 20)
		#	try:
		#		text = "* " + part.content[0]
		#		textline = font.render(text, 1, (0, 0, 0))
		#		part.surfaces.append(textline)
		#	except:
		#		pass
		elif a['__type'] == "bullet":
			if a.has_key('css::color'):
				rgbcolor = a['css::color']
				colors = getcolor(rgbcolor)
			else:
				colors = (0, 0, 0)
			img = pygame.Surface((10, 10))
			img.fill(colors)
			s = surface(img)
			part.surfaces.append(s)
		elif a['__type'] == "hr":
			img = pygame.Surface((page.get_width() - 20, 5))
			img.fill((255, 200, 200))
			s = surface(img)
			part.surfaces.append(s)
		elif a['__type'] == "input":
			type = "text"
			if a.has_key('type'):
				type = a['type']
			if type == "text":
				img = pygame.Surface((300, 20))
				img.fill((255, 200, 200))
			elif type == "checkbox":
				img = pygame.Surface((20, 20))
				img.fill((255, 200, 200))
				if a.has_key('checked'):
					if a['checked'] == "CHECKED":
						img.fill((0, 0, 0), (5, 5, 10, 10))
			elif type == "submit":
				title = "Submit"
				if a.has_key('value'):
					title = a['value']
				fontsize = 20
				font = pygame.font.Font(None, fontsize)
				text = font.render(title, 1, (0, 0, 0))
				img = pygame.Surface((text.get_width(), text.get_height()))
				img.fill((255, 200, 200))
				img.blit(text, (0, 0))
			else:
				img = pygame.Surface((20, 20))
				img.fill((255, 200, 200))
			s = surface(img)
			part.surfaces.append(s)
		elif a['__type'] == "body":
			#or a['__type'] == "td" or a['__type'] == "th":
#			colors = None
#			img = None
#			if a.has_key('bgcolor'):
#				rgbcolor = a['bgcolor']
#				#print "** BGCOLOR", rgbcolor
#				colors = getcolor(rgbcolor)
#				#print colors
#			else:
#				colors = (255, 255, 255)
#			for j in range(0, page.get_height()):
#				page.set_at((0, j), (0, 0, 0))
#				page.set_at((page.get_width() - 1, j), (0, 0, 0))
#			for i in range(0, page.get_width()):
#				page.set_at((i, 0), (0, 0, 0))
#				page.set_at((i, page.get_height() - 1), (0, 0, 0))
#			if a.has_key('background'):
#				bg = parser.download(a['background'])
#				img = pygame.image.load(bg)
#				img = pygame.transform.scale(img, (800, 600))

			img = containerrender(part)
			font = pygame.font.Font(None, 20)
			for text in part.content:
				textline = font.render(text, 1, (0, 0, 0), colors)
				s = surface(textline)
				part.surfaces.append(s)

			if a['__type'] == "body":
				if img is not None:
					page.blit(img, (0, 0))
				elif colors is not None:
					page.fill(colors)
			else:
				w = 100
				h = 100
				if a.has_key('width'):
					wx = a['width']
					if wx[-1:] == "%":
						wx = "200" ### FIXME!
					w = int(wx)
				if a.has_key('height'):
					hx = int(a['height'])
					if hx[-1:] == "%":
						hx = "200" ### FIXME!
					h = int(hx)
				x = pygame.Surface((w, h))
				x.fill(colors)
				s = surface(x)
				part.surfaces.append(s)
			#if a['__type'] == "td":
			#	frame = pygame.Surface((200, 200))
			#	frame.fill((240, 240, 240))
			#	part.surfaces.append(frame)
		elif a['__type'] == "__text":
			fontobject = getfont(part)
			colors = getfontcolors(part)
			font = fontobject.font
			rfontsize = fontobject.rfontsize

			width = page.get_width() - 100 ### FIXME: containers
			textstrategy = "words" ### words or width
			#print "BEFORE", a['content']
			for text in a['content']:
				(x, y) = font.size(text)
				#lines = int(x / width) + 1
				txt = text
				while len(txt):
					if textstrategy == "words":
						txt2 = ""
						ready = 0
						while not ready:
							txt2 += txt[0:1]
							txt = txt[1:]
							if txt2[-1:] == " ":
								ready = 1
							if len(txt) == 0:
								ready = 1
					elif textstrategy == "width":
						txt2 = txt
						while font.size(txt2)[0] > width and len(txt2):
							txt2 = txt2[:-1]
						txt = txt[len(txt2):]
						if len(txt2) == 0:
							txt2 = txt
							txt = ""
					else:
						print "Error! Unknown text strategy"
					a['content'].insert(a['content'].index(text), txt2)
				a['content'].remove(text)
			#print "AFTER", a['content']

			for text in a['content']:
				#text = "Gr__e 1"
				#text = "Gr_"
				#print "*** [critical] render", text, colors, fontsize
				if text != " ":
					textline = fontrender(part, text, fontobject, colors)
					s = surface(textline)
					part.surfaces.append(s)
					#print "#### append textline surface"
	for part in part.children:
		render(page, part)

class hyperlink:
	def __init__(self, surface, x, y, part, xoff, yoff, page):
		self.surface = surface
		self.x = x
		self.y = y
		self.part = part
		self.xoff = xoff
		self.yoff = yoff
		self.page = page

def getinput(h):
	url = ""
	#pygame.key.set_repeat()
	shadow = {}
	shift = 0
	while 1:
		pygame.event.pump()
		keystates = pygame.key.get_pressed()
		for keycode in range(0, len(keystates)):
			key = pygame.key.name(keycode)
			if not shadow.has_key(keycode):
				shadow[keycode] = 0
			if keystates[keycode] and not shadow[keycode]:
				shadow[keycode] = 1
				if key == "escape":
					return ""
				elif key == "return":
					return url
				elif key == "backspace":
					url = url[:-1]
				elif key == "right shift":
					shift = 1
				else:
					if shift == 1:
						if key == ".":
							key = ":"
						if key == "7":
							key = "/"
					url = url + key
			if not keystates[keycode] and shadow[keycode]:
				shadow[keycode] = 0
				if key == "right shift":
					shift = 0

		h.page.blit(h.part.surfaces[0].surface, (h.part.x, h.part.y))
		if url is not "":
			font = pygame.font.Font(None, 20)
			textline = font.render(url, 1, (0, 0, 255))
			h.page.blit(textline, (h.part.x, h.part.y))
		screen.blit(h.page, (h.xoff, h.yoff))
		pygame.display.update()

zoomfactor = 100

globalx = 0
globaly = 0
globalpage = None

class dummypage:
	def __init__(self, w):
		self.w = w

	def get_width(self):
		return self.w

class displayer:
	def __init__(self):
		self.edgex = 10
		self.edgey = 10
		self.depth = 0

		self.reset()

	def reset(self):
		self.posx = self.edgex
		self.posy = self.edgey
		self.oldmaxy = 0
		self.leftmargin = 0
		self.totalheight = 0

	def display(self, page, part, construction, reset = 1):
		global globalx, globaly
		global zoomfactor

		if part == None:
			return

		if reset == 1:
			self.reset()
			print "reset image:", page.get_width()

		a = part.attributes

		#	if a.has_key('__type'):
		#		for i in range(0, self.depth):
		#			print "",
		#		print "DISPLAY PART", part, a['__type'],
		#		for s in part.surfaces:
		#			print s,
		#		print
		#	else:
		#		print "DISPLAY UNKNOWN PART"

		dobreak = 0

		types = ["ol", "br", "p", "table"]
		if a.has_key('__type'):
			if a['__type'] in types:
				dobreak = 1

		if a.has_key('__prebreak'):
			if a['__prebreak'] == 1:
				dobreak = 1

		if a.has_key('css::leftmargin'):
			self.leftmargin = a['css::leftmargin']
		else:
			self.leftmargin = 0

		if dobreak:
			#print "** force pre-break", posy, "+=", oldmaxy + 10
			self.posx = self.edgex
			self.posy += self.oldmaxy + zoomfactor / 10
			self.oldmaxy = 0

		if reset == 0:
			if part.container is not None:
				print "# containersurfaces", len(part.surfaces)
				d = displayer()
#				w = part.container.w - part.container.x
#				rpage = dummypage(w) # FIXME: relative values
#				d.display(rpage, part, 1)
#				print "r-totalheight =", d.totalheight, "width =", w
#				print "r-BLITTER NOW!"
#				d.totalheight += 30 # FIXME: kludge
#				w += 30
#				part.container.h = d.totalheight
#				part.attributes['width'] = w
#				part.attributes['height'] = part.container.h
				#print "containerrender", part.attributes['width'], part.attributes['height']
				rpage = containerrender(part)
				if rpage is not None:
					d.blitter(rpage, part, 1, 1)
					part.surfaces = [surface(rpage)]

		maxx = 0
		lastx = 0
		maxy = 0
		for s in part.surfaces:
			#if maxy > 0:
			#	posy += maxy
			if lastx > 0:
				self.posx += lastx
			if s.original == None:
				o = pygame.Surface((s.surface.get_width(), s.surface.get_height()))
				o.fill((255, 255, 255)) ### ALPHA
				o.blit(s.surface, (0, 0))
				s.original = o
				#s.original = s.surface
			if zoomfactor != 100:
				zoomx = (s.original.get_width() * zoomfactor) / 100
				zoomy = (s.original.get_height() * zoomfactor) / 100
				print "zoom", s.original.get_width(), s.original.get_height(), "=>", zoomx, zoomy, zoomfactor
				s.surface = pygame.transform.scale(s.original, (zoomx, zoomy))
			else:
				o = pygame.Surface((s.original.get_width(), s.original.get_height()))
				o.fill((255, 255, 255)) ### ALPHA
				o.blit(s.original, (0, 0))
				s.surface = o
				#s.surface = s.original

			if self.posx + s.surface.get_width() > page.get_width() - self.leftmargin + 10: ### FIXME: containers
				#print "** width break **"
				self.posx = self.edgex
				self.posy += self.oldmaxy + zoomfactor / 10
				self.oldmaxy = 0

			rposx = self.posx + self.leftmargin
			part.x = rposx
			part.y = self.posy
			s.x = rposx
			s.y = self.posy

			if construction == 1:
				if a.has_key('href'):
					h = hyperlink(s, rposx, self.posy, part, globalx, globaly, globalpage)
					links.append(h)
				if a.has_key('__type'):
					if a['__type'] == "input":
						part.attributes['href'] = "internal:input"
						h = hyperlink(s, rposx, self.posy, part, globalx, globaly, globalpage)
						links.append(h)

			#print "BLIT posx=", rposx, "posy=", posy, "width=", s.get_width(), "height=", s.get_height()
			if s.surface.get_width() > maxx:
				maxx = s.surface.get_width() + 1
			lastx = s.surface.get_width() + 1
			if s.surface.get_height() > maxy:
				maxy = s.surface.get_height() + 1

			if self.posy + s.surface.get_height() > self.totalheight:
				self.totalheight = self.posy + s.surface.get_height()

			#posx = posx + s.get_width() + 1

		dobreak = 0

		types = ["p", "br"]
		if a.has_key('__type'):
			if a['__type'] in types:
				dobreak = 1
	#	if a['__type'] == "li":
	#		dobreak = 1

		if a.has_key('__break'):
			if a['__break'] == 1:
				dobreak = 1

		#if a.has_key('css::leftmargin'):
		#	self.leftmargin = a['css::leftmargin']
		#else:
		#	self.leftmargin = 0

		subrender = 1
		if reset == 0:
			if part.container is not None:
				subrender = 0

		if subrender:
			for childpart in part.children:
				self.depth += 1
				maxy = self.display(page, childpart, construction, 0)
				self.depth -= 1

		if dobreak:
			#print "** force break", posy, "+=", maxy + 10
			self.posx = self.edgex
			self.posy += maxy + zoomfactor / 10
		else:
			self.posx += maxx + zoomfactor / 10

		if maxy > 0:
			self.oldmaxy = maxy

		return maxy

	def blitter(self, page, part, construction, override):
		if part == None:
			return

		a = part.attributes

		counter = 0
		for s in part.surfaces:
			rposx = s.x
			posy = s.y
			if a['__type'] == "__textXXX":
				tmp = fontrender(part, a['content'][counter])
				page.blit(tmp, (rposx, posy))
			else:
				page.blit(s.surface, (rposx, posy))
			print "blitted", s, "in", part.attributes['__type']
			counter += 1

			if construction == 0:
				if part.activated == 1:
					for i in range(0, s.surface.get_width()):
						try:
							page.set_at((rposx + i, posy + s.surface.get_height() - 1), (0, 0, 0))
						except:
							pass
			if construction == 1:
				if a.has_key('__type'):
					if a['__type'] == 'input':
						try:
							for j in range(0, s.surface.get_height()):
								page.set_at((rposx, posy + j), (0, 0, 0))
								page.set_at((rposx + s.surface.get_width() - 1, posy + j), (0, 0, 0))
							for i in range(0, s.surface.get_width()):
								page.set_at((rposx + i, posy), (0, 0, 0))
								page.set_at((rposx + i, posy + s.surface.get_height() - 1), (0, 0, 0))
						except:
							pass

		if part.container == None or override == 1:
			if a.has_key('__type'):
				print "BLIT children of", a['__type'], "container:", part.container
			for c in part.children:
				self.blitter(page, c, construction, override)
		else:
			print "BLIT directly", a['__type'], "container:", part.container

parser = myparser()

pygame.init()
screen = pygame.display.set_mode((800, 600))
#page = pygame.Surface((800, 560))
page = pygame.Surface((800, 6000))

font = pygame.font.Font(None, 48)

links = []
history = []

navigation = """
<html>
<body bgcolor='#ffffff'>
<a href='internal:back'>Zurück</a>
<b> | </b>
<a href='internal:forward'>Vor</a>
<b> | </b>
<input type='text' name='internal:url'>URL</input>
<b> | </b>
<a href='internal:clear'>Cache leeren</a>
</body>
</html>
"""
navigation = "" ##### temp! FIXME TODO
#navigation = re.sub("\\n", " ", navigation)
navpage = pygame.Surface((800, 40))
globalpage = navpage
object = renderobject()
parser.object = object
parser.feed(navigation)
parser.close()
render(navpage, object.bodypart)
d = displayer()
d.display(navpage, object.bodypart, 1)
screen.blit(navpage, (0, 0))
pygame.display.update()

navlinks = []
for l in links:
	navlinks.append(l)
globalpage = page

url = ""
#url = "example.html"
print sys.argv
if len(sys.argv) > 1:
	url = sys.argv[1]
history.append(url)

while url is not None:
	links = []
	for l in navlinks:
		links.append(l)

	print "***** LINKS", links

	object = renderobject()
	parser.parse(url, object)
	progress(len(object.resources))

	print "*** containers ***", len(object.containers)
	for c in object.containers:
		c.dump()
		c.normalize("vertical")
		c.cons()

#	parser.optimizer_encapsulate(object.root)
	style = parser.stylesheetparser(object.stylepart)
	parser.optimizer_style(object.root, style)
	parser.optimizer_headings(object.root)
	parser.optimizer_attributes(object.root)
	parser.dump(object.root, 0)
	pygame.display.set_caption(object.title)

	globalx = 0
	globaly = 40
	render(page, object.bodypart)

	print "*** containers (after render) ***", len(object.containers)
	for c in object.containers:
		for d in c.children:
			for e in d.children:
				part = e.content
				dd = displayer()
				w = part.container.w - part.container.x
				rpage = dummypage(w) # FIXME: relative values
				dd.display(rpage, part, 1)
				e.h = dd.totalheight

	for c in object.containers:
		c.normalize("horizontal")

	for c in object.containers:
		for d in c.children:
			for e in d.children:
				part = e.content
				w = part.container.w - part.container.x
				h = part.container.h
				w += 20
				h += 20
				part.attributes['width'] = w
				part.attributes['height'] = h

	for c in object.containers:
		c.finish()
		c.dump()

	#parser.dump(object.root, 0) ### temp
	d = displayer()
	print "(1) GO DISPLAY WITH", d.posy
	d.display(page, object.bodypart, 1)
	print "totalheight =", d.totalheight
	print "BLITTER NOW!"
	d.blitter(page, object.bodypart, 1, 0)

	scrollx = 0
	scrolly = 0
	scrolled = 0
	zoomed = 0

	screen.blit(page, (0, 40), (scrollx, scrolly, scrollx + 800, scrolly + 560))

	pygame.display.update()

	loop = 1
	activelink = None
	oldbutton = 0
	while loop:
		link = None

		pygame.event.pump()
		key = pygame.key.get_pressed()
		mods = pygame.key.get_mods()
		if key[pygame.K_ESCAPE]:
			loop = 0
			url = None
		if key[pygame.K_PAGEDOWN]:
			scrolly += 50
			scrolled = 1
		if key[pygame.K_PAGEUP]:
			scrolly -= 50
			scrolled = 1
		if key[pygame.K_DOWN]:
			scrolly += 3
			scrolled = 1
		if key[pygame.K_UP]:
			scrolly -= 3
			scrolled = 1
		if key[pygame.K_RIGHT]:
			if mods & (pygame.KMOD_LALT | pygame.KMOD_RALT):
				link = "internal:forward"
			else:
				scrollx += 3
				scrolled = 1
		if key[pygame.K_LEFT]:
			if mods & (pygame.KMOD_LALT | pygame.KMOD_RALT):
				link = "internal:back"
			else:
				scrollx -= 3
				scrolled = 1
		if key[pygame.K_MINUS] or key[pygame.K_KP_MINUS]:
			if zoomfactor > 10:
				zoomfactor -= 10
				zoomed = 1
		if key[pygame.K_PLUS] or key[pygame.K_KP_PLUS]:
			if zoomfactor < 400:
				zoomfactor += 10
				zoomed = 1

		if zoomed:
			#page.fill((255, 255, 255))
			page = containerrender(object.bodypart)
			d = displayer()
			d.display(page, object.bodypart, 1)
			d.blitter(page, object.bodypart, 1, 0)
			screen.blit(page, (0, 40), (scrollx, scrolly, scrollx + 800, scrolly + 560))
			pygame.display.update()
			zoomed = 0

		if scrolled:
			xscrollx = scrollx
			xscrolly = scrolly
			if xscrollx >= 800 - 800:
				xscrollx = 800 - 800
			if xscrolly >= 6000 - 560:
				xscrolly = 6000 - 560
			if xscrollx < 0:
				xscrollx = 0
			if xscrolly < 0:
				xscrolly = 0
			screen.fill((0, 0, 0))
			#print "rect", scrollx, xscrollx + 800
			screen.blit(page, (0, 40), (scrollx, scrolly, xscrollx + 800, xscrolly + 560))
			pygame.display.update()

		(mx, my) = pygame.mouse.get_rel()
		if mx != 0 or my != 0:
			(mx, my) = pygame.mouse.get_pos()
			mx = mx + scrollx
			my = my + scrolly
			#print "MOUSE!", mx, my
			if activelink is not None:
				activelink.part.activated = 0
				posx = activelink.x # FIXME!
				posy = activelink.y
				print "(2) GO DISPLAY WITH", posy
				d = displayer()
				d.display(activelink.page, activelink.part, 0)
				#screen.blit(activelink.page, (activelink.xoff, activelink.yoff))
				#pygame.display.update()

				activelink = None
				print "mouse reset"
			for h in links:
				s = h.surface.surface
				if mx < (h.x + h.xoff) + s.get_width() and my < (h.y + h.yoff) + s.get_height():
					if mx >= h.x and my >= h.y:
						print "-->", s, "at", h.x, h.y
						h.part.activated = 1
						posx = h.x
						posy = h.y # FIXME!
						d = displayer()
						d.display(h.page, h.part, 0)
						activelink = h

						#screen.blit(h.page, (h.xoff, h.yoff))
						#pygame.display.update()

		(button1, button2, button3) = pygame.mouse.get_pressed()
		if button1 is not oldbutton:
			oldbutton = button1
			if button1 == 1:
				if activelink is not None:
					link = activelink.part.attributes['href']
					print "HYPERREF", link

		if link is not None:
			if link == "internal:back":
				if len(history) > 1:
					url = history[len(history) - 2]
					loop = 0
					print "*** GET", url, "FROM HISTORY"
			elif link == "internal:forward":
				pass
			elif link == "internal:input":
				newurl = getinput(activelink)
				if newurl is not "":
					url = newurl
					loop = 0
					history.append(url)
			else:
				url = parser.correcturl(link)
				loop = 0
				history.append(url)


