#!/usr/bin/ruby
#
# Ruby implementation of the GGZComm source generator
# Copyright (C) 2002 - 2006 Josef Spillner <josef@ggzgamingzone.org>
# Published under GNU GPL conditions

# Version number
$version = 0.4

# Use xml parsing library
require 'xmlparser'

# Helper method to return canonical (mangled) variable names
def varname(s)
	return s.sub(":", "__")
end

# Storage class for a single <event> and its contents
# Contains a tree of data definitions, sequences and conditions
class GGZCommEvent
	def initialize(name, section)
		@datalist = Array.new
		@evallist = nil
		@name = name
		@section = section
	end

	def add(var, type, min, max)
		@datalist.push [var, type, [min, max]]
	end

	def sequence(count, mincount, maxcount)
		@datalist.push ["%SEQ", count, [mincount, maxcount]]
	end

	def endsequence
		@datalist.push "%ENDSEQ"
	end

	def evaluate
		@datalist.push ["%EVAL", [], 0]
	end

	def endevaluate
		@datalist.push "%ENDEVAL"
	end

	def setevallist(list)
		@evallist = list
	end

	def name
		return @name
	end

	def section
		return @section
	end

	def datalist
		return @datalist
	end

	def evallist
		return @evallist
	end

	def decl(definition, qtmode)
		x = ""
		seq = 0
		count = 0
		@datalist.each do |data, type, option|
			case data
				when "%SEQ"
					seq += 1
					count = type
				when "%ENDSEQ"
					seq -= 1
					count = 0
				when "%EVAL"
					#seq += 1
				when "%ENDEVAL"
					#seq -= 1
				else
					if x.length > 0 then
						if not definition then
							x = x + ", "
						end
					end
					case type
						when "string"
							if qtmode then
								type = "QString"
							else
								type = "char*"
							end
						when "byte"
							if qtmode then
								if qtmode == "qt4"
									type = "qint8"
								else
									type = "Q_INT8"
								end
							else
								type = "char"
							end
					end
					if definition then
						x = x + "\t"
					end
					x = x + type
					#for i in (1..seq)
					#	x = x + "*"
					#end
					for i in (1..seq)
						#data += "[" + count.to_s + "]";
						if count.to_i.to_s == count then
							data += "[" + count.to_s + "]";
						else
							data += "[100]";
							# FIXME: boundaries (maxcount?)
						end
					end
					if not definition then
						x = x + " " + data
					else
						x = x + " " + data + ";\n"
					end
			end
		end
		if x == "" and not definition then
			x = "void"
		end
		return x
	end

	def network(role, mode, library)
		x = ""
		seq = 0
		maxseq = 0

		@datalist.each do |data, type, option|
			case data
				when "%SEQ"
					seq += 1
					if seq > maxseq then maxseq = seq end
				when "%ENDSEQ"
					seq -= 1
			end
		end
		seq = 0

		for i in (1..maxseq)
			x += "\tint i" + i.to_s + ";\n"
		end
		if maxseq > 0 then
			x += "\n"
		end

		esname = "int"
		data = @name
		if @section == role then
			x += "\tret = ggz_write_" + esname + "(fd, " + data + ");\n"
			x += "\tif(ret < 0) ggzcomm_error();\n"
		else
			if mode == "debug":
				x += "#ifdef GGZCOMM_DEBUG\n"
				x += "\tprintf(\"(ggzcomm) reading message '" + @name + "'...\\n\");\n"
				x += "#endif\n"
				x += "\n"
			end
		end

		myvars = Hash.new

		parts = 0
		index = ""
		@datalist.each do |data, type, option|
			indent = ""
			for i in (1..seq)
				indent += "\t"
			end
			case data
				when "%SEQ"
					seq += 1
					count = type
					maxcount = option
					if myvars[count] then
						if not maxcount then
							error "Sequence using variable '" + count + "' needs maxcount argument."
						end
						count = "variables." + count
					end
					vname = "i" + seq.to_s
					#x = x + indent + "\tif(" + count.to_s + " > " + maxcount.to_s + ") ggzcomm_error();\n"
					x = x + indent + "\tfor(" + vname + " = 0; " + vname + " < " + count.to_s + "; " + vname + "++)\n"
					x = x + indent + "\t{\n"
					index += "[" + vname + "]"
				when "%ENDSEQ"
					seq -= 1
					count = 0
					x = x + indent + "}\n"
					#index = ""
					index.gsub!(/\[(\w+)\]$/, "")
				when "%EVAL"
					seq += 1
					x = x + indent + "\tif("
					i = 0
					@evallist.each do |name, result, type|
						if type == "equal"
							type = "=="
						elsif type == "unequal"
							type = "!="
						elsif type == "smaller"
							type = "<"
						elsif type == "larger"
							type = ">"
						end
						if i > 0 then
							x = x + " || "
						end
						x = x + "(variables." + name + index + " " + type + " " + varname(result) + ")"
						i += 1
					end
					x = x + ")\n"
					x = x + indent + "\t{\n"
				when "%ENDEVAL"
					seq -= 1
					x = x + indent + "}\n"
				else
					myvars[data] = 1
					esname = type
					case type
						when "string"
							type = "char*"
							if @section == role then
								esname = "string"
							else
								esname = "string_alloc"
							end
						when "byte"
							type = "char"
							esname = "char"
					end
					if @section == role then
						x += indent + "\tret = ggz_write_" + esname + "(fd, variables." + data + index + ");\n"
					else
						if library == "dio"
							x += indent + "\tggz_dio_get_" + esname + "(dio, &variables." + data + index + ");\n"
						else
							x += indent + "\tret = ggz_read_" + esname + "(fd, &variables." + data + index + ");\n"
						end
					end
					parts += 1
					if library == "easysock"
						x += indent + "\tif(ret < 0) ggzcomm_error();\n"
					end
			end
		end

		if @section != role then
			if mode == "debug":
				x += "#ifdef GGZCOMM_DEBUG\n"
				x += "\tprintf(\"(ggzcomm) - message was read, " + parts.to_s + " parts\\n\");\n"
				x += "#endif\n"
				x += "\n"
			end
			x += "\tif(notifier_func) (notifier_func)(" + name + ");\n"
		end

		return x
	end

	def network_cpp_xml(role, engine)
		x = ""
		##seq = 0
		cseq = 0

		myvars = Hash.new
		maxcounts = Hash.new

		index = ""
		@datalist.each do |data, type, option|
			indent = "\t\t\t\t"
			##for i in (1..seq)
			##	indent += "\t"
			##end
			case data
				when "%SEQ"
					##seq += 1
					cseq += 1
					if type.to_i.to_s == type then
						count = type
						# FIXME: go here when type is a number (constant) - better way?
					else
						count = "msg_" + @name + "." + type
					end
					mincount, maxcount = option
					if myvars[count] then
						if not maxcount then
							error "Sequence using variable '" + count + "' needs maxcount argument."
						end
					end
					vname = "i" + cseq.to_s
					#maxcounts[cseq] = maxcount
					maxcounts[cseq] = count
					##x = x + indent + "\tfor(int " + vname + " = 0; " + vname + " < " + count.to_s + "; " + vname + "++)\n"
					##x = x + indent + "\t{\n"
					index += "[msg_" + @name + "." + vname + "]"
				when "%ENDSEQ"
					##seq -= 1
					cseq -= 0
					count = 0
					##x = x + indent + "}\n"
					index.gsub!(/\[(\w+)\]$/, "")
				when "%EVAL"
					##seq += 1
					##x = x + indent + "\tif("
					i = 0
					@evallist.each do |name, result, type|
						if type == "equal"
							type = "=="
						elsif type == "unequal"
							type = "!="
						elsif type == "smaller"
							type = "<"
						elsif type == "larger"
							type = ">"
						end
						if i > 0 then
							##x = x + " || "
						end
						##x = x + "(msg_" + @name + "." + name + index + " " + type + " " + varname(result) + ")"
						i += 1
					end
					##x = x + ")\n"
					##x = x + indent + "\t{\n"
				when "%ENDEVAL"
					##seq -= 1
					##x = x + indent + "}\n"
				else
					myvars[data] = 1
					if @section == role then
						# FIXME: consider role here?
					end

					x = x + indent + "else if(lname == \"" + data + "\")\n"
					x = x + indent + "{\n"
					if type == "int"
						assignexpr = "data.toInt()"
					elsif type == "byte"
						assignexpr = "data.toInt()"
					else
						assignexpr = "data"
					end
					x = x + indent + "\tmsg_" + @name + "." + data + index + " = " + assignexpr + ";\n"
					min, max = option
					if min != nil
						x = x + indent + "\tif(msg_" + @name + "." + data + index + " < " + min + ")\n"
						x = x + indent + "\t{\n"
						x = x + indent + "\t\treturn false;\n"
						x = x + indent + "\t}\n"
					end
					if max != nil
						x = x + indent + "\tif(msg_" + @name + "." + data + index + " > " + max + ")\n"
						x = x + indent + "\t{\n"
						x = x + indent + "\t\treturn false;\n"
						x = x + indent + "\t}\n"
					end
					if cseq > 0
						vname = "i" + cseq.to_s
						x = x + indent + "\tmsg_" + @name + "." + vname + "++;\n"
						if cseq > 1
							# FIXME: make nesting work for any cseq
							vname2 = "i1"
							max = maxcounts[1]
							x = x + indent + "\tif(msg_" + @name + "." + vname + " == " + max + ")\n"
							x = x + indent + "\t{\n"
							x = x + indent + "\t\tmsg_" + @name + "." + vname2 + "++;\n"
							x = x + indent + "\t\tmsg_" + @name + "." + vname + " = 0;\n"
							x = x + indent + "\t}\n"
						end
					end
					x = x + indent + "}\n"
			end
		end

		return x
	end

	def network_cpp(role, engine)
		x = ""
		seq = 0

		data = @name
		if @section == role then
			x += "\t*socket << " + engine + "Opcodes::message_" + data + ";\n"
			x += "\tif(socketdev->error() != QSocketDevice::NoError) emit signalError();\n"
		end

		myvars = Hash.new

		index = ""
		@datalist.each do |data, type, option|
			indent = ""
			for i in (1..seq)
				indent += "\t"
			end
			case data
				when "%SEQ"
					seq += 1
					if type.to_i.to_s == type then
						count = type
						# FIXME: go here when type is a number (constant) - better way?
					else
						count = "message." + type
					end
					maxcount = option
					if myvars[count] then
						if not maxcount then
							error "Sequence using variable '" + count + "' needs maxcount argument."
						end
					end
					vname = "i" + seq.to_s
					#x = x + indent + "\tif(" + count.to_s + " > " + maxcount.to_s + ") emit signalError();\n"
					x = x + indent + "\tfor(int " + vname + " = 0; " + vname + " < " + count.to_s + "; " + vname + "++)\n"
					x = x + indent + "\t{\n"
					index += "[" + vname + "]"
				when "%ENDSEQ"
					seq -= 1
					count = 0
					x = x + indent + "}\n"
					#index = ""
					index.gsub!(/\[(\w+)\]$/, "")
				when "%EVAL"
					seq += 1
					x = x + indent + "\tif("
					i = 0
					@evallist.each do |name, result, type|
						if type == "equal"
							type = "=="
						elsif type == "unequal"
							type = "!="
						elsif type == "smaller"
							type = "<"
						elsif type == "larger"
							type = ">"
						end
						if i > 0 then
							x = x + " || "
						end
						x = x + "(message." + name + index + " " + type + " " + varname(result) + ")"
						i += 1
					end
					x = x + ")\n"
					x = x + indent + "\t{\n"
				when "%ENDEVAL"
					seq -= 1
					x = x + indent + "}\n"
				else
					myvars[data] = 1
					if @section == role then
						x += indent + "\t*socket << " + "message." + data + index + ";\n"
					else
						x += indent + "\t*socket >> " + "message." + data + index + ";\n"
					end
					x += indent + "\tif(socketdev->error() != QSocketDevice::NoError) emit signalError();\n"
			end
		end

		if @section != role then
			x += "\temit signalNotification(" + engine + "Opcodes::message_" + name + ", message);\n"
		end

		return x
	end

	def network_python(role, opcodetype)
		x = ""
		seq = 0

		data = @name
		if @section == role then
			x += "\t\t# assign fd??\n"
			if opcodetype == "int"
				x += "\t\tself.sendint(self." + data + ")\n"
			elsif opcodetype == "byte"
				x += "\t\tself.sendbyte(self." + data + ")\n"
			end
		else
			x += "\t\tmsgobj = " + data + "()\n"
		end

		myvars = Hash.new

		index = ""
		@datalist.each do |data, type, option|
			indent = "\t"
			for i in (1..seq)
				indent += "\t"
			end
			case data
				when "%SEQ"
					seq += 1
					count = type
					maxcount = option
					if myvars[count] then
						if not maxcount then
							error "Sequence using variable '" + count + "' needs maxcount argument."
						end
						count = "self." + count
					end
					vname = "i" + seq.to_s
					#x = x + indent + "\tif " + count.to_s + " > " + maxcount.to_s + ":\n"
					#x = x + indent + "\t\traise Exception()\n"
					x = x + indent + "\tfor " + vname + " in range(0, " + count.to_s + "):\n"
					index = "[" + vname + "]"
				when "%ENDSEQ"
					seq -= 1
					count = 0
					index = ""
				when "%EVAL"
					seq += 1
					x = x + indent + "\tif "
					i = 0
					@evallist.each do |name, result, type|
						if type == "equal"
							type = "=="
						elsif type == "unequal"
							type = "!="
						elsif type == "smaller"
							type = "<"
						elsif type == "larger"
							type = ">"
						end
						if i > 0 then
							x = x + " or "
						end
						x = x + "(self." + name + index + " " + type + " " + varname(result) + ")"
						i += 1
					end
					x = x + ":\n"
				when "%ENDEVAL"
					seq -= 1
				else
					myvars[data] = 1
					#esname = type
					if @section == role then
						x += indent + "\t# assign fd???\n"
						case type
							when "string"
								x += indent + "\tself.sendstring(msgobj." + data + index + ")\n"
							when "byte"
								x += indent + "\tself.sendbyte(msgobj." + data + index + ")\n"
							when "int"
								x += indent + "\tself.sendint(msgobj." + data + index + ")\n"
						end
					else
						x += indent + "\t# assign fd???\n"
						case type
							when "string"
								x += indent + "\tmsgobj." + data + index + " = self.getstring()\n"
							when "byte"
								x += indent + "\tmsgobj." + data + index + " = self.getbyte()\n"
							when "int"
								x += indent + "\tmsgobj." + data + index + " = self.getint()\n"
						end
					end
			end
		end

		if @section != role then
			x += "\t\tif self.notifier:\n"
			x += "\t\t\tself.notifier(\"" + @name + "\", msgobj)\n"
		end

		return x
	end

	def network_ruby(role)
		x = ""
		seq = 0

		data = @name
		if @section == role then
			x += "\t\t# assign fd??\n"
			x += "\t\tsendbyte(@" + data + ")\n"
		end

		myvars = Hash.new

		index = ""
		@datalist.each do |data, type, option|
			indent = "\t"
			for i in (1..seq)
				indent += "\t"
			end
			case data
				when "%SEQ"
					seq += 1
					count = type
					maxcount = option
					if myvars[count] then
						if not maxcount then
							error "Sequence using variable '" + count + "' needs maxcount argument."
						end
						count = "@" + count
					end
					vname = "i" + seq.to_s
					#x = x + indent + "\tif " + count.to_s + " > " + maxcount.to_s + ":\n"
					#x = x + indent + "\t\traise Exception()\n"
					x = x + indent + "\tfor " + vname + " in 0.." + count.to_s + "\n"
					index += "[" + vname + "]"
				when "%ENDSEQ"
					seq -= 1
					count = 0
					index.gsub!(/\[(\w+)\]$/, "")
					x = x + indent + "end\n"
				when "%EVAL"
					seq += 1
					x = x + indent + "\tif "
					i = 0
					@evallist.each do |name, result, type|
						if type == "equal"
							type = "=="
						elsif type == "unequal"
							type = "!="
						elsif type == "smaller"
							type = "<"
						elsif type == "larger"
							type = ">"
						end
						if i > 0 then
							x = x + " or "
						end
						x = x + "(@" + name + index + " " + type + " @" + varname(result) + ")"
						i += 1
					end
					x = x + "\n"
				when "%ENDEVAL"
					seq -= 1
					x = x + indent + "end\n"
				else
					myvars[data] = 1
					#esname = type
					if @section == role then
						x += indent + "\t# assign fd???\n"
						case type
							when "string"
								x += indent + "\tsendstring(@" + data + index + ")\n"
							when "byte"
								x += indent + "\tsendchar(@" + data + index + ")\n"
							when "int"
								x += indent + "\tsendbyte(@" + data + index + ")\n"
						end
					else
						x += indent + "\t# assign fd???\n"
						case type
							when "string"
								x += indent + "\t@" + data + index + " = getstring()\n"
							when "byte"
								x += indent + "\t@" + data + index + " = getbyte()\n"
							when "int"
								x += indent + "\t@" + data + index + " = getint()\n"
						end
					end
			end
		end

		if @section != role then
			x += "\t\tif @notifier\n"
			x += "\t\t\t@notifier.call(@" + name + ")\n"
			x += "\t\tend\n"
		end

		return x
	end
end

# The class definition for the whole communication protocol
# Contains handlers for all XML tags, the parser and the output
class GGZComm

	# Initialize all non-scalar values
	def initialize
		@eventlist = Array.new
		@constants = Hash.new
		@clientlinks = Hash.new
		@serverlinks = Hash.new
		@verbose = false

		@constants["ggz:seat_open"] = "1"
		@constants["ggz:seat_bot"] = "2"
		@constants["ggz:seat_player"] = "3"
		@constants["ggz:seat_reserved"] = "4"
		@constants["ggz:seat_abandoned"] = "5"

		@opcodetype = "int"
	end

	def handle_definitions(data)
		data.each do |key, value|
			if @verbose then
				puts key + "=" + value
			end
			case key
				when "opcodetype"
					if value == "int" or value == "byte" then
						@opcodetype = value
					else
						error "Invalid value for 'opcodetype'."
					end
				else
					error "Unknown attribute '" + key + "' for definitions, only 'opcodetype' is allowed."
			end
		end
	end

	def handle_toplevel(data)
		data.each do |key, value|
			if @verbose then
				puts key + "=" + value
			end
			case key
				when "engine"
					@engine = value
					@protocol = @engine
				when "version"
					@version = value
				when "xmlns"
					if value != "urn:ggz:ggzcomm" then
					end
				else
					error "Unknown attribute '" + key + "' for ggzcomm, only 'version' and 'engine' are allowed."
			end
		end
	end

	def handle_event(data, section)
		data.each do |key, value|
			if @verbose then
				puts "event:" + key + "=" + value
			end
			case key
				when "name"
					@event = GGZCommEvent.new(value, section)
					@eventlist.push @event
				else
					error "Events must not carry any attributes except 'name'."
			end
		end
	end

	def handle_event_sequence(data)
		xcount = 0
		xmincount = -1
		xmaxcount = -1
		data.each do |key, value|
			if @verbose then
				puts "sequence:" + key + "=" + value
			end
			case key
				when "count"
					xcount = value
				when "maxcount"
					xmaxcount = value
				when "mincount"
					xmincount = value
				else
					error "No attribute other than 'count', 'mincount', 'maxcount' can be given for 'sequence'."
			end
		end
		if xcount
			# FIXME: check here if maxcount given if count is non-constant?
			@event.sequence(xcount, xmincount, xmaxcount)
		else
			error "Tag 'sequence' needs attribute 'count'."
		end
	end

	def handle_event_eval(data)
		@event.evaluate
		@evallist = []
	end

	def handle_event_condition(data)
		xname = 0
		xresult = 0
		xtype = "equal"
		data.each do |key, value|
			if @verbose then
				puts "eval:" + key + "=" + value
			end
			case key
				when "name"
					xname = value
				when "result"
					xresult = value
				when "type"
					xtype = value
				else
					error "Attribute for 'condition' must be 'name' or 'result'"
			end
		end
		if xname && xresult
			if xtype != "equal" and xtype != "smaller" and xtype != "larger" and xtype != "unequal" then
				error "Tag 'condition' does not know operator type '" + xtype + "'."
			end
			@evallist.push [xname, xresult, xtype]
		else
			error "Tag 'condition' requires both 'name' and 'result' as attribute."
		end
	end

	def handle_definitions_def(data)
		xname = nil
		xvalue = nil
		data.each do |key, value|
			if @verbose then
				puts "def:" + key + "=" + value
			end
			case key
				when "name"
					xname = value
				when "value"
					xvalue = value
				else
					error "Don't know anything about a '" + key + "' for the 'def' tag."
			end
		end
		if xname and xvalue then
			@constants[xname] = xvalue
		else
			error "Incorrect definition for '" + xname.to_s + "' = '" + xvalue.to_s + "'."
		end
	end

	def handle_protocol_link(data)
		xclient = nil
		xserver = nil
		#xfollowup = nil
		mode = nil
		initiator = nil
		data.each do |key, value|
			if @verbose then
				puts "link:" + key + "=" + value
			end
			case key
				when "client"
					xclient = value
					if mode == nil then
						mode = "client"
					elsif mode == "server"
						mode = "both"
					else
						error "No 'client' attribute allowed here."
					end
				when "server"
					xserver = value
					if mode == nil then
						mode = "server"
					elsif mode == "client"
						mode = "both"
					else
						error "No 'server' attribute allowed here."
					end
				when "initiator"
					if initiator == nil then
						if value == "server" or value == "client" then
							initiator = value
						else
							error "Invalid 'initiator' value."
						end
					else
						error "Duplicate 'initiator' attribute found."
					end
				#when "followup"
				#	xfollowup = value
				else
					error "No '" + key + "' attribute allowed for the 'link' tag."
			end
		end
		# no followups yet - XXX FIXME!!!
		if mode == "both" then
			if initiator == "server"
				@clientlinks[xserver] = xclient
				@serverlinks[xclient] = xserver
			elsif initiator == "client"
				@serverlinks[xserver] = xclient
				@clientlinks[xclient] = xserver
			end
		else
			error "Incorrect link for '" + xclient.to_s + "', '" + xserver.to_s + "'."
		end
	end

	def handle_event_data(data)
		xname = 0
		xtype = 0
		xmin = nil
		xmax = nil
		data.each do |key, value|
			if @verbose then
				puts "data:" + key + "=" + value
			end
			case key
				when "name"
					xname = value
				when "type"
					xtype = value
				when "min"
					xmin = value
				when "max"
					xmax = value
				else
					error "Only 'name', 'type', 'min' and 'max' may be present in the 'data' tag."
			end
		end
		if xname && xtype
			if xtype == "int" or xtype == "byte" or xtype == "string" then
				@event.add(xname, xtype, xmin, xmax)
			else
				error "Invalid value for the 'type' tag."
			end
		else
			error "Missing one of the attributes 'name' or 'type' for the 'data' tag."
		end
	end

	# Load an XML file and setup all internal structures
	def load(file)
		@protocol = file
		section = "toplevel"
		parser = XMLParser.new
		filename = file

		begin
			f = File.new(filename)
		rescue
			filename = file + ".protocol"
			begin
				f = File.new(filename)
			rescue
				puts "Could not open " + filename + "!"
				#return
				raise
			end
		end

		# cut off xml header
		f.gets
		while input = f.read
			if input == "" then break end
			if @verbose then
				puts "Input: "
				print input
			end
			parser.parse input do |type, name, data|
				if name then tag = name.downcase end
				case type
					when XMLParser::START_ELEM
						case tag
							when "ggzcomm"
								if section == "toplevel"
									handle_toplevel(data)
								else
									error "The 'ggzcomm' tag may only appear as top-level tag."
								end
							when "protocol"
								section = "protocol"
							when "server"
								section = "server"
							when "client"
								section = "client"
							when "definitions"
								section = "definitions"
								handle_definitions(data)
							when "event"
								if section == "client" || section == "server"
									handle_event(data, section)
								else
									error "The 'event' tag is only valid in 'server' or 'client'."
								end
							when "sequence"
								if section == "client" || section == "server"
									handle_event_sequence(data)
								else
									error "The 'sequence' tag is only valid in 'server' or 'client'."
								end
							when "eval"
								if section == "client" || section == "server"
									handle_event_eval(data)
								else
									error "The 'eval' tag is only valid in 'server' or 'client'."
								end
							when "condition"
								if section == "client" || section == "server"
									handle_event_condition(data)
								else
									error "The 'condition' tag is only valid in 'server' or 'client'."
								end
							when "def"
								if section == "definitions"
									handle_definitions_def(data)
								else
									error "The 'def' tag is only valid in 'definitions'."
								end
							when "link"
								if section == "protocol"
									handle_protocol_link(data)
								else
									error "The 'link' tag is only valid in 'protocol'."
								end
							when "data"
								if section == "client" || section == "server"
									handle_event_data(data)
								else
									error "The 'data' tag may only be used in 'server' or 'client'."
								end
							else
								error "Don't know how to process XML tag " + tag + "."
						end
					when XMLParser::END_ELEM
						case tag
							when "sequence"
								@event.endsequence
							when "eval"
								@event.endevaluate
								@event.setevallist(@evallist)
						end
					when XMLParser::CDATA
						#puts "cdata"
				end
			end
		end
	end

	# Convenience method: returns whether a string is a number or not
	def isnum(s)
		if s == "0" or s.to_i != 0 then
			return true
		else
			return false
		end
	end

	# Output all internal structures in a certain format
	def output(format, library, role, mode, xmlcode)
		refconstants = Hash.new

		@eventlist.each do |event|
			if isnum(event.name) then
				next
			end
			if not @constants[event.name] then
				error "Missing value definition for '" + event.name + "'"
			else
				refconstants[event.name] = @constants[event.name]
			end

			evallist = event.evallist
			if not evallist then
				next
			end
			evallist.each do |name, result, type|
				if isnum(result) then
					next
				end
				if not @constants[result] then
					error "Missing value definition for '" + result + "'"
				else
					refconstants[result] = @constants[result]
				end
			end
		end

		@constants = refconstants

		if role == "client" then
			links = @clientlinks
			revlinks = @serverlinks
		elsif role == "server"
			links = @serverlinks
			revlinks = @clientlinks
		else
			error "Unknown role '" + role + "'"
		end

		@eventlist.each do |event|
			checkvars = Hash.new
			event.datalist.each do |name, type|
				next if name == '%SEQ'
				next if name == '%EVAL'
				#if checkvars[name] and checkvars[name] != type then
				if checkvars[name] then
					error "Variable of name '" + name + "' is already in use."
				end
				checkvars[name] = type
			end
		end

		basename = @protocol + "_" + role

		if format == "c" and (library == "easysock" or library == "dio") then
			f = File.new(basename + ".h", "w")
			f.puts "/* Generated by GGZComm/ruby version " + $version.to_s + " */"
			f.puts "/* Protocol '" + @engine + "', version '" + @version + "' */"
			f.puts ""
			proto = @protocol.upcase
			f.puts "#ifndef GGZCOMM_" + proto + "_H"
			f.puts "#define GGZCOMM_" + proto + "_H"
			f.puts ""
			@eventlist.each do |event|
				f.puts "#define " + event.name + " " + @constants[event.name]
				@constants[event.name] = nil
			end

			i = 0
			@eventlist.each do |event|
				i += event.decl(true, nil).length
			end
			if i > 0
				f.puts ""
				f.puts "struct variables_t"
				f.puts "{"
				@eventlist.each do |event|
					if event.decl(true, nil) != "" then
						f.puts event.decl(true, nil);
					end
				end
				f.puts "};"
				f.puts "struct variables_t variables;"
			end

			f.puts ""
			f.puts "void ggzcomm_network_main(void);"
			f.puts ""
			@eventlist.each do |event|
				if event.section == role then
					f.print "void ggzcomm_" + event.name + "("
					f.print "void"
					f.puts ");"
				end
			end
			f.puts ""
			f.puts "typedef void (*notifier_func_type)(int opcode);"
			f.puts "typedef void (*error_func_type)(void);"
			f.puts ""
			f.puts "void ggzcomm_set_fd(int usefd);"
			f.puts "int  ggzcomm_get_fd(void);"
			f.puts "void ggzcomm_set_notifier_callback(notifier_func_type f);"
			f.puts "void ggzcomm_set_error_callback(error_func_type f);"
			f.puts ""
			f.puts "#endif"
			f.puts ""
			f.close

			f = File.new(basename + ".c", "w")
			f.puts "/* Generated by GGZComm/ruby version " + $version.to_s + " */"
			f.puts "/* Protocol '" + @engine + "', version '" + @version + "' */"
			f.puts ""
			f.puts "#include \"" + basename + ".h" + "\""
			f.puts "#include <stdlib.h>"
			f.puts "#include <ggz.h>"
			if library == "dio" then
				f.puts "#include <ggz_dio.h>"
			end
			if mode == "debug" then
				f.puts "#define GGZCOMM_DEBUG 1"
				f.puts "#include <stdio.h>"
			end
			if @constants.length > 0 then
				f.puts ""
				@constants.each do |name, value|
					next if not value
					f.puts "#define " + varname(name) + " " + value
				end
			end
			f.puts ""
			f.puts "static notifier_func_type notifier_func = NULL;"
			f.puts "static error_func_type error_func = NULL;"
			f.puts "static int fd = -1;"
			f.puts "static int ret;"
			f.puts "static int requirelink = 0;"
			f.puts "static int nextlink;"
			if library == "dio" then
				f.puts "static GGZDataIO *dio = NULL;"
			end
			f.puts ""
			f.puts "static void ggzcomm_error(void);"
			f.puts ""
			@eventlist.each do |event|
				if event.section != role then
					f.print "static "
				end
				f.print "void ggzcomm_" + event.name + "("
				f.print "void"
				f.puts ")"
				f.puts "{"
				f.puts event.network(role, mode, library)
				if links[event.name] then
					f.puts ""
					f.puts "\trequirelink = 1;"
					f.puts "\tnextlink = " + links[event.name] + ";"
				end
				f.puts "}"
				f.puts ""
			end
			f.puts "void ggzcomm_network_main(void)"
			f.puts "{"
			if @opcodetype == "int"
				f.puts "\tint opcode;"
			elsif @opcodetype == "byte"
				f.puts "\tchar opcode;"
			end
			f.puts ""
			if library == "dio" then
				f.puts "\tif(!dio) dio = ggz_dio_new(fd);"
			f.puts ""
			end
			if mode == "debug":
				f.puts "#ifdef GGZCOMM_DEBUG"
				f.puts "\tprintf(\"(ggzcomm) reading opcode...\\n\");"
				f.puts "#endif"
				f.puts ""
			end
			if library == "dio"
				f.puts "\tggz_dio_get_int(dio, &opcode);"
			else
				if @opcodetype == "int"
					f.puts "\tggz_read_int(fd, &opcode);"
				elsif @opcodetype == "byte"
					f.puts "\tggz_read_char(fd, &opcode);"
				end
			end
			f.puts ""
			if mode == "debug":
				f.puts "#ifdef GGZCOMM_DEBUG"
				f.puts "\tprintf(\"(ggzcomm) -> opcode is %i\\n\", opcode);"
				f.puts "#endif"
				f.puts ""
			end
			f.puts "\tif(requirelink)"
			f.puts "\t{"
			f.puts "\t\tif(opcode != nextlink) ggzcomm_error();"
			f.puts "\t\trequirelink = 0;"
			f.puts "\t}"
			f.puts ""
			f.puts "\tswitch(opcode)"
			f.puts "\t{"
			@eventlist.each do |event|
				if event.section != role
					f.puts "\t\tcase " + event.name + ":\n"
					f.puts "\t\t\tggzcomm_" + event.name() + "();\n"
					f.puts "\t\t\tbreak;"
				end
			end
			f.puts "\t}"
			f.puts "}"
			f.puts ""
			f.puts "void ggzcomm_set_notifier_callback(notifier_func_type f)"
			f.puts "{"
			f.puts "\tnotifier_func = f;"
			f.puts "}"
			f.puts ""
			f.puts "void ggzcomm_set_error_callback(error_func_type f)"
			f.puts "{"
			f.puts "\terror_func = f;"
			f.puts "}"
			f.puts ""
			f.puts "void ggzcomm_set_fd(int usefd)"
			f.puts "{"
			f.puts "\tfd = usefd;"
			f.puts "}"
			f.puts ""
			f.puts "int ggzcomm_get_fd(void)"
			f.puts "{"
			f.puts "\treturn fd;"
			f.puts "}"
			f.puts ""
			f.puts "static void ggzcomm_error(void)"
			f.puts "{"
			if mode == "debug":
				f.puts "#ifdef GGZCOMM_DEBUG"
				f.puts "\tprintf(\"(ggzcomm) error!\\n\");"
				f.puts "#endif"
				f.puts ""
			end
			f.puts "\tif(error_func) (error_func)();"
			f.puts "}"
			f.puts ""
			f.close
		elsif format == "c++" and (library == "qt" or library == "qt4")
			f = File.new(basename + ".h", "w")
			f.puts "/* Generated by GGZComm/ruby version " + $version.to_s + " */"
			f.puts "/* Protocol '" + @engine + "', version '" + @version + "' */"
			f.puts ""
			proto = @protocol.upcase
			f.puts "#ifndef GGZCOMM_" + proto + "_H"
			f.puts "#define GGZCOMM_" + proto + "_H"
			f.puts ""
			f.puts "#include <qobject.h>"
			if xmlcode then
				f.puts "#include <qxml.h>"
			end
			f.puts ""
			f.puts "class QDataStream;"
			if library == "qt4" then
				f.puts "class QTcpSocket;"
				f.puts "class QAbstractSocket;"
			else
				f.puts "class QSocket;"
				f.puts "class QSocketDevice;"
			end
			if xmlcode then
				f.puts "class QXmlInputSource;"
				f.puts "class QXmlSimpleReader;"
			end

			f.puts ""
			f.puts "class " + @engine + "Opcodes"
			f.puts "{"
			f.puts "\tpublic:"

			f.puts "\tenum Opcode"
			f.puts "\t{"
			i = 0
			@eventlist.each do |event|
				if i > 0 then
					f.puts ","
				end
				f.print "\t\tmessage_" + event.name + " = " + @constants[event.name]
				@constants[event.name] = nil
				i += 1
			end
			f.puts ""
			f.puts "\t};"
			f.puts "};"
			f.puts ""

			f.puts "class msg"
			f.puts "{"
			f.puts "\tpublic:"
			f.puts "\tmsg(" + @engine + "Opcodes::Opcode type){m_type = type;}"
			f.puts "\t" + @engine + "Opcodes::Opcode type() const{return m_type;}"
			f.puts "\tprivate:"
			f.puts "\t" + @engine + "Opcodes::Opcode m_type;"
			f.puts "};"

			@eventlist.each do |event|
				f.puts ""
				f.puts "class " + event.name + " : public msg"
				f.puts "{"
				f.puts "\tpublic:"
				f.puts "\t" + event.name + "() : msg(" + @engine + "Opcodes::message_" + event.name + "){}"
				f.puts event.decl(true, library)
				seq = 0
				event.datalist.each do |data, type, option|
					case data
						when "%SEQ"
							seq += 1
							f.puts "\tint i" + seq.to_s + ";";
					end
				end
				f.puts "};"
			end

			if xmlcode then
				f.puts ""
				f.puts "class " + @engine + "XML : public QXmlDefaultHandler"
				f.puts "{"
				f.puts "\tpublic:"
				f.puts "\t\t" + @engine + "XML();"
				f.puts "\t\tbool startElement(const QString& nsuri, const QString& lname, const QString& qname, const QXmlAttributes& atts);"
				f.puts "\t\tbool endElement(const QString& nsuri, const QString& lname, const QString& qname);"
				f.puts "\t\tbool characters(const QString& s);"
				@eventlist.each do |event|
					f.puts "\t\t" + event.name + " msg_" + event.name + ";"
					# XXX only for recv-role
				end
				f.puts "\t\tQString lastmsg;"
				f.puts "\tprivate:"
				f.puts "\t\t" + @engine + "Opcodes::Opcode lastmessage;"
				f.puts "\t\tbool streamstarted;"
				f.puts "\t\tbool streammessage;"
				f.puts "\t\tQString data;"
				f.puts "};"
			end

			f.puts ""
			f.puts "class " + @engine + " : public QObject"
			f.puts "{"
			f.puts "\tQ_OBJECT"
			f.puts "\tpublic:"
			f.puts "\t" + @engine + "();"
			f.puts ""
			f.puts "\tvoid ggzcomm_network_main();"
			f.puts ""
			@eventlist.each do |event|
				if event.section == role then
					f.puts "\tvoid ggzcomm_" + event.name + "(const " + event.name + "& message);"
				end
			end
			f.puts ""
			f.puts "\tvoid ggzcomm_set_fd(int usefd);"
			if library == "qt4" then
				f.puts "\tvoid ggzcomm_set_socket(QTcpSocket *socket);"
			else
				f.puts "\tvoid ggzcomm_set_socket(QSocket *socket);"
			end
			f.puts ""
			f.puts "\tsignals:"
			f.puts "\tvoid signalNotification(" + @engine + "Opcodes::Opcode messagetype, const msg& message);"
			f.puts "\tvoid signalError();"
			f.puts ""
			f.puts "\tprivate:"
			f.puts "\t\tvoid handle(bool ret);"
			@eventlist.each do |event|
				if event.section != role then
					f.puts "\tvoid ggzcomm_" + event.name + "();"
				end
			end
			f.puts ""
			f.puts "\tint fd;"
			f.puts "\tint ret;"
			f.puts "\tint requirelink;"
			f.puts "\tint nextlink;"
			if library == "qt4" then
				f.puts "\tQTcpSocket *clientsocket;"
				f.puts "\tQAbstractSocket *socketdev;"
			else
				f.puts "\tQSocket *clientsocket;"
				f.puts "\tQSocketDevice *socketdev;"
			end
			if xmlcode then
				f.puts "\tQXmlInputSource *source;"
				f.puts "\tQXmlSimpleReader *reader;"
				f.puts "\t" + @engine + "XML *handler;"
			else
				f.puts "\tQDataStream *socket;"
			end
			f.puts "};"
			f.puts ""
			f.puts "#endif"
			f.puts ""
			f.close

			f = File.new(basename + ".cpp", "w")
			f.puts "/* Generated by GGZComm/ruby version " + $version.to_s + " */"
			f.puts "/* Protocol '" + @engine + "', version '" + @version + "' */"
			f.puts ""
			f.puts "#include \"" + basename + ".h" + "\""
			if library == "qt4" then
				f.puts "#include <qtcpsocket.h>"
				f.puts "#include <qabstractsocket.h>"
			else
				f.puts "#include <qsocket.h>"
				f.puts "#include <qsocketdevice.h>"
			end
			if xmlcode then
				f.puts "#include <qxml.h>"
			else
				f.puts "#include <qdatastream.h>"
			end
			if @constants.length > 0 then
				f.puts ""
				@constants.each do |name, value|
					next if not value
					f.puts "#define " + varname(name) + " " + value
				end
			end
			#if xmlcode then
			#	f.puts ""
			#	f.puts "static struct tags {"
			#	i = 0
			#	@eventlist.each do |event|
			#		if i > 0 then
			#			f.puts ","
			#		end
			#		f.print "\t{\"" + event.name + "\", " + @engine + "Opcodes::message_" + event.name + "}"
			#		@constants[event.name] = nil
			#		i += 1
			#	end
			#	f.puts ""
			#	f.puts "};"
			#end
			f.puts ""
			f.puts @engine + "::" + @engine + "()"
			f.puts ": QObject()"
			f.puts "{"
			f.puts "\trequirelink = 0;"
			f.puts "\tfd = -1;"
			if xmlcode then
				f.puts "\treader = NULL;"
				f.puts "\tsource = NULL;"
			else
				f.puts "\tsocket = NULL;"
			end
			f.puts "\tsocketdev = NULL;"
			f.puts "}"
			f.puts ""
			@eventlist.each do |event|
				#if event.section != role then
				#	f.print "static "
				#end
				if event.section != role
					f.puts "void " + @engine + "::ggzcomm_" + event.name + "()"
				else
					f.puts "void " + @engine + "::ggzcomm_" + event.name + "(const " + event.name + "& message)"
				end
				f.puts "{"
				if event.section != role
					f.puts "\t" + event.name + " message;"
				end
				if not xmlcode
					f.puts event.network_cpp(role, @engine)
				else
					# FIXME!
				end
				if links[event.name] then
					f.puts ""
					f.puts "\trequirelink = 1;"
					f.puts "\tnextlink = " + @engine + "Opcodes::message_" + links[event.name] + ";"
				end
				f.puts "}"
				f.puts ""
			end
			f.puts "void " + @engine + "::ggzcomm_network_main()"
			f.puts "{"
			if not xmlcode
				if @opcodetype == "int"
					f.puts "\tint opcode;"
				elsif @opcodetype == "byte"
					if library == "qt4"
						f.puts "\tqint8 opcode;"
					else
						f.puts "\tQ_INT8 opcode;"
					end
				end
				f.puts ""
				f.puts "\t*socket >> opcode;"
			else
				# FIXME!
				f.puts "\tif(!reader) return;"
				f.puts ""
				f.puts "\tbool ret = reader->parseContinue();"
				if mode == "debug" then
					f.puts "\tqDebug(\"parseContinue :: %i\", ret);"
				end
				f.puts "\thandle(ret);"
			end
			if not xmlcode then
				f.puts ""
				f.puts "\tif(requirelink)"
				f.puts "\t{"
				f.puts "\t\tif(opcode != nextlink) emit signalError();"
				f.puts "\t\trequirelink = 0;"
				f.puts "\t}"
				f.puts ""
				f.puts "\tswitch(opcode)"
				f.puts "\t{"
				@eventlist.each do |event|
					if event.section != role
						f.puts "\t\tcase " + @engine + "Opcodes::message_" + event.name + ":\n"
						f.puts "\t\t\tggzcomm_" + event.name() + "();\n"
						f.puts "\t\t\tbreak;"
					end
				end
				f.puts "\t}"
			end
			f.puts "}"

			if xmlcode then
				f.puts ""
				f.puts "void " + @engine + "::handle(bool ret)"
				f.puts "{"
				f.puts "\tif(!ret)"
				f.puts "\t{"
				f.puts "\t\temit signalError();"
				f.puts "\t\treturn;"
				f.puts "\t}"
				f.puts ""
				f.puts "\tif(!handler->lastmsg.isEmpty())"
				f.puts "\t{"
				@eventlist.each do |event|
					f.puts "\t\tif(handler->lastmsg == \"" + event.name + "\")"
					f.puts "\t\t{"
					f.puts "\t\t\temit signalNotification(" + @engine + "Opcodes::message_" + event.name + ", handler->msg_" + event.name + ");"
					f.puts "\t\t}"
				end
				f.puts "\t}"
				f.puts "}"
			end
			f.puts ""
			f.puts "void " + @engine + "::ggzcomm_set_fd(int usefd)"
			f.puts "{"
			if mode == "debug" then
				f.puts "\tqDebug(\"set fd; start parsing\");"
			end
			f.puts "\tfd = usefd;"
			if library == "qt4" then
				f.puts "\tsocketdev = new QAbstractSocket(QAbstractSocket::TcpSocket, this);"
				f.puts "\tsocketdev->setSocketDescriptor(fd);"
			else
				f.puts "\tsocketdev = new QSocketDevice(fd, QSocketDevice::Stream);"
			end
			if xmlcode then
				f.puts "\tsource = new QXmlInputSource(socketdev);"
				f.puts "\treader = new QXmlSimpleReader();"
				f.puts "\thandler = new " + @engine + "XML();"
				f.puts "\treader->setContentHandler(handler);"
				f.puts "\tbool ret = reader->parse(source, true);"
				if mode == "debug" then
					f.puts "\tqDebug(\"finished parsing (a bit) :: %i\", ret);"
				end
				f.puts "\thandle(ret);"
			else
				f.puts "\tsocket = new QDataStream(socketdev);"
			end
			f.puts "}"
			f.puts ""
			if library == "qt4" then
				f.puts "void " + @engine + "::ggzcomm_set_socket(QTcpSocket *socket)"
			else
				f.puts "void " + @engine + "::ggzcomm_set_socket(QSocket *socket)"
			end
			f.puts "{"
			if mode == "debug" then
				f.puts "\tqDebug(\"set socket; start parsing\");"
			end
			f.puts "\tclientsocket = socket;"
			if xmlcode then
				f.puts "\tsource = new QXmlInputSource(clientsocket);"
				f.puts "\treader = new QXmlSimpleReader();"
				f.puts "\thandler = new " + @engine + "XML();"
				f.puts "\treader->setContentHandler(handler);"
				f.puts "\tbool ret = reader->parse(source, true);"
				if mode == "debug" then
					f.puts "\tqDebug(\"finished parsing (a bit) :: %i\", ret);"
				end
				f.puts "\thandle(ret);"
			end
			f.puts "}"
			f.puts ""
			if xmlcode then
				f.puts @engine + "XML::" + @engine + "XML()"
				f.puts "{"
				f.puts "\tstreamstarted = false;"
				f.puts "\tstreammessage = false;"
				f.puts "}"
				f.puts ""
				f.puts "bool " + @engine + "XML::startElement(const QString& nsuri, const QString& lname, const QString& qname, const QXmlAttributes& atts)"
				f.puts "{"
				if mode == "debug" then
					if library == "qt4"
						f.puts "\tqDebug(\"sax: start element! %s [%s]\", qPrintable(qname), qPrintable(lname));"
					else
						f.puts "\tqDebug(\"sax: start element! %s [%s]\", qname.utf8().data(), lname.utf8().data());"
					end
				end
				f.puts "\tlastmsg = QString::null;"
				f.puts ""
				f.puts "\tif(!streamstarted)"
				f.puts "\t{"
				f.puts "\t\tif(lname == \"ggzcomm-xml\")"
				f.puts "\t\t{"
				f.puts "\t\t\tstreamstarted = true;"
				f.puts "\t\t}"
				f.puts "\t\telse"
				f.puts "\t\t{"
				f.puts "\t\t\treturn false;"
				f.puts "\t\t}"
				f.puts "\t}"
				f.puts "\telse"
				f.puts "\t{"
				f.puts "\t\tif(!streammessage)"
				f.puts "\t\t{"
				ifword = "if"
				@eventlist.each do |event|
					f.puts "\t\t\t" + ifword + "(lname == \"" + event.name + "\")"
					f.puts "\t\t\t{"
					f.puts "\t\t\t\tlastmessage = " + @engine + "Opcodes::message_" + event.name + ";"
					f.puts "\t\t\t\tstreammessage = true;"

					seq = 0
					event.datalist.each do |data, type, option|
						case data
							when "%SEQ"
								seq += 1
								f.puts "\t\t\t\tmsg_" + event.name + ".i" + seq.to_s + " = 0;";
						end
					end

					f.puts "\t\t\t}"
					ifword = "else if"
				end
				f.puts "\t\t\telse"
				f.puts "\t\t\t{"
				f.puts "\t\t\t\treturn false;"
				f.puts "\t\t\t}"
				f.puts "\t\t}"
				@eventlist.each do |event|
					f.puts "\t\telse if(lastmessage == " + @engine + "Opcodes::message_" + event.name + ")"
					f.puts "\t\t{"
					ifword = "if"
					event.datalist.each do |data, type, option|
						next if data == '%SEQ'
						next if data == '%ENDSEQ'
						next if data == '%EVAL'
						next if data == '%ENDEVAL'
						f.puts "\t\t\t" + ifword + "(lname == \"" + data + "\")"
						f.puts "\t\t\t{"
						f.puts "\t\t\t\t//..."
						f.puts "\t\t\t}"
						ifword = "else if"
					end
					if event.datalist.length == 0 then
						f.puts "\t\t\treturn false;"
					else
						f.puts "\t\t\telse"
						f.puts "\t\t\t{"
						f.puts "\t\t\t\treturn false;"
						f.puts "\t\t\t}"
					end
					f.puts "\t\t}"
				end
				f.puts "\t}"
				f.puts ""
				f.puts "\treturn true;"
				f.puts "}"
				f.puts ""
				f.puts "bool " + @engine + "XML::endElement(const QString& nsuri, const QString& lname, const QString& qname)"
				f.puts "{"
				if mode == "debug"
					if library == "qt4"
						f.puts "\tqDebug(\"sax: end element! %s\", qPrintable(qname));"
					else
						f.puts "\tqDebug(\"sax: end element! %s\", qname.utf8().data());"
					end
				end
				f.puts "\tif(!streamstarted)"
				f.puts "\t{"
				f.puts "\t\treturn false;"
				f.puts "\t}"
				f.puts "\telse"
				f.puts "\t{"
				f.puts "\t\tif(streammessage)"
				f.puts "\t\t{"
				ifword = "if"
				@eventlist.each do |event|
					f.puts "\t\t\t" + ifword + "(lastmessage == " + @engine + "Opcodes::message_" + event.name + ")"
					f.puts "\t\t\t{"
					f.puts "\t\t\t\tif(lname == \"" + event.name + "\")"
					f.puts "\t\t\t\t{"
					if mode == "debug"
						f.puts "\t\t\t\t\tqDebug(\"..." + event.name + "....\");"
					end
					f.puts "\t\t\t\t\tlastmsg = \"" + event.name + "\";"
					f.puts "\t\t\t\t}"

					f.puts event.network_cpp_xml(role, @engine)

					f.puts "\t\t\t\telse"
					f.puts "\t\t\t\t{"
					f.puts "\t\t\t\t\treturn false;"
					f.puts "\t\t\t\t}"

					f.puts "\t\t\t}"
					ifword = "else if"
				end
				f.puts "\t\t\telse"
				f.puts "\t\t\t{"
				f.puts "\t\t\t\treturn false;"
				f.puts "\t\t\t}"
				f.puts "\t\t}"
				f.puts "\t\telse"
				f.puts "\t\t{"
				f.puts "\t\t\tif(lname == \"ggzcomm-xml\")"
				f.puts "\t\t\t{"
				f.puts "\t\t\t\tstreamstarted = false;"
				f.puts "\t\t\t}"
				f.puts "\t\t\telse"
				f.puts "\t\t\t{"
				f.puts "\t\t\t\treturn false;"
				f.puts "\t\t\t}"
				f.puts "\t\t}"
				f.puts "\t}"
				f.puts ""
				f.puts "\treturn true;"
				f.puts "}"
				f.puts ""
				f.puts "bool " + @engine + "XML::characters(const QString& s)"
				f.puts "{"
				if mode == "debug" then
					if library == "qt4"
						f.puts "\tqDebug(\"sax: characters! %s\", qPrintable(s));"
					else
						f.puts "\tqDebug(\"sax: characters! %s\", s.utf8().data());"
					end
				end
				f.puts "\tdata.append(s);"
				f.puts "\treturn true;"
				f.puts "}"
			end
			f.close
		elsif format == "python" and library == "socket"
			#error "Currently unsupported"
			f = File.new(basename + ".py", "w")
			f.puts "### Generated by GGZComm/ruby version " + $version.to_s
			f.puts "### Protocol '" + @engine + "', version '" + @version + "'"
			f.puts ""
			f.puts "import socket"
			if xmlcode:
				f.puts "import xml.sax"
				f.puts "import xml.sax.handler"
				f.puts "import xml.sax.writer"
			end
			@eventlist.each do |event|
				f.puts ""
				f.puts "class " + varname(event.name) + ":"
				f.puts "\tdef __init__(self):"
				event.datalist.each do |data, type, option|
					next if data == '%SEQ'
					next if data == '%ENDSEQ'
					next if data == '%EVAL'
					next if data == '%ENDEVAL'
					f.puts "\t\tself." + data + " = None"
				end
				if event.datalist.length == 0 then
					f.puts "\t\tpass"
				end
			end
			f.puts ""
			if xmlcode then
				f.puts "class Parser(xml.sax.handler.ContentHandler):"
				f.puts "\tdef __init__(self):"
				f.puts "\t\txml.sax.handler.ContentHandler.__init__(self)"
				f.puts ""
				f.puts "\t\tself.valid = False"
				f.puts "\t\tself.message = None"
				f.puts "\t\tself.msgobj = None"
				f.puts "\t\tself.chars = []"
				f.puts "\t\tself.messagehandler = None"
				f.puts "\t\tself.errorhandler = None"
				if mode == "debug"
					f.puts "\t\tself.verbose = True"
				else
					f.puts "\t\tself.verbose = False"
				end
				f.puts ""
				f.puts "\t\tself.debug(\"=> parser initialised\")"
				f.puts ""
				f.puts "\tdef debug(self, str):"
				f.puts "\t\tif self.verbose:"
				f.puts "\t\t\tprint str"
				f.puts ""
				f.puts "\tdef setmessagehandler(self, handler):"
				f.puts "\t\tself.messagehandler = handler"
				f.puts ""
				f.puts "\tdef seterrorhandler(self, handler):"
				f.puts "\t\tself.errorhandler = handler"
				f.puts ""
				f.puts "\tdef firemessage(self, name, message):"
				f.puts "\t\tif self.messagehandler:"
				f.puts "\t\t\tself.messagehandler(name, message)"
				f.puts "\t\telse:"
				f.puts "\t\t\tprint \"[parser] warning: no message handler set!\""
				f.puts ""
				f.puts "\tdef error(self, str):"
				f.puts "\t\tif self.errorhandler:"
				f.puts "\t\t\tself.errorhandler(str)"
				f.puts "\t\telse:"
				f.puts "\t\t\tprint \"[parser] warning: no error handler set!\""
				f.puts "\t\t\tprint str"
				f.puts ""
				f.puts "\tdef setverbose(self, verbose):"
				f.puts "\t\tself.verbose = verbose"
				f.puts ""
				f.puts "\tdef characters(self, content):"
				f.puts "\t\tself.debug(\"=> content \" + content)"
				f.puts "\t\tself.chars.append(content)"
				f.puts ""
				f.puts "\tdef endElement(self, name):"
				f.puts "\t\tself.debug(\"=> end element \" + name)"
				f.puts ""
				f.puts "\t\tif self.valid:"
				f.puts "\t\t\tif self.message == None:"
				f.puts "\t\t\t\tif name == \"ggzcomm-xml\":"
				f.puts "\t\t\t\t\tself.valid = False"
				f.puts "\t\t\t\t\tself.debug(\" -> end session\")"
				f.puts "\t\t\t\telse:"
				f.puts "\t\t\t\t\tself.error(\"expected ggzcomm-xml\")"
				@eventlist.each do |event|
					f.puts "\t\t\telif self.message == \"" + event.name + "\":"
					f.puts "\t\t\t\tif name == \"" + event.name + "\":"
					f.puts "\t\t\t\t\tself.debug(\" -> fire options handler!\")"
					f.puts "\t\t\t\t\tself.firemessage(self.message, self.msgobj)"
					f.puts "\t\t\t\t\tself.message = None"
					f.puts "\t\t\t\t\tself.msgobj = None"
					event.datalist.each do |data, type, option|
						next if data == '%SEQ'
						next if data == '%ENDSEQ'
						next if data == '%EVAL'
						next if data == '%ENDEVAL'
						f.puts "\t\t\t\telif name == \"" + data + "\":"
						f.puts "\t\t\t\t\tself.debug(\"--- \" + str(self.chars))"
						if type == "int"
							expr = "int(\"\".join(self.chars))"
						else
							expr = "\"\".join(self.chars)"
						end
						f.puts "\t\t\t\t\tself.msgobj." + data + " = " + expr
					end
					if event.datalist.length > 0
						f.puts "\t\t\t\telse:"
						f.puts "\t\t\t\t\tself.error(\"expected " + event.name + " message part\")"
					else
						f.puts "\t\t\t\t\tself.error(\"invalid " + event.name + " message part\")"
					end
				end
				f.puts "\t\telse:"
				f.puts "\t\t\tself.error(\"invalid state\")"
				f.puts ""
				f.puts "\tdef startElement(self, name, attrs):"
				f.puts "\t\tself.debug(\"=> start element \" + name)"
				f.puts ""
				f.puts "\t\tif not self.valid:"
				f.puts "\t\t\tif name == \"ggzcomm-xml\":"
				f.puts "\t\t\t\tself.valid = True"
				f.puts "\t\t\t\tself.debug(\" -> start session!\")"
				f.puts "\t\telse:"
				f.puts "\t\t\tif self.message == None:"
				ifword = "if"
				@eventlist.each do |event|
					f.puts "\t\t\t\t" + ifword + " name == \"" + event.name + "\":"
					f.puts "\t\t\t\t\tself.message = name"
					f.puts "\t\t\t\t\tself.msgobj = " + event.name + "()"
					f.puts "\t\t\t\t\tself.debug(\" -> " + event.name + " start\")"
					ifword = "elif"
				end
				f.puts "\t\t\t\telse:"
				f.puts "\t\t\t\t\tself.error(\"expected message\")"
				@eventlist.each do |event|
					f.puts "\t\t\telif self.message == \"" + event.name + "\":"
					ifword = "if"
					event.datalist.each do |data, type, option|
						next if data == '%SEQ'
						next if data == '%ENDSEQ'
						next if data == '%EVAL'
						next if data == '%ENDEVAL'
						f.puts "\t\t\t\t" + ifword + " name == \"" + data + "\":"
						f.puts "\t\t\t\t\tself.debug(\"+++\")"
						f.puts "\t\t\t\t\tself.chars = []"
						ifword = "elif"
					end
					if event.datalist.length > 0
						f.puts "\t\t\t\telse:"
						f.puts "\t\t\t\t\tself.error(\"expected " + event.name + " message part\")"
					else
						f.puts "\t\t\t\t\tself.error(\"invalid " + event.name + " message part\")"
					end
				end
				f.puts "\t\t\telse:"
				f.puts "\t\t\t\tself.error(\"invalid message state\")"
				f.puts ""
				f.puts "class Device:"
				f.puts "\tdef __init__(self, socket):"
				f.puts "\t\tself.socket = socket"
				f.puts ""
				f.puts "\tdef write(self, s):"
				f.puts "\t\tself.socket.send(s)"
				f.puts ""
				f.puts "class Stream:"
				f.puts "\tdef __init__(self, socket):"
				f.puts "\t\tself.socket = socket"
				f.puts ""
				f.puts "\tdef getByteStream(self):"
				f.puts "\t\treturn self"
				f.puts ""
				f.puts "\tdef getSystemId(self):"
				f.puts "\t\treturn \"ggzcomm\""
				f.puts ""
				f.puts "\tdef read(self, bufsize):"
				f.puts "\t\treturn self.socket.recv(bufsize)"
			end
			f.puts ""
			f.puts "class " + @engine + ":"
			if @constants.length > 0 or xmlcode then
				f.puts "\tdef __init__(self):"
				if xmlcode:
					f.puts "\t\tself.parser = Parser()"
					#f.puts ""
				else
					@constants.each do |name, value|
						next if not value
						f.puts "\t\tself." + varname(name) + " = " + value
					end
				end
				f.puts ""
			end
			#@eventlist.each do |event|
			#	event.datalist.each do |data, type, option|
			#		next if data == '%SEQ'
			#		next if data == '%ENDSEQ'
			#		next if data == '%EVAL'
			#		next if data == '%ENDEVAL'
			#		f.puts "\t\tself." + data + " = None"
			#	end
			#end
			#f.puts ""
			f.puts "\t\tself.notifier = None"
			f.puts "\t\tself.fd = -1"
			f.puts "\t\tself.socket = -1"
			f.puts "\t\tself.ret = 0"
			f.puts "\t\tself.requirelink = 0"
			f.puts "\t\tself.nextlink = 0"
			f.puts "\t\tself.writer = None"
			f.puts ""
			@eventlist.each do |event|
				if event.section != role then
					#f.print "static "
				end
				if event.section != role
					f.puts "\tdef ggzcomm_" + event.name + "(self):"
				else
					f.puts "\tdef ggzcomm_" + event.name + "(self, msgobj):"
				end
				if not xmlcode
					f.puts event.network_python(role, @opcodetype)
					if links[event.name] then
						f.puts ""
						f.puts "\t\tself.requirelink = 1"
						f.puts "\t\tself.nextlink = self." + links[event.name]
					end
				else
					f.puts "\t\tself.writer.startElement(\"" + event.name + "\")"
					event.datalist.each do |data, type, option|
						next if data == '%SEQ'
						next if data == '%ENDSEQ'
						next if data == '%EVAL'
						next if data == '%ENDEVAL'
						f.puts "\t\tself.writer.startElement(\"" + data + "\")"
						f.puts "\t\ts = str(msgobj." + data + ")"
						f.puts "\t\tself.writer.characters(s, 0, len(s))"
						f.puts "\t\tself.writer.endElement(\"" + data + "\")"
					end
					f.puts "\t\tself.writer.endElement(\"" + event.name + "\")"
				end
				f.puts ""
			end
			if not xmlcode:
				f.puts "\tdef getbyte(self):"
				f.puts "\t\topstr = self.socket.recv(1)"
				f.puts "\t\tif len(opstr) < 1:"
				f.puts "\t\t\traise Exception()"
				f.puts "\t\top = ord(opstr[0])"
				f.puts "\t\treturn op"
				f.puts ""
				f.puts "\tdef getint(self):"
				f.puts "\t\topstr = self.socket.recv(4)"
				f.puts "\t\tif len(opstr) < 4:"
				f.puts "\t\t\traise Exception()"
				f.puts "\t\tc1 = ord(opstr[0])"
				f.puts "\t\tc2 = ord(opstr[1])"
				f.puts "\t\tc3 = ord(opstr[2])"
				f.puts "\t\tc4 = ord(opstr[3])"
				f.puts "\t\top = c1 * 256 * 256 * 256 + c2 * 256 * 256 + c3 * 256 + c4"
				f.puts "\t\tif socket.ntohl(op) == op:"
				f.puts "\t\t\top = socket.ntohl(op)"
				f.puts "\t\treturn op"
				f.puts ""
				f.puts "\tdef getstring(self):"
				f.puts "\t\tlength = self.getint()"
				f.puts "\t\topstr = self.socket.recv(length)"
				f.puts "\t\tif len(opstr) < length:"
				f.puts "\t\t\traise Exception()"
				f.puts "\t\treturn opstr"
				f.puts ""
				f.puts "\tdef sendint(self, int):"
				f.puts "\t\tif socket.ntohl(int) == int:"
				f.puts "\t\t\tint = socket.ntohl(int)"
				f.puts "\t\tc1 = (int >> 24) & 0xFF"
				f.puts "\t\tc2 = (int >> 16) & 0xFF"
				f.puts "\t\tc3 = (int >> 8) & 0xFF"
				f.puts "\t\tc4 = (int >> 0) & 0xFF"
				f.puts "\t\ts = chr(c1) + chr(c2) + chr(c3) + chr(c4)"
				f.puts "\t\tself.socket.send(s)"
				f.puts ""
				f.puts "\tdef sendbyte(self, byte):"
				f.puts "\t\tself.socket.send(chr(byte))"
				f.puts ""
				f.puts "\tdef sendstring(self, str):"
				f.puts "\t\tself.sendint(len(str))"
				f.puts "\t\tself.socket.send(str)"
				f.puts ""
			end
			f.puts "\tdef ggzcomm_network_main(self):"
			if xmlcode
				f.puts "\t\tstream = Stream(self.socket)"
				f.puts "\t\txml.sax.parse(stream, self.parser)"
			else
				if @opcodetype == "int"
					f.puts "\t\topcode = self.getint()"
				elsif @opcodetype == "byte"
					f.puts "\t\topcode = self.getbyte()"
				end
				f.puts ""
				f.puts "\t\tif(self.requirelink):"
				f.puts "\t\t\tif opcode != self.nextlink:"
				f.puts "\t\t\t\traise Exception()"
				f.puts "\t\t\tself.requirelink = 0"
				f.puts ""
				ifword = "if"
				@eventlist.each do |event|
					if event.section != role
						f.puts "\t\t" + ifword + " opcode == self." + event.name + ":\n"
						f.puts "\t\t\tself.ggzcomm_" + event.name() + "()\n"
						ifword = "elif"
					end
				end
				f.puts "\t\telse:"
				f.puts "\t\t\traise Exception()"
			end
			f.puts ""
			f.puts "\tdef ggzcomm_set_notifier_callback(self, notifier):"
			f.puts "\t\tself.notifier = notifier"
			if xmlcode:
				f.puts "\t\tself.parser.setmessagehandler(notifier)"
				# FIXME: error handler? seterrorhandler!
			end
			f.puts ""
			f.puts "\tdef ggzcomm_set_fd(self, fd):"
			f.puts "\t\tself.fd = fd"
			f.puts "\t\tself.socket = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)"
			if xmlcode
				f.puts "\t\tdevice = Device(self.socket)"
				f.puts "\t\tself.writer = xml.sax.writer.XmlWriter(device)"
				f.puts "\t\tself.writer.startElement(\"ggzcomm-xml\")"
			end
			f.puts ""
			f.puts "\tdef ggzcomm_set_socket(self, socket):"
			f.puts "\t\tself.socket = socket"
			f.puts "\t\tself.fd = socket.fileno()"
			if xmlcode
				f.puts "\t\tdevice = Device(self.socket)"
				f.puts "\t\tself.writer = xml.sax.writer.XmlWriter(device)"
				f.puts "\t\tself.writer.startElement(\"ggzcomm-xml\")"
			end
			f.close
		elsif format == "ruby" and library == "socket"
			f = File.new(basename + ".rb", "w")
			f.puts "### Generated by GGZComm/ruby version " + $version.to_s
			f.puts "### Protocol '" + @engine + "', version '" + @version + "'"
			f.puts ""
			f.puts "require 'socket'"
			f.puts ""
			f.puts "class " + @engine.capitalize
			if @constants.length > 0 then
				f.puts "\tdef initialize"
				@constants.each do |name, value|
					next if not value
					f.puts "\t\t@" + varname(name) + " = " + value
				end
			end
			f.puts ""
			@eventlist.each do |event|
				event.datalist.each do |data, type, option|
					next if data == '%SEQ'
					next if data == '%ENDSEQ'
					next if data == '%EVAL'
					next if data == '%ENDEVAL'
					f.puts "\t\t@" + data + " = nil"
				end
			end
			f.puts ""
			f.puts "\t\t@notifier = nil"
			f.puts "\t\t@fd = -1"
			f.puts "\t\t@socket = nil"
			f.puts "\t\t@ret = 0"
			f.puts "\t\t@requirelink = 0"
			f.puts "\t\t@nextlink = 0"
			f.puts "\tend"
			f.puts ""
			@eventlist.each do |event|
				if event.section != role then
					#f.print "static "
				end
				f.puts "\tdef ggzcomm_" + event.name
				f.puts event.network_ruby(role)
				if links[event.name] then
					f.puts ""
					f.puts "\t\t@requirelink = 1"
					f.puts "\t\t@nextlink = @" + links[event.name]
				end
				f.puts "\tend"
				f.puts ""
			end
			f.puts "\tdef getint"
			f.puts "\t\topstr = @socket.recv(4)"
			f.puts "\t\tif opstr.len < 4"
			f.puts "\t\t\traise 'ggzcommerror'"
			f.puts "\t\tend"
			f.puts "\t\tc1 = opstr[0].to_i"
			f.puts "\t\tc2 = opstr[1].to_i"
			f.puts "\t\tc3 = opstr[2].to_i"
			f.puts "\t\tc4 = opstr[3].to_i"
			f.puts "\t\top = c1 * 256 * 256 * 256 + c2 * 256 * 256 + c3 * 256 + c4"
			f.puts "\t\t# FIXME: ntohl()"
			f.puts "\t\treturn op"
			f.puts "\tend"
			f.puts ""
			f.puts "\tdef getstring"
			f.puts "\t\tlength = getint()"
			f.puts "\t\topstr = @socket.recv(length)"
			f.puts "\t\tif opstr.len < length"
			f.puts "\t\t\traise 'ggzcommerror'"
			f.puts "\t\tend"
			f.puts "\t\treturn opstr"
			f.puts "\tend"
			f.puts ""
			f.puts "\tdef ggzcomm_network_main"
			if @opcodetype == "int"
				f.puts "\t\topcode = getint()"
			elsif @opcodetype == "byte"
				f.puts "\t\topcode = getbyte()"
			end
			f.puts ""
			f.puts "\t\tif @requirelink"
			f.puts "\t\t\tif opcode != @nextlink"
			f.puts "\t\t\t\traise 'ggzcommerror'"
			f.puts "\t\t\tend"
			f.puts "\t\t\t@requirelink = 0"
			f.puts "\t\tend"
			f.puts ""
			@eventlist.each do |event|
				if event.section != role
					f.puts "\t\tif opcode == " + event.name + "\n"
					f.puts "\t\t\tggzcomm_" + event.name() + "\n"
					f.puts "\t\tend"
				end
			end
			f.puts "\tend"
			f.puts ""
			f.puts "\tdef ggzcomm_set_notifier_callback(notifier)"
			f.puts "\t\t@notifier = notifier"
			f.puts "\tend"
			f.puts ""
			f.puts "\tdef ggzcomm_set_fd(fd)"
			f.puts "\t\t@fd = fd"
			f.puts "\t\t@socket = Socket.for_fd(fd)"
			f.puts "\tend"
			f.puts "end"
			f.puts ""
			f.close
		else
			str = "Unknown output format and library combination" + "\n"
			str += "Format was: '" + format + "', "
			str += "only 'c' and 'c++' and 'python' and 'ruby' are allowed" + "\n"
			str += "Library was: '" + library + "', "
			str += "only 'easysock' and 'dio' and 'qt' and 'socket' are allowed"
			error str
		end
	end

	# Bail out with an error message
	def error(errormessage)
		puts "Error!"
		puts errormessage
		exit -1
	end
end

file = nil
language = nil
interface = nil
role = nil
mode = nil
xmlcode = false

skipper = false
for index in (0..ARGV.length - 1)
	if skipper then
		skipper = false
		next
	end
	arg = ARGV[index]
	case arg
		when "-h", "--help"
			puts "GGZ Communication Protocol Generator (ggzcommgen)"
			puts "Call: ggzcommgen [options] <filename> (for filename.protocol)"
			puts "[-l | --language ] The language to use: c, c++, python, ruby"
			puts "[-i | --interface] The networking library: easysock, dio, qt, qt4, mnet, socket"
			puts "[-r | --role     ] The usage role: client, server"
			puts ""
			puts "[-m | --mode     ] Generation mode: normal, debug"
			puts "[-x | --xml      ] Produce XML networking code"
			puts ""
			puts "[-h | --help     ] This help screen"
			puts "[-v | --version  ] Display version number"
			exit
		when "-r", "--role"
			skipper = true
			role = ARGV[index + 1]
		when "-l", "--language"
			skipper = true
			language = ARGV[index + 1]
		when "-i", "--interface"
			skipper = true
			interface = ARGV[index + 1]
		when "-m", "--mode"
			skipper = true
			mode = ARGV[index + 1]
		when "-x", "--xml"
			xmlcode = true
		when "-v", "--version"
			puts $version
			exit
		else
			if arg =~ /^-/
				puts "Unknown option: " + arg
				exit -1
			else
				file = arg
			end
	end
end

if not file
	puts "No file given."
	exit -1
end
if not interface
	puts "Need to specify a library interface."
	exit -1
end
if not language
	puts "Need to specify a language."
	exit -1
end
if not role
	puts "Need to specify a role."
	exit -1
end

if not mode
	mode = "normal"
else
	if not ["normal", "debug"].member?(mode)
		puts "Mode must be one of 'normal', 'debug'."
		exit -1
	end
end

# Test application
comm = GGZComm.new
begin
	comm.load(file)
	comm.output(language, interface, role, mode, xmlcode)
rescue => msg
	puts "Something went wrong. Nothing was generated:"
	puts msg
	exit -1
end

