#!/usr/bin/perl

use strict;
use XML::DOM;


	# the global variables
	my $filename_callid_h;
	my $filename_rpcalls_h;
	my $filename_rpcalls_cpp;
	my $filename_dbbridge_h;
	my $filename_dbbridge_cpp;
	my $filename_state_h;
	my $filename_state_cpp;
	my $filename_rpcdata_h;

	my $state_all_content;
	my $callid_count = 0;
	my %protocol_types = ();
	my %rpc_types = ();
	my %protocol_names = ();
	my %rpc_names = ();
	my $namespace = "GNET";
	my $file_prefix;
	my $name_prefix;
	my $rpc_base_default = "Rpc";

	my $file_callid_h;
	my $file_rpcalls_h;
	my $file_rpcalls_cpp;
	my $file_dbbridge_h;
	my $file_dbbridge_cpp;
	my $file_state_h;
	my $file_state_cpp;

	generateit();

sub generateit
{
	# parse
	my $file_src = "rpcalls.xml";
	$file_src = @ARGV[0] if( @ARGV > 0 );

	my $parser = new XML::DOM::Parser;
	my $doc = $parser->parsefile( $file_src );

	# application name
	$namespace = "GNET";
	my $applications = $doc-> getElementsByTagName ("application");
	if( $applications->getLength > 0 )
	{
		my $application = $applications->item (0);
		my $applicationnameattr = $application->getAttributeNode ("name");
		$namespace = $applicationnameattr->getValue if $applicationnameattr;
		my $rpcbasedefaultattr = $application->getAttributeNode ("rpcbasedefault");
		$rpc_base_default = $rpcbasedefaultattr->getValue if $rpcbasedefaultattr;
	}

	$file_prefix = lc($namespace) . "_" if $namespace ne "GNET";

	$filename_callid_h = $file_prefix . "callid.hxx";
	$filename_rpcalls_h = $file_prefix . "rpcalls.hxx";
	$filename_rpcalls_cpp = $file_prefix . "rpcalls.cxx";
	$filename_dbbridge_h = $file_prefix . "dbbridge.hxx";
	$filename_dbbridge_cpp = $file_prefix . "dbbridge.cxx";
	$filename_state_h = $file_prefix . "state.hxx";
	$filename_state_cpp = $file_prefix . "state.cxx";
	$filename_rpcdata_h = $file_prefix . "rpcdata.hxx";

	generate_rpcalls( $doc );

	print "\n";

	# Avoid memory leaks - cleanup circular references for garbage collection
	$doc->dispose;
}


##########################################################################
#                                                                        #
# The following is subroutines that generate rpcalls code.               #
#                                                                        #
##########################################################################

sub generate_rpcalls
{
	my $doc = shift;

	# begin
	$file_callid_h = new FileHandle;
	$file_callid_h->open( ">$filename_callid_h" );
	$file_rpcalls_h = new FileHandle;
	$file_rpcalls_h->open( ">$filename_rpcalls_h" );
	$file_rpcalls_cpp = new FileHandle;
	$file_rpcalls_cpp->open( ">$filename_rpcalls_cpp" );
	$file_state_h = new FileHandle;
	$file_state_h->open( ">$filename_state_h" );
	$file_state_cpp = new FileHandle;
	$file_state_cpp->open( ">$filename_state_cpp" );


	print_callid_header( $file_callid_h );
	print_rpcalls_h_header( $file_rpcalls_h );
	print_rpcalls_cpp_header( $file_rpcalls_cpp );

	# generate
	print "\ngenerating code for client ...\n";

	my $table_count = extractdoc_tables( $doc );
	extractdoc_rpcs( $doc );
	extractdoc_protocols( $doc );
	extractdoc_states( $doc );
	extractdoc_managers( $doc );

	# finish
	print "\tgenerate code for client finished.\n";

	# end
	print_callid_tail( $file_callid_h );
	print_rpcalls_h_tail( $file_rpcalls_h );
	print_rpcalls_cpp_tail( $file_rpcalls_cpp );

	$file_callid_h->close();
	$file_rpcalls_h->close();
	$file_rpcalls_cpp->close();
	$file_state_h->close();
	$file_state_cpp->close();

	if( $table_count > 0 )
	{	generate_dbbridge( $doc );	}
}

##########################################################################
#                                                                        #
# The following is subroutines that generate code for table element.     #
#                                                                        #
##########################################################################

sub extractdoc_tables
{
	my $doc = shift;

	# pass through all tables
	my $tables = $doc-> getElementsByTagName ("table");
	my $n = $tables->getLength;

	# write client file
	for (my $i = 0; $i < $n; $i++)
	{
		my $table = $tables->item ($i);
		extract_table( $table );
	}
	return $n;
}

sub print_callid_header
{
	my $file = shift;

	my $temp = uc($filename_callid_h);
	$temp =~ s/\./\_/g;

	$file->print( "/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.\
\
#ifndef __$namespace" . "_$temp\
#define __$namespace" . "_$temp\
\
namespace $namespace\
{\
\
	enum CallID {\n" );
}

sub print_callid_tail
{
	my $file = shift;
	$file->print( "\t};\n\n};\n\n#endif\n" );
}

sub print_rpcalls_h_header
{
	my $file = shift;

	my $temp = uc($filename_rpcalls_h);
	$temp =~ s/\./\_/g;

	$file->print( "/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.\
\
#ifndef __$namespace" . "_$temp\
#define __$namespace" . "_$temp\n\n" );

}

sub print_rpcalls_h_tail
{
	my $file = shift;
	$file->print( "\n#endif\n" );
}

sub print_rpcalls_cpp_header
{
	my $file = shift;

	$file->print( "/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.\
\
#include \"$filename_rpcalls_h\"\
\
namespace $namespace\
{\n\n" );

}

sub print_rpcalls_cpp_tail
{
	my $file = shift;
	$file->print( "\n};\n" );
}

sub extract_table
{
	my $table = shift;

	my $tablenameattr = $table->getAttributeNode ("name");
	my $tablename = $tablenameattr->getValue;
	my $filename = lc($tablename) . ".hxx";
	my $vars = $table->getElementsByTagName ("variable");

	my $tablemethodattr = $table->getAttributeNode ("method");
	my $tablemethod = $tablemethodattr->getValue if $tablemethodattr;
	my @tablemethods = split( /;/, $tablemethod );

	my $maxsizeattr = $table->getAttributeNode ("maxsize");
	my $maxsize = $maxsizeattr->getValue if $maxsizeattr;

	my $priorattr = $table->getAttributeNode ("prior");
	my $prior = $priorattr->getValue if $priorattr;

	my $timeoutattr = $table->getAttributeNode ("timeout");
	my $timeout = $timeoutattr->getValue if $timeoutattr;

	print "\tgenerating $tablename ...\n";

	my $file = new FileHandle;
	if( $file->open( ">$filename" ) )
	{
		print_header( $file, $filename, 1, 1, 1, 0, 0 );

		print_classdefheader( $file, $tablename, "GNET::Rpc::Data" );
		print_variables( $file, $vars );
		print_rpc_constructor( $file, $tablename, $vars );
		print_marshalmethods( $file, $vars );
		print_classdeftail( $file );

		print_variable_classes( $file, $tablename, $vars );

		print_method_classes( $file, $tablename, $maxsize, $prior, $timeout, $vars, @tablemethods );

		print_tail( $file, lc($tablename) );

		$file->close();

		$file_rpcalls_h->print( "#include \"$filename\"\n" );
	}

}


sub print_header
{
	my ($file, $filename, $cannot_modify, $use_dbbridge, $use_callid, $use_state, $use_rpcdata) = @_;
	$filename = uc($filename);
	$filename =~ s/\./\_/g;

	my $cannot_modify_content;
	if( $cannot_modify )
	{
		$cannot_modify_content="/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.\n";
	}

	my $use_dbbridge_content;
	if( $use_dbbridge )
	{	$use_dbbridge_content = "\n#ifdef _RPC_SERVER\n#include \"$filename_dbbridge_h\"\n#endif\n\n";	}

	my $use_callid_content;
	if( $use_callid )
	{	$use_callid_content = "#include \"$filename_callid_h\"";	}

	my $use_state_content;
	if( $use_state )
	{	$use_state_content = "#include \"$filename_state_h\"";	}

	my $use_rpcdata_content;
	if( $use_rpcdata )
	{	$use_rpcdata_content = "#include \"$filename_rpcdata_h\"";	}

	$file->print( "$cannot_modify_content\
#ifndef __$namespace" . "_$filename\
#define __$namespace" . "_$filename\
\
#include \"rpcdefs.h\"\
#include \"$filename_callid_h\"\
#include \"$filename_state_h\"\
#include \"$filename_rpcdata_h\"\
$use_dbbridge_content\
/**/\
namespace $namespace\
{\n" );
}

sub print_classdefheader
{
	my $file = shift;
	my $classname = shift;
	my $father_classname = shift;

	$file->print( "\
	class $classname : public $father_classname\
	{\n" );
}

sub print_classdeftail
{
	my $file = shift;

	$file->print( "\n\t};\n" );
}

sub print_tail
{
	my ($file, $filename) = @_;
	my $include_content;
	if( defined $filename )
	{
		$include_content = "\n#include \"$filename.hh\"\n";
	}

	$file->print( "\n$include_content\n};\n#endif\n" );
}

sub print_variables
{
	my $file = shift;
	my $vars = shift;
	my $callid = shift;

	$file->print( "\tpublic:\n" );

	my $n = $vars->getLength;

	for (my $i = 0; $i < $n; $i++)
	{
		my $var = $vars->item ($i);

		my $nameattr = $var->getAttributeNode ("name");
		my $typeattr = $var->getAttributeNode ("type");
		my $sizeattr = $var->getAttributeNode ("size");
		my $attrattr = $var->getAttributeNode ("attr");

		my $name = $nameattr->getValue;
		my $type = $typeattr->getValue;
		my $size = $sizeattr->getValue if $sizeattr;
		my $attr = $attrattr->getValue if $attrattr;

		$file->print( "\t\t$type $name" );
		$file->print( "[$size]" ) if $size;
		$file->print( ";" );
		$file->print( "\t//$attr" ) if $attr;
		$file->print( "\n" );
	}
	$file->print( "\t\tenum { PROTOCOL_TYPE = $callid };" ) if( $callid );
	$file->print( "\n" );
}

sub print_rpc_constructor
{
	my $file = shift;
	my $classname = shift;
	my $vars = shift;

	my $n = $vars->getLength;

	my ($evaluate1_1, $evaluate1_2, $evaluate1_3);
	my ($evaluate2_1, $evaluate2_2);
	my $evaluate3;

	my $isfirst = 1;
	my $count_default = 0;
	for (my $i = 0; $i < $n; $i++)
	{
		my $var = $vars->item ($i);

		my $nameattr = $var->getAttributeNode ("name");
		my $typeattr = $var->getAttributeNode ("type");
		my $sizeattr = $var->getAttributeNode ("size");
		my $attrattr = $var->getAttributeNode ("attr");
		my $defaultattr = $var->getAttributeNode ("default");

		my $name = $nameattr->getValue;
		my $type = $typeattr->getValue;
		my $size = $sizeattr->getValue if $sizeattr;
		my $attr = $attrattr->getValue if $attrattr;
		my $default = $defaultattr->getValue if $defaultattr;

		if( not defined $default and $attr eq 'primary' )
		{	print "\t\tError: No default value for $name in $classname.\n";	}

		my $count = $i + 1;

		if( defined $default )
		{
			$count_default ++;
			if( $type eq 'char' and $size )
			{
				$evaluate1_1 .= "," if (not defined $isfirst);
				$evaluate1_1 .= "const $type * l_$name";
				$evaluate1_1 .= " = \"$default\"" if defined $default;
				$evaluate1_1 .= "\n\t\t\t" if( 0 == ($count_default%3) and $count_default>1 and $count<$n);
				$evaluate1_3 .= "\t\t\tstrncpy( $name, l_$name, sizeof($name)-1 );\n";
				$evaluate1_3 .= "\t\t\t$name [sizeof($name)-1] = 0;\n";
			}
			else
			{
				$evaluate1_1 .= "," if (not defined $isfirst);
				$evaluate1_1 .= "$type l_$name";
				$evaluate1_1 .= " = $default" if defined $default;
				$evaluate1_1 .= "\n\t\t\t" if( 0 == ($count_default%3) and $count_default>1 and $count<$n);
				$evaluate1_2 .= "," if (not defined $isfirst);
				$evaluate1_2 .= "$name(l_$name)";
				$evaluate1_2 .= "\n\t\t\t" if( 0 == ($count_default%3) and $count_default>1 and $count<$n);
			}
			undef $isfirst;
		}
		
		if( $type eq 'char' and $size )
		{
			$evaluate2_2 .= "\t\t\tmemcpy( $name, rhs.$name, sizeof($name) );\n";
			$evaluate3 .= "\t\t\t\tmemcpy( $name, r->$name, sizeof($name) );\n";
		}
		else
		{
			$evaluate2_1 .= "$name(rhs.$name)";
			$evaluate2_1 .= "," if ($count < $n);
			$evaluate2_1 .= "\n\t\t\t" if( 0 == ($count % 3) and $count > 1 and $count < $n);
			$evaluate3 .= "\t\t\t\t$name = r->$name;\n";
		}
	}

	$evaluate1_2 = "\n\t\t\t: " . $evaluate1_2 if defined $evaluate1_2;
	my $evaluate1 = "($evaluate1_1)$evaluate1_2\n\t\t{\n$evaluate1_3\t\t}\n";
	my $evaluate2;
	$evaluate2 = ": $evaluate2_1" if $evaluate2_1;
	$evaluate2 .= "\n\t\t{\n$evaluate2_2\t\t}" if $evaluate2_2;
	$evaluate2 .= " { }" if not $evaluate2_2;

	$file->print( "\tpublic:\
		$classname $evaluate1\
		$classname(const $classname &rhs)\
			$evaluate2\n\
		Rpc::Data *Clone() const { return new $classname(*this); }\n\
		Rpc::Data& operator = (const Rpc::Data &rhs)\
		{\
			const $classname *r = dynamic_cast<const $classname *>(&rhs);\
			if (r && r != this)\
			{\n$evaluate3\t\t\t}\
			return *this;\
		}\n\
		$classname& operator = (const $classname &rhs)\
		{\
			const $classname *r = &rhs;\
			if (r && r != this)\
			{\n$evaluate3\t\t\t}\
			return *this;\
		}\n" );
}

sub print_marshalmethods
{
	my ($file, $vars, $debug, $classname) = @_;

	my $n = $vars->getLength;

	$file->print("\
		OctetsStream& marshal(OctetsStream & os) const\
		{\n");

	$file->print("\t\t\tSTAT_MIN5(\"$classname\",os.size());\n") if($debug);

	for (my $i = 0; $i < $n; $i++)
	{
		my $var = $vars->item ($i);

		my $nameattr = $var->getAttributeNode ("name");
		my $typeattr = $var->getAttributeNode ("type");
		my $sizeattr = $var->getAttributeNode ("size");

		my $name = $nameattr->getValue;
		my $type = $typeattr->getValue;
		my $size = $sizeattr->getValue if $sizeattr;
		
		if( $type eq 'char' and $size )
		{
			$file->print("\t\t\tGNET::Octets oct_$name($name,$name+strlen($name));\n");
			$file->print("\t\t\tos << oct_$name;\n");
		}
		#elsif( $type eq 'int' )
		#{	$file->print("\t\t\tos << CompactSINT($name);\n");	}
		#elsif( $type eq 'unsigned int' )
		#{	$file->print("\t\t\tos << CompactUINT($name);\n");	}
		else
		{	$file->print("\t\t\tos << $name;\n");	}
	}

	$file->print("\t\t\treturn os;\
		}\n\
		const OctetsStream& unmarshal(const OctetsStream &os)\
		{\n");

	$file->print("\t\t\tSTAT_MIN5(\"$classname\",os.size());\n") if($debug);

	for (my $i = 0; $i < $n; $i++)
	{
		my $var = $vars->item ($i);

		my $nameattr = $var->getAttributeNode ("name");
		my $typeattr = $var->getAttributeNode ("type");
		my $sizeattr = $var->getAttributeNode ("size");

		my $name = $nameattr->getValue;
		my $type = $typeattr->getValue;
		my $size = $sizeattr->getValue if $sizeattr;
		
		if( $type eq 'char' and $size )
		{
			$file->print("\t\t\tGNET::Octets oct_$name;\n");
			$file->print("\t\t\tos >> oct_$name;\n");
			$file->print("\t\t\tmemcpy( $name, oct_$name.begin(), std::min(sizeof($name)-1,oct_$name.size()) );\n");
			$file->print("\t\t\t$name [oct_$name.size()] = 0;\n");
		}
		#elsif( $type eq 'int' )
		#{	$file->print("\t\t\tos >> CompactSINT($name);\n");	}
		#elsif( $type eq 'unsigned int' )
		#{	$file->print("\t\t\tos >> CompactUINT($name);\n");	}
		else
		{	$file->print("\t\t\tos >> $name;\n");	}
	}

	$file->print("\t\t\treturn os;\
		}\n");

}

sub print_variable_classes
{
	my $file = shift;
	my $tablename = shift;
	my $vars = shift;

	my $n = $vars->getLength;
	my @var_primary;

	for (my $i = 0; $i < $n; $i++)
	{
		my $var = $vars->item ($i);

		my $nameattr = $var->getAttributeNode ("name");
		my $typeattr = $var->getAttributeNode ("type");
		my $sizeattr = $var->getAttributeNode ("size");
		my $attrattr = $var->getAttributeNode ("attr");
		my $defaultattr = $var->getAttributeNode ("default");
		my $methodattr = $var->getAttributeNode ("method");

		my $name = $nameattr->getValue;
		my $type = $typeattr->getValue;
		my $size = $sizeattr->getValue if $sizeattr;
		my $attr = $attrattr->getValue if $attrattr;
		my $default = $defaultattr->getValue if $defaultattr;
		my $method = $methodattr->getValue if $methodattr;

		my @methods = split( /;/, $method );

		if( $attr eq 'primary' or @methods > 0 )
		{
			print_variable_class( $file, $tablename, $name, $type, $size, $default );
		}

		@var_primary = ($name,$type,$size,$default) if( $attr eq 'primary' );
		if( $method =~ m/put/ )
		{
			print_putvariable_class( $file, $tablename, $name, $type, $size, $default, @var_primary );
		}
	}
}

sub print_variable_class
{
	my ($file, $tablename, $name, $type, $size, $default) = @_;

	my $classname = $tablename . "_" . $name;

	my $var_define = "$type $name";
	$var_define .= "[$size]" if $size;

	my ($evaluate1, $evaluate2, $evaluate3);

	if( $type eq 'char' and $size )
	{
		$evaluate1 .= "(const $type * l_$name";
		$evaluate1 .= " = \"$default\"" if defined $default;
		$evaluate1 .= ")\n\t\t{\n";
		$evaluate1 .= "\t\t\tstrncpy($name, l_$name, sizeof($name)-1);\n";
		$evaluate1 .= "\t\t\t$name [sizeof($name)-1] = 0;\n\t\t}\n";

		$evaluate2 .= "{ memcpy( $name, rhs.$name, sizeof($name) ); }";
		$evaluate3 .= "\t\t\t\tmemcpy( $name, r->$name, sizeof($name) );\n";
	}
	else
	{
		$evaluate1 .= "($type l_$name";
		$evaluate1 .= " = $default" if defined $default;
		$evaluate1 .= ") : $name(l_$name) { }";

		$evaluate2 .= ": $name(rhs.$name) { }";
		$evaluate3 .= "\t\t\t\t$name = r->$name;\n";
	}
	$evaluate1 = "( ) { }" if( not defined $default );

	$file->print( "\
	class $classname : public GNET::Rpc::Data\
	{\
	public:\
		$var_define;\n\
		$classname $evaluate1\
		$classname(const $classname &rhs)\
			$evaluate2\
		Rpc::Data *Clone() const { return new $classname(*this); }\
		Rpc::Data& operator = (const Rpc::Data &rhs)\
		{\
			const $classname *r = dynamic_cast<const $classname *>(&rhs);\
			if (r && r != this)\
			{\n$evaluate3\t\t\t}\
			return *this;\
		}\n" );

	$file->print( "\
		OctetsStream& marshal(OctetsStream & os) const" );

			if( $type eq 'char' and $size )
			{
				$file->print("\n\t\t{\n\t\t\tGNET::Octets oct_$name($name,$name+strlen($name));\n");
				$file->print("\t\t\treturn os << oct_$name;\n\t\t}" );
			}
			#elsif( $type eq 'int' )
			#{	$file->print(" { return os << CompactSINT($name); }");	}
			#elsif( $type eq 'unsigned int' )
			#{	$file->print(" { return os << CompactUINT($name); }");	}
			else
			{	$file->print(" { return os << $name; }");	}
	
			$file->print( "\
		const OctetsStream& unmarshal(const OctetsStream &os)" );

			if( $type eq 'char' and $size )
			{
				$file->print("\n\t\t{\n\t\t\tOctets oct_$name;\n");
				$file->print("\t\t\tos >> oct_$name;\n");
				$file->print("\t\t\tmemcpy( $name, oct_$name.begin(), std::min(sizeof($name)-2,oct_$name.size()) );\n");
				$file->print("\t\t\t$name [oct_$name.size()] = 0;\n");
				$file->print("\t\t\treturn os;\n\t\t}" );
			}
			#elsif( $type eq 'int' )
			#{	$file->print(" { return os >> CompactSINT($name); }");	}
			#elsif( $type eq 'unsigned int' )
			#{	$file->print(" { return os >> CompactUINT($name); }");	}
			else
			{	$file->print(" { return os >> $name; }");	}

	$file->print( "\n\t};\n" );
	
}

sub print_putvariable_class
{
	my ($file, $tablename, $name, $type, $size, $default,
		$name_primary, $type_primary, $size_primary, $default_primary) = @_;

	my $classname = $tablename . "_" . $name_primary . "_" . $name;

	my $var_define = "$type_primary $name_primary;\n";
	$var_define .= "\t\t$type $name";
	$var_define .= "[$size]" if $size;

	my ($evaluate1, $evaluate2, $evaluate3);
	if( $type eq 'char' and $size )
	{
		$evaluate1 .= "(";
		$evaluate1 .= "$type_primary l_$name_primary = $default_primary" if defined $default_primary;
		$evaluate1 .= "," if defined $default_primary and defined $default;
		$evaluate1 .= "const $type * l_$name = \"$default\"" if defined $default;
		$evaluate1 .= ")\n\t\t{\n";
		$evaluate1 .= "\t\t\t$name_primary = l_$name_primary;\n" if defined $default_primary;
		$evaluate1 .= "\t\t\tstrncpy($name, l_$name, sizeof($name)-1);\n" if defined $default;
		$evaluate1 .= "\t\t\t$name [sizeof($name)-1] = 0;\n" if defined $default;
		$evaluate1 .= "\t\t}\n";
		$evaluate2 .= ": $name_primary(rhs.$name_primary) { memcpy($name,rhs.$name,sizeof($name)); }";
		$evaluate3 .= "\t\t\t\t$name_primary = r->$name_primary;\n";
		$evaluate3 .= "\t\t\t\tmemcpy( $name, r->$name, sizeof($name) );\n";
	}
	else
	{
		$evaluate1 .= "(";
		$evaluate1 .= "$type_primary l_$name_primary = $default_primary" if defined $default_primary;
		$evaluate1 .= "," if defined $default_primary and defined $default;
		$evaluate1 .= "$type l_$name = $default" if defined $default;
		$evaluate1 .= ")\n";
		$evaluate1 .= "\t\t\t\t: " if defined $default_primary or defined $default;
		$evaluate1 .= "$name_primary(l_$name_primary)" if defined $default_primary;
		$evaluate1 .= ", " if defined $default_primary and defined $default;
		$evaluate1 .= "$name(l_$name)" if defined $default;
		$evaluate1 .= " { }";
		$evaluate2 .= ": $name_primary(rhs.$name_primary), $name(rhs.$name) { }";
		$evaluate3 .= "\t\t\t\t$name_primary = r->$name_primary;\n";
		$evaluate3 .= "\t\t\t\t$name = r->$name;\n";
	}

	$file->print( "\
	class $classname : public GNET::Rpc::Data\
	{\
	public:\
		$var_define;\n\
		$classname $evaluate1\
		$classname(const $classname &rhs)\
			$evaluate2\
		Rpc::Data *Clone() const { return new $classname(*this); }\
		Rpc::Data& operator = (const Rpc::Data &rhs)\
		{\
			const $classname *r = dynamic_cast<const $classname *>(&rhs);\
			if (r && r != this)\
			{\n$evaluate3\t\t\t}\
			return *this;\
		}\n" );

	$file->print( "\
		OctetsStream& marshal(OctetsStream & os) const" );

			if( $type eq 'char' and $size )
			{
				$file->print("\n\t\t{\n\t\t\tos << $name_primary;\n");
				$file->print("\t\t\tGNET::Octets oct_$name($name,$name+strlen($name));\n");
				$file->print("\t\t\treturn os << oct_$name;\n\t\t}" );
			}
			#elsif( $type eq 'int' )
			#{	$file->print(" { return os << $name_primary << CompactSINT($name); }");	}
			#elsif( $type eq 'unsigned int' )
			#{	$file->print(" { return os << $name_primary << CompactUINT($name); }");	}
			else
			{	$file->print(" { return os << $name_primary << $name; }");	}
	
			$file->print( "\
		const OctetsStream& unmarshal(const OctetsStream &os)" );

			if( $type eq 'char' and $size )
			{
				$file->print("\n\t\t{\n\t\t\tos >> $name_primary;\n");
				$file->print("\t\t\tGNET::Octets oct_$name;\n");
				$file->print("\t\t\tos >> oct_$name;\n");
				$file->print("\t\t\tmemcpy( $name, oct_$name.begin(), std::min(sizeof($name)-2,oct_$name.size()) );\n");
				$file->print("\t\t\t$name [oct_$name.size()] = 0;\n");
				$file->print("\t\t\treturn os;\n\t\t}" );
			}
			#elsif( $type eq 'int' )
			#{	$file->print(" { return os >> $name_primary >> CompactSINT($name); }");	}
			#elsif( $type eq 'unsigned int' )
			#{	$file->print(" { return os >> $name_primary >> CompactUINT($name); }");	}
			else
			{	$file->print(" { return os >> $name_primary >> $name; }");	}

	$file->print( "\n\t};\n" );
	
}

sub print_method_classes
{
	my ($file, $tablename, $maxsize, $prior, $timeout, $vars, @tablemethods) = @_;

	my $n = $vars->getLength;

	my @var_primary;

	for (my $i = 0; $i < $n; $i++)
	{
		my $var = $vars->item ($i);

		my $nameattr = $var->getAttributeNode ("name");
		my $typeattr = $var->getAttributeNode ("type");
		my $sizeattr = $var->getAttributeNode ("size");
		my $attrattr = $var->getAttributeNode ("attr");
		my $methodattr = $var->getAttributeNode ("method");

		my $name = $nameattr->getValue;
		my $type = $typeattr->getValue;
		my $size = $sizeattr->getValue if $sizeattr;
		my $attr = $attrattr->getValue if $attrattr;
		my $method = $methodattr->getValue if $methodattr;

		my @methods = split( /;/, $method );

		@var_primary = ($name,$type,$size) if( $attr eq 'primary' );
		next if( $attr eq 'primary' );
                
		for( my $k=0; $k<@methods; $k++ )
		{
			print_var_method( $file, $tablename, $maxsize, $prior, $timeout,
								$name, $type, $size, @methods[$k], @var_primary );
		}
	}

	{
		for( my $k=0; $k<@tablemethods; $k++ )
		{
			print_table_method( $file, $tablename, $maxsize, $prior, $timeout,
								@tablemethods[$k], @var_primary );
		}
	}
}

sub print_var_method
{
	my ($file, $tablename, $maxsize, $prior, $timeout, $name, $type, $size, $method, $name_primary, $type_primary, $size_primary ) = @_;

	my ($callid, $classname, $argumentclass, $resultclass);	

	my $filename_inl = lc($tablename) . ".hh";
	my $file_inl = new FileHandle;
	$file_inl->open( ">>$filename_inl" );

	my $priorpolicy_content = "{ return Rpc::PriorPolicy(); }";
	$priorpolicy_content = "{ return $prior; }" if defined $prior;

	my $sizepolicy_content = "{ return Rpc::SizePolicy(size); }";
	$sizepolicy_content = "{ return size <= $maxsize; }" if defined $maxsize;

	my $timepolicy_content = "{ return Rpc::TimePolicy(timeout); }";
	$timepolicy_content = "{ return timeout <= $timeout; }" if defined $timeout;

	if( $method eq 'getsyn' )
	{
		$callid = "RPC_" . uc($tablename) . "_GETSYN" . uc($name);
		$classname = "Rpc_" . $tablename . "_getsyn" . $name;
		$argumentclass = $tablename . "_" . $name_primary;
		$resultclass = $tablename . "_" . $name;

		my $content = "res->$name = v_all.$name;";
		$content = "memcpy( res->$name, v_all.$name, sizeof(res->$name) );" if $size; 

		$file->print("\
	class $classname : public SynRpc\
	{\
		GNET::Protocol *Clone() const { return new $classname(*this); }\
	public:\
		enum { PROTOCOL_TYPE = $callid };\
		$classname(Type type, Rpc::Data *argument, Rpc::Data *result)\
			: SynRpc(type, argument, result ) { }\
		$classname(const $classname &rhs) : SynRpc(rhs) { }\
		void Server(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid)\
		{\
#ifdef _RPC_SERVER\
			$argumentclass *arg = ($argumentclass *)argument;\
			$resultclass *res = ($resultclass *)result;\
			Marshal::OctetsStream key, value;\
			key << *arg;\
			if( 0 == DBBridge::buf_get_$tablename( key, value ) )\
			{\
				$tablename v_all;\
				value >> v_all;\
				$content\
			}\
#endif\
		}\
		void Client(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid);\
		void OnTimeout();\
		int  PriorPolicy() const $priorpolicy_content\
		bool SizePolicy(size_t size) const $sizepolicy_content\
		bool TimePolicy(int timeout) const $timepolicy_content\
	};\n");

	}
	if( $method eq 'get' )
	{
		$callid = "RPC_" . uc($tablename) . "_GET" . uc($name);
		$classname = "Rpc_" . $tablename . "_get" . $name;
		$argumentclass = $tablename . "_" . $name_primary;
		$resultclass = $tablename . "_" . $name;

		my $content = "res->$name = v_all.$name;";
		$content = "memcpy( res->$name, v_all.$name, sizeof(res->$name) );" if $size; 

		$file->print("\
	class $classname : public Rpc\
	{\
		GNET::Protocol *Clone() const { return new $classname(*this); }\
	public:\
		enum { PROTOCOL_TYPE = $callid };\
		$classname(Type type, Rpc::Data *argument, Rpc::Data *result)\
			: Rpc(type, argument, result ) { }\
		$classname(const $classname &rhs) : Rpc(rhs) { }\
		void Server(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid)\
		{\
#ifdef _RPC_SERVER\
			$argumentclass *arg = ($argumentclass *)argument;\
			$resultclass *res = ($resultclass *)result;\
			Marshal::OctetsStream key, value;\
			key << *arg;\
			if( 0 == DBBridge::buf_get_$tablename( key, value ) )\
			{\
				$tablename v_all;\
				value >> v_all;\
				$content\
			}\
#endif\
		}\
		void Client(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid);\
		void OnTimeout();\
		int  PriorPolicy() const $priorpolicy_content\
		bool SizePolicy(size_t size) const $sizepolicy_content\
		bool TimePolicy(int timeout) const $timepolicy_content\
	};\n");

	}
	if( $method eq 'put' )
	{
		$callid = "RPC_" . uc($tablename) . "_PUT" . uc($name);
		$classname = "Rpc_" . $tablename . "_put" . $name;
		$argumentclass = $tablename . "_" . $name_primary . "_" . $name;
		$resultclass = "RpcRetcode";
		my $keyclass = $tablename . "_" . $name_primary;

		my $content = "v_all.$name = arg->$name;";
		$content = "memcpy( v_all.$name, arg->$name, sizeof(v_all.$name) );" if $size; 

		$file->print("\
	class $classname : public Rpc\
	{\
		GNET::Protocol *Clone() const { return new $classname(*this); }\
	public:\
		enum { PROTOCOL_TYPE = $callid };\
		$classname(Type type, Rpc::Data *argument, Rpc::Data *result)\
			: Rpc(type, argument, result ) { }\
		$classname(const $classname &rhs) : Rpc(rhs) { }\
		void Server(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid)\
		{\
#ifdef _RPC_SERVER\
			$argumentclass *arg = ($argumentclass *)argument;\
			$resultclass *res = ($resultclass *)result;\
			Marshal::OctetsStream key, value_all;\
			$keyclass key_temp(arg->$name_primary);\
			key << key_temp;\
			if(0==(res->retcode=DBBridge::buf_get_$tablename(key,value_all)))\
			{\
				$tablename v_all;\
				value_all >> v_all;
				$content\
				Marshal::OctetsStream os_value_all;
				os_value_all << v_all;
				res->retcode = DBBridge::buf_put_$tablename(key,os_value_all);\
			}\
#endif\
		}\
		void Client(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid);\
		void OnTimeout();\
		int  PriorPolicy() const $priorpolicy_content\
		bool SizePolicy(size_t size) const $sizepolicy_content\
		bool TimePolicy(int timeout) const $timepolicy_content\
	};\n");

	}

	if( $classname )
	{
		my $method_client = $classname . "::Client";
		my $method_timeout = $classname . "::OnTimeout";
		if( not is_already_exists( $filename_inl, $method_client ) )
		{
			$file_inl->print( "\
inline void $method_client(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid)\
{\
#ifdef _RPC_CLIENT\
	// TODO\
	// $argumentclass *arg = ($argumentclass *)argument;\
	// $resultclass *res = ($resultclass *)result;\
#endif\
}\n" );
		}

		if( not is_already_exists( $filename_inl, $method_timeout ) )
		{
			$file_inl->print( "\
inline void $method_timeout( )\
{\
#ifdef _RPC_CLIENT\
	// TODO\
#endif\
}\n" );
		}

	}

	
	if( $callid )
	{
		$callid_count ++;

		my $type = $callid_count;
		if( defined $rpc_types{$type} )
		{
			print "\t\tError: duplicate rpc type.($callid . type = $type). see $filename_callid_h.\n";
			$file_callid_h->print( "\t\t// $callid\t=\t$type,\n" );
		}
		else
		{
			$file_callid_h->print( "\t\t$callid\t=\t$type,\n" );
			$rpc_types{$type} = $callid;
			$rpc_names{$callid} = $type;
		}

		$state_all_content .= "$callid,\n";

		$file_rpcalls_cpp->print( "static $classname __stub_$classname($callid, new $argumentclass, new $resultclass);\n" ) if $classname;
	}
	$file_inl->close();
}

sub print_table_method
{
	my ($file, $tablename, $maxsize, $prior, $timeout, $method, $name_primary, $type_primary, $size_primary ) = @_;

	my ($callid, $classname, $argumentclass, $resultclass);

	my $filename_inl = lc($tablename) . ".hh";
	my $file_inl = new FileHandle;
	$file_inl->open( ">>$filename_inl" );

	my $priorpolicy_content = "{ return Rpc::PriorPolicy(); }";
	$priorpolicy_content = "{ return $prior; }" if defined $prior;

	my $sizepolicy_content = "{ return Rpc::SizePolicy(size); }";
	$sizepolicy_content = "{ return size <= $maxsize; }" if defined $maxsize;

	my $timepolicy_content = "{ return Rpc::TimePolicy(timeout); }";
	$timepolicy_content = "{ return timeout <= $timeout; }" if defined $timeout;


	if( $method eq 'getsyn' )
	{
		$callid = "RPC_" . uc($tablename) . "_GETSYN";
		$classname = "Rpc_" . $tablename . "_getsyn";
		$argumentclass = $tablename . "_" . $name_primary;
		$resultclass = $tablename;

		$file->print("\
	class $classname : public SynRpc\
	{\
		GNET::Protocol *Clone() const { return new $classname(*this); }\
	public:\
		enum { PROTOCOL_TYPE = $callid };\
		$classname(Type type, Rpc::Data *argument, Rpc::Data *result)\
			: SynRpc(type, argument, result ) { }\
		$classname(const $classname &rhs) : SynRpc(rhs) { }\
		void Server(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid)\
		{\
#ifdef _RPC_SERVER\
			$argumentclass *arg = ($argumentclass *)argument;\
			$resultclass *res = ($resultclass *)result;\
			Marshal::OctetsStream key, value;\
			key << *arg;\
			if( 0 == DBBridge::buf_get_$tablename( key, value ) )\
				value >> *res;\
#endif
		}\
		void Client(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid);\
		void OnTimeout();\
		int  PriorPolicy() const $priorpolicy_content\
		bool SizePolicy(size_t size) const $sizepolicy_content\
		bool TimePolicy(int timeout) const $timepolicy_content\
	};\n");

	}
	if( $method eq 'get' )
	{
		$callid = "RPC_" . uc($tablename) . "_GET";
		$classname = "Rpc_" . $tablename . "_get";
		$argumentclass = $tablename . "_" . $name_primary;
		$resultclass = $tablename;

		$file->print("\
	class $classname : public Rpc\
	{\
		GNET::Protocol *Clone() const { return new $classname(*this); }\
	public:\
		enum { PROTOCOL_TYPE = $callid };\
		$classname(Type type, Rpc::Data *argument, Rpc::Data *result)\
			: Rpc(type, argument, result ) { }\
		$classname(const $classname &rhs) : Rpc(rhs) { }\
		void Server(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid)\
		{\
#ifdef _RPC_SERVER\
			$argumentclass *arg = ($argumentclass *)argument;\
			$resultclass *res = ($resultclass *)result;\
			Marshal::OctetsStream key, value;\
			key << *arg;\
			if( 0 == DBBridge::buf_get_$tablename( key, value ) )\
				value >> *res;\
#endif
		}\
		void Client(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid);\
		void OnTimeout();\
		int  PriorPolicy() const $priorpolicy_content\
		bool SizePolicy(size_t size) const $sizepolicy_content\
		bool TimePolicy(int timeout) const $timepolicy_content\
	};\n");

	}
	if( $method eq 'put' )
	{
		$callid = "RPC_" . uc($tablename) . "_PUT";
		$classname = "Rpc_" . $tablename . "_put";
		$argumentclass = $tablename;
		$resultclass = "RpcRetcode";
		my $keyclass = $tablename . "_" . $name_primary;

		$file->print("\
	class $classname : public Rpc\
	{\
		GNET::Protocol *Clone() const { return new $classname(*this); }\
	public:\
		enum { PROTOCOL_TYPE = $callid };\
		$classname(Type type, Rpc::Data *argument, Rpc::Data *result)\
			: Rpc(type, argument, result ) { }\
		$classname(const $classname &rhs) : Rpc(rhs) { }\
		void Server(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid)\
		{\
#ifdef _RPC_SERVER\
			$argumentclass *arg = ($argumentclass *)argument;\
			$resultclass *res = ($resultclass *)result;\
			Marshal::OctetsStream key, value_all;\
			$keyclass key_temp(arg->$name_primary);\
			key << key_temp;\
			value_all << *arg;
			res->retcode = DBBridge::buf_put_$tablename( key, value_all );\
#endif\
		}\
		void Client(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid);\
		void OnTimeout();\
		int  PriorPolicy() const $priorpolicy_content\
		bool SizePolicy(size_t size) const $sizepolicy_content\
		bool TimePolicy(int timeout) const $timepolicy_content\
	};\n");

	}

	if( $classname )
	{
		my $method_client = $classname . "::Client";
		my $method_timeout = $classname . "::OnTimeout";
		if( not is_already_exists( $filename_inl, $method_client ) )
		{
			$file_inl->print( "\
inline void $method_client(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid)\
{\
#ifdef _RPC_CLIENT\
	// TODO\
	// $argumentclass *arg = ($argumentclass *)argument;\
	// $resultclass *res = ($resultclass *)result;\
#endif\
}\n" );
		}
		if( not is_already_exists( $filename_inl, $method_timeout ) )
		{
			$file_inl->print( "\
inline void $method_timeout( )\
{\
#ifdef _RPC_CLIENT\
	// TODO\
#endif\
}\n" );
		}

	}

	if( $callid )
	{
		$callid_count ++;

		my $type = $callid_count;
		if( defined $rpc_types{$type} )
		{
			print "\t\tError: duplicate rpc type.($callid . type = $type). see $filename_callid_h.\n";
			$file_callid_h->print( "\t\t// $callid\t=\t$type,\n" );
		}
		else
		{
			$file_callid_h->print( "\t\t$callid\t=\t$type,\n" );
			$rpc_types{$type} = $callid;
			$rpc_names{$callid} = $type;
		}

		$state_all_content .= "$callid,\n";

		$file_rpcalls_cpp->print( "static $classname __stub_$classname($callid, new $argumentclass, new $resultclass);\n" ) if $classname;
	}
	$file_inl->close();
}

sub is_already_exists
{
	my ($filename, $content) = @_;

	my $ret;

	my $file = new FileHandle;
	if( $file->open( "<$filename" ) )
	{
		# my $file_content = <$file>;

		my $line;
		while( $line = $file->getline() )
		{
			$ret = ( $line =~ m/$content/ );
			last if( $ret );
		}
		$file->close();
	}
	return $ret;
}


##########################################################################
#                                                                        #
# The following is subroutines that generate code for rpc element.       #
#                                                                        #
##########################################################################

sub extractdoc_rpcs
{
	my $doc = shift;

	# write rpcdatas
	print "\tgenerating $filename_rpcdata_h ...\n";

	my $filename = $filename_rpcdata_h;
	my $file = new FileHandle;
	$file->open( ">$filename" );

	print_header( $file, $filename, 1, 0, 0, 0, 0 );
	my $rpcdatas = $doc->getElementsByTagName ("rpcdata");
	my $n = $rpcdatas->getLength;
	for (my $i = 0; $i < $n; $i++)
	{
		my $rpcdata = $rpcdatas->item ($i);

		my $classnameattr = $rpcdata->getAttributeNode ("name");
		my $classname = $classnameattr->getValue;
		my $attrattr = $rpcdata->getAttributeNode ("attr");
		my $attr = $attrattr->getValue if $attrattr;
		my $vars = $rpcdata->getElementsByTagName ("variable");

		print_classdefheader( $file, $classname, "GNET::Rpc::Data" );
		print_variables( $file, $vars );
		print_rpc_constructor( $file, $classname, $vars );
		print_marshalmethods( $file, $vars );
		print_classdeftail( $file );

		if( $attr eq 'vector' )
		{
			$file->print( "\ttypedef GNET::RpcDataVector<$classname>\t$classname" . "Vector;\n" );
		}
	}

	print_tail( $file );
	$file->close();

	$file_rpcalls_h->print( "#include \"$filename\"\n" );

	# write rpcs
	my $calls = $doc->getElementsByTagName ("rpc");
	my $n = $calls->getLength;

	# write
	for (my $i = 0; $i < $n; $i++)
	{
		my $call = $calls->item ($i);
		extract_rpc( $call );
	}
}

sub extract_rpc
{
	my $call = shift;

	my $callnameattr = $call->getAttributeNode ("name");
	my $argumentclassattr = $call->getAttributeNode ("argument");
	my $resultclassattr = $call->getAttributeNode ("result");
	my $callname = $callnameattr->getValue;
	my $argumentclass = $argumentclassattr->getValue;
	my $resultclass = $resultclassattr->getValue;

	my $typeattr = $call->getAttributeNode ("type");
	my $type = undef;
	$type = $typeattr->getValue if $typeattr;
	my $priorattr = $call->getAttributeNode ("prior");
	my $prior = $priorattr->getValue if $priorattr;
	my $maxsizeattr = $call->getAttributeNode ("maxsize");
	my $maxsize = $maxsizeattr->getValue if $maxsizeattr;
	my $timeoutattr = $call->getAttributeNode ("timeout");
	my $timeout = $timeoutattr->getValue if $timeoutattr;
	my $baseattr = $call->getAttributeNode ("base");
	my $baseclass = $rpc_base_default;
	$baseclass = $baseattr->getValue if $baseattr;
	my $debugattr = $call->getAttributeNode ("debug");
	my $debug = $debugattr->getValue if $debugattr;

	print "\tgenerating $callname ...\n";

	my $filename_inl = lc($callname) . ".inl";
	my $file_inl = new FileHandle;
	$file_inl->open( ">$filename_inl" );

	my $filename = lc($callname) . ".hh";
	my $file = new FileHandle;
	if( not -f $filename and $file->open( ">$filename" ) )
	{
		print_header( $file, $filename, 0, 0, 1, 0, 1 );

		$file->print("\
	class $callname : public $baseclass\
	{\
		#include \"$filename_inl\"\n");

		if( $baseclass eq 'ProxyRpc' )
		{
			$file->print("\
		bool Delivery(Manager::Session::ID proxy_sid, const OctetsStream& osArg)\
		{\
			// TODO\
			// $argumentclass arg;\
			// osArg >> arg;\
			if( SomeManager::GetInstance().SendP( *this ) )\
			{\
				return true;\
			}\
			else\
			{\
				SetResult($resultclass(ERR_DELIVER_SEND));\
				SendToSponsor();\
				return false;\
			}\
		}\n\
		void PostProcess(Manager::Session::ID proxy_sid,const OctetsStream& osArg,const OctetsStream& osRes)\
		{\
			// TODO\
			// $argumentclass arg;\
			// osArg >> arg;\
			// $resultclass res;\
			// osRes >> res;\
			// SetResult( &res ); // if you modified res, do not forget to call this. \
		}\n\
		void OnTimeout( )\
		{\
			// TODO\
		}\

	};\n");

		}
		else
		{
			$file->print("\
		void Server(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid)\
		{\
#ifdef _RPC_SERVER\
			// TODO\
			// $argumentclass *arg = ($argumentclass *)argument;\
			// $resultclass *res = ($resultclass *)result;\
#endif\
		}\n\
		void Client(Rpc::Data *argument, Rpc::Data *result, Manager *manager, Manager::Session::ID sid)\
		{\
#ifdef _RPC_CLIENT\
			// TODO\
			// $argumentclass *arg = ($argumentclass *)argument;\
			// $resultclass *res = ($resultclass *)result;\
#endif\
		}\n\
		void OnTimeout( )\
		{\
#ifdef _RPC_CLIENT\
			// TODO\
#endif\
		}\

	};\n");

		}

		print_tail( $file );
		$file->close();
	}

	my $callid = "RPC_" . uc($callname);
	print_rpc_inl( $file_inl, $callname, $baseclass, $argumentclass, $resultclass,
					$maxsize, $prior, $timeout, $debug, $callid );
	$file_inl->close();


	$file_rpcalls_h->print( "#include \"$filename\"\n" );


	if( not defined $type )
	{
		print "\t\tWarning: rpc $callname has no type, use default.\n";
		$callid_count ++;
		$type = $callid_count;
	}
	if( defined $rpc_types{$type} )
	{
		print "\t\tError: duplicate rpc type.($callname . type = $type). see $filename_callid_h.\n";
		$file_callid_h->print( "\t\t// $callid\t=\t$type,\n" );
	}
	else
	{
		$file_callid_h->print( "\t\t$callid\t=\t$type,\n" );
		$rpc_types{$type} = $callid;
		$rpc_names{$callid} = $type;
	}

	$state_all_content .= "$callid,\n";

	$file_rpcalls_cpp->print( "static $callname __stub_$callname($callid, new $argumentclass, new $resultclass);\n" ) if $callname;
}

sub print_rpc_inl
{
	my ($file, $classname, $baseclass, $argumentclass, $resultclass, $maxsize, $prior, $timeout, $debug, $callid) = @_;

	my $priorpolicy_content = "{ return $baseclass" . "::PriorPolicy(); }";
	$priorpolicy_content = "{ return $prior; }" if defined $prior;

	my $sizepolicy_content = "{ return $baseclass" . "::SizePolicy(size); }";
	$sizepolicy_content = "{ return size<=$maxsize; }" if defined $maxsize;

	my $timepolicy_content = "{ return $baseclass" . "Rpc::TimePolicy(timeout); }";
	$timepolicy_content = "{ return timeout <= $timeout; }" if defined $timeout;

	my $debug_content;
	$debug_content = "\n\t\t\tSTAT_MIN5(\"$classname\",1);\n\t\t\t" if($debug);

	$file->print("/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.

		GNET::Protocol *Clone() const { $debug_content return new $classname(*this); }\
	public:\
		enum { PROTOCOL_TYPE = $callid };\
		$classname(Type type, Rpc::Data *argument, Rpc::Data *result)\
			: $baseclass(type, argument, result ) { }\
		$classname(const $classname &rhs) : $baseclass(rhs) { }\
		int  PriorPolicy() const $priorpolicy_content\
		bool SizePolicy(size_t size) const $sizepolicy_content\
		bool TimePolicy(int timeout) const $timepolicy_content\n" );
}

##########################################################################
#                                                                        #
# The following is subroutines that generate code for protocol element.  #
#                                                                        #
##########################################################################

sub extractdoc_protocols
{
	my $doc = shift;

	$file_callid_h->print( "\t};\n\n\tenum ProtocolType {\n" );

	# pass through all protocols
	my $protocols = $doc->getElementsByTagName ("protocol");
	my $n = $protocols->getLength;

	# write file
	for (my $i = 0; $i < $n; $i++)
	{
		my $protocol = $protocols->item ($i);
		extract_protocol( $protocol );
	}
}

sub extract_protocol
{
	my $protocol = shift;

	my $protocolnameattr = $protocol->getAttributeNode ("name");
	my $protocolname = $protocolnameattr->getValue;

	my $typeattr = $protocol->getAttributeNode ("type");
	my $type = undef;
	$type = $typeattr->getValue if $typeattr;

	my $maxsizeattr = $protocol->getAttributeNode ("maxsize");
	my $maxsize = $maxsizeattr->getValue if $maxsizeattr;

	my $priorattr = $protocol->getAttributeNode ("prior");
	my $prior = $priorattr->getValue if $priorattr;

	my $debugattr = $protocol->getAttributeNode ("debug");
	my $debug = $debugattr->getValue if $debugattr;

	my $vars = $protocol->getElementsByTagName ("variable");

	my $filename = lc($protocolname) . ".hh";
	my $filename_inl = lc($protocolname) . ".inl";

	print "\tgenerating $protocolname ...\n";

	my $callid = "PROTOCOL_" . uc($protocolname);

	my $file = new FileHandle;
	if( (not -f $filename) and $file->open( ">$filename" ) )
	{
		print_header( $file, $filename, 0, 0, 1, 0, 0 );

		# argument
		print_classdefheader( $file, $protocolname, "GNET::Protocol" );

		$file->print( "\t\t#include \"$filename_inl\"\n" );
		$file->print("\n\t\tvoid Process(Manager *manager, Manager::Session::ID sid)\n" );
		$file->print("\t\t{\n\t\t\t// TODO\n\t\t}\n" );

		print_classdeftail( $file );

		print_tail( $file );

		$file->close();
	}

	my $file_inl = new FileHandle;
	if( $file_inl->open( ">$filename_inl" ) )
	{
		$file_inl->print( "/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.\n\n" );

		print_variables( $file_inl, $vars, $callid );
		print_protocol_constructor( $file_inl, $protocolname, $vars );
		print_marshalmethods( $file_inl, $vars, $debug, $protocolname );
		print_protocol_method( $file_inl, $maxsize, $prior );

		$file_inl->close();
	}

	if( not defined $type )
	{
		print "\t\tWarning: protocol $protocolname has no type, use default.\n";
		$callid_count ++;
		$type = $callid_count;
	}
	if( defined $protocol_types{$type} )
	{
		print "\t\tError: duplicate protocol type.($protocolname . type = $type). see $filename_callid_h.\n";
		$file_callid_h->print( "\t\t// $callid\t=\t$type,\n" );
	}
	else
	{
		$file_callid_h->print( "\t\t$callid\t=\t$type,\n" );
		$protocol_types{$type} = $callid;
		$protocol_names{$callid} = $type;
	}

	$state_all_content .= "$callid,\n";

	$file_rpcalls_h->print( "#include \"$filename\"\n" );
	$file_rpcalls_cpp->print( "static $protocolname __stub_$protocolname($callid);\n" ) if $protocolname;
}

sub print_protocol_method
{
	my ($file, $maxsize, $prior) = @_;

	my $priorpolicy_content = "{ return GNET::Protocol::PriorPolicy(); }";
	$priorpolicy_content = "{ return $prior; }" if defined $prior;
	$file->print("\n\t\tint PriorPolicy( ) const $priorpolicy_content\n" );

	my $sizepolicy_content = "{ return GNET::Protocol::SizePolicy(size); }";
	$sizepolicy_content = "{ return size <= $maxsize; }" if defined $maxsize;
	$file->print("\n\t\tbool SizePolicy(size_t size) const $sizepolicy_content\n" );
}

sub print_protocol_constructor
{
	my $file = shift;
	my $classname = shift;
	my $vars = shift;

	my $callid = "PROTOCOL_" . uc($classname);

	my $n = $vars->getLength;

	my ($evaluate1_1, $evaluate1_2, $evaluate1_3);
	my ($evaluate2_1, $evaluate2_2);
	my $evaluate3;

	my $isfirst = 1;
	my $count_default = 0;
	for (my $i = 0; $i < $n; $i++)
	{
		my $var = $vars->item ($i);

		my $nameattr = $var->getAttributeNode ("name");
		my $typeattr = $var->getAttributeNode ("type");
		my $sizeattr = $var->getAttributeNode ("size");
		my $defaultattr = $var->getAttributeNode ("default");

		my $name = $nameattr->getValue;
		my $type = $typeattr->getValue;
		my $size = $sizeattr->getValue if $sizeattr;
		my $default = $defaultattr->getValue if $defaultattr;

		my $count = $i + 1;

		if( $type eq 'char' and $size )
		{
			$evaluate1_1 .= "const $type * l_$name";
			$evaluate1_1 .= " = \"$default\"" if defined $default;
			$evaluate1_1 .= "," if ($count < $n);
			$evaluate1_1 .= "\n\t\t\t" if( 0 == ($count % 3) and $count > 1 and $count < $n);
			$evaluate1_3 .= "\t\t\tstrncpy( $name, l_$name, sizeof($name)-1 );\n";
			$evaluate1_3 .= "\t\t\t$name [sizeof($name)-1] = 0;\n";
			$evaluate2_2 .= "\t\t\tmemcpy( $name, rhs.$name, sizeof($name) );\n";
			$evaluate3 .= "\t\t\t\tmemcpy( $name, r->$name, sizeof($name) );\n";
		}
		else
		{
			$evaluate1_1 .= "$type l_$name";
			$evaluate1_1 .= " = $default" if defined $default;
			$evaluate1_1 .= "," if ($count < $n);
			$evaluate1_1 .= "\n\t\t\t" if( 0 == ($count % 3) and $count > 1 and $count < $n);
			$evaluate1_2 .= "$name(l_$name)";
			$evaluate1_2 .= "," if ($count < $n);
			$evaluate1_2 .= "\n\t\t\t" if( 0 == ($count % 3) and $count > 1 and $count < $n);
			$evaluate2_1 .= "$name(rhs.$name)";
			$evaluate2_1 .= "," if ($count < $n);
			$evaluate2_1 .= "\n\t\t\t" if( 0 == ($count % 3) and $count > 1 and $count < $n);
			$evaluate3 .= "\t\t\t\t$name = r->$name;\n";
		}
	}

	$evaluate1_2 = "," . $evaluate1_2 if defined $evaluate1_2;
	my $evaluate1 = "($evaluate1_1)\n\t\t\t: Protocol($callid)$evaluate1_2\n\t\t{\n$evaluate1_3\t\t}\n";
	my $evaluate2 = ": Protocol(rhs)";
	$evaluate2 = ": Protocol(rhs),$evaluate2_1" if $evaluate2_1;
	$evaluate2 .= "\n\t\t{\n$evaluate2_2\t\t}" if $evaluate2_2;
	$evaluate2 .= " { }" if not $evaluate2_2;

	$file->print( "\tpublic:\
		$classname(Type type) : Protocol(type) { }
		$classname $evaluate1\
		$classname(const $classname &rhs)\
			$evaluate2\n\
		GNET::Protocol *Clone() const { return new $classname(*this); }\n" );
}


##########################################################################
#                                                                        #
# The following is subroutines that generate code for state element.     #
#                                                                        #
##########################################################################

sub extractdoc_states
{
	my $doc = shift;

	# pass through all states
	my $states = $doc->getElementsByTagName ("state");
	my $n = $states->getLength;

	print "\tgenerating $filename_state_h $filename_state_cpp ...\n";

	$file_state_h->print( "/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.\
\
#ifndef __$namespace" . "_STATE_HXX\
#define __$namespace" . "_STATE_HXX\
\
#include \"protocol.h\"\
#include \"$filename_callid_h\"\
\
namespace $namespace\
{\n" );
	$file_state_cpp->print( "/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.\
\
#include \"$filename_state_h\"\
\
namespace $namespace\
{\n" );

	# write file
	for (my $i = 0; $i < $n; $i++)
	{
		my $state = $states->item ($i);
		extract_state( $state );
	}

	$file_state_h->print( "\n}\n#endif\n" );
	$file_state_cpp->print( "\n}\n" );

}

sub extract_state
{
	my ($state) = @_;

	my $statenameattr = $state->getAttributeNode ("name");
	my $statename = $statenameattr->getValue;

	my $timeoutattr = $state->getAttributeNode ("timeout");
	my $timeout = $timeoutattr->getValue if $timeoutattr;
	$timeout = "5" if not defined $timeout;

	my $attrattr = $state->getAttributeNode ("attr");
	my $attr = $attrattr->getValue if $attrattr;

	my $protos = $state->getElementsByTagName ("proto");
	# my $calls = $state->getElementsByTagName ("rpcall");

	$file_state_h->print( "\n" );
	# $file_state_h->print( "extern GNET::Protocol::Type _state_$statename" . "[];\n" ) if( $attr eq 'null' );
	$file_state_h->print( "extern GNET::Protocol::Manager::Session::State state_$statename;\n" );

	if( $attr eq 'all' )
	{
		$file_state_cpp->print( "\nstatic GNET::Protocol::Type _state_$statename" . "[] = {\n$state_all_content};\n" );
		$file_state_cpp->print( "GNET::Protocol::Manager::Session::State state_$statename(_state_$statename,\
					sizeof(_state_$statename)/sizeof(GNET::Protocol::Type), $timeout);\n" );
	}
	elsif( $attr eq 'null' )
	{
		$file_state_cpp->print( "GNET::Protocol::Manager::Session::State state_$statename(NULL,(unsigned int)0,$timeout);\n" );
	}
	else
	{
		$file_state_cpp->print( "\nstatic GNET::Protocol::Type _state_$statename" . "[] = {\n" );

		for (my $i = 0; $i < $protos->getLength; $i++)
		{
			my $proto = $protos->item ($i);
			my $protonameattr = $proto->getAttributeNode ("name");
			my $protoname = $protonameattr->getValue if $protonameattr;
			if( defined $protocol_names{"PROTOCOL_".uc($protoname)} )
			{	$file_state_cpp->print( "PROTOCOL_" . uc($protoname) . ",\n" );	}
			elsif( defined $rpc_names{"RPC_".uc($protoname)} )
			{	$file_state_cpp->print( "RPC_" . uc($protoname) . ",\n" );		}
			else
			{
				print "\t\tWarning: proto $protoname in state $statename is not defined.\n";
				$file_state_cpp->print( "PROTOCOL_" . uc($protoname) . ",\n" );
			}
		}

		#for (my $i = 0; $i < $calls->getLength; $i++)
		#{
		#	my $call = $calls->item ($i);
		#	my $callnameattr = $call->getAttributeNode ("name");
		#	my $callname = $callnameattr->getValue if $callnameattr;
		#	$file_state_cpp->print( "RPC_" . uc($callname) . ",\n" );
		#}


		$file_state_cpp->print( "};\nGNET::Protocol::Manager::Session::State state_$statename(_state_$statename,\
					sizeof(_state_$statename)/sizeof(GNET::Protocol::Type), $timeout);\n" );
	}


}


##########################################################################
#                                                                        #
# The following is subroutines that generate code for manager element.   #
#                                                                        #
##########################################################################

sub extractdoc_managers
{
	my $doc = shift;

	# pass through all managers
	my $managers = $doc->getElementsByTagName ("manager");
	my $n = $managers->getLength;

	# write file
	for (my $i = 0; $i < $n; $i++)
	{
		my $manager = $managers->item ($i);
		extract_manager( $manager );
	}
}

sub extract_manager
{
	my $manager = shift;

	my $managernameattr = $manager->getAttributeNode ("name");
	my $managername = $managernameattr->getValue;

	my $initstateattr = $manager->getAttributeNode ("initstate");
	my $initstate = $initstateattr->getValue if $initstateattr;

	my $idattr = $manager->getAttributeNode ("identification");
	my $id = $idattr->getValue if $idattr;

	# my $vars = $manager->getElementsByTagName ("variable");

	my $filename = lc($managername) . ".hh";
	my $filename_inl = lc($managername) . ".inl";

	print "\tgenerating $managername ...\n";

	my $file = new FileHandle;
	if( (not -f $filename) and $file->open( ">$filename" ) )
	{
		print_header( $file, $filename, 0, 0, 0, 1, 0 );

		# argument
		print_classdefheader( $file, $managername, "GNET::Protocol::Manager" );

		$file->print( "\t\t#include \"$filename_inl\"\n" );
		$file->print("\
		void OnAddSession(Session::ID)\
		{\
			// TODO\
		}\n\
		void OnDelSession(Session::ID)\
		{\
			// TODO\
		}\n\
		int  PriorPolicy(Protocol::Type type) const\
		{\
			return Protocol::GetStub(type)->PriorPolicy();\
		}\n\
		bool InputPolicy(Protocol::Type type, size_t size) const\
		{\
			return Protocol::GetStub(type)->SizePolicy(size);\
		}\n\
		void OnCheckAddress(SockAddr &) const\
		{\
			// TODO\
		}\n" );

		print_classdeftail( $file );

		print_tail( $file );

		$file->close();

	}

	my $file_inl = new FileHandle;
	if( $file_inl->open( ">$filename_inl" ) )
	{
		$file_inl->print( "/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.\n" );

		# print_variables( $file_inl, $vars );
		# print_manager_constructor( $file_inl, $managername, $vars );
		# print_marshalmethods( $file_inl, $vars );
		print_manager_method( $file_inl, $managername, $initstate, $id );

		$file_inl->close();
	}

	$file_rpcalls_h->print( "#include \"$filename\"\n" );
}

sub print_manager_method
{
	my ($file, $classname, $initstate, $id) = @_;

	my $initstatepolicy_content = "{ return &state_default; }";
	$initstatepolicy_content = "{ return &state_$initstate; }" if defined $initstate;
	$file->print("\n\t\tconst Session::State *GetInitState() const $initstatepolicy_content\n" );

	my $idpolicy_content = "{ return \"Unknown\"; }";
	$idpolicy_content = "{ return \"$id\"; }" if defined $id;
	$file->print("\n\t\tstd::string Identification() const $idpolicy_content\n" );
}


##########################################################################
#                                                                        #
# The following is subroutines that generate dbbridge code.             #
#                                                                        #
##########################################################################

sub generate_dbbridge
{
	my $doc = shift;

	# begin
	$file_dbbridge_h = new FileHandle;
	$file_dbbridge_h->open( ">$filename_dbbridge_h" );
	$file_dbbridge_cpp = new FileHandle;
	$file_dbbridge_cpp->open( ">$filename_dbbridge_cpp" );

	print_dbbridge_h_header( $file_dbbridge_h );
	print_dbbridge_cpp_header( $file_dbbridge_cpp );

	# generate
	print "\ngenerating code for server ...\n";

	extractdoc_dbbridge( $doc );

	# finish
	print "\tgenerate code for server finished.\n";

	# end
	print_dbbridge_h_tail( $file_dbbridge_h );
	print_dbbridge_cpp_tail( $file_dbbridge_cpp );
}

sub extractdoc_dbbridge
{
	my $doc = shift;

	# pass through all tables
	my $tables = $doc->getElementsByTagName ("table");
	my $n = $tables->getLength;

	# write server file
	for( my $i = 0; $i < $n; $i++)
	{
		my $table = $tables->item ($i);
		print_buffer_variables( $table );
	}
}

sub print_dbbridge_h_header
{
	my $file = shift;

	my $temp = uc($filename_dbbridge_h);
	$temp =~ s/\./\_/g;

	$file->print( "/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.\
\
#ifndef __$namespace" . "_$temp\
#define __$namespace" . "_$temp\
\
#include \"thread.h\"\
#include \"marshal.h\"\
#include \"dbbuffer.h\"\
\
using namespace GNET;
\
namespace $namespace\
{\
\
class DBBridge\
{\
public:\n" );

}

sub print_dbbridge_h_tail
{
	my $file = shift;
	$file->print( "\n};\n\n};\n\n#endif\n" );
}

sub print_dbbridge_cpp_header
{
	my $file = shift;

	$file->print( "/////////////////////////////////////////////////////////////////////////////\
// This file is generated by rpcgen.pl, do not modified this file.\
\
#include \"$filename_dbbridge_h\"\
\
namespace $namespace\
{\n\n" );

}

sub print_dbbridge_cpp_tail
{
	my $file = shift;
	$file->print( "\n};\n" );
}

sub print_buffer_variables
{
	my $table = shift;

	my $tablenameattr = $table->getAttributeNode ("name");
	my $tablename = $tablenameattr->getValue;

	$file_dbbridge_h->print( "\tstatic GNET::DBBuffer::OctetsMap s_map_$tablename;\n" );
	$file_dbbridge_h->print( "\tstatic Thread::Mutex s_mutex_$tablename;\n" );

	$file_dbbridge_h->print( "\
	static int buf_get_$tablename( GNET::Octets & key, GNET::Octets & value )\
	{\
		return DBBuffer::buf_get( s_map_$tablename, s_mutex_$tablename, \"$tablename\", key, value );\
	}\
	static int buf_put_$tablename( GNET::Octets & key, GNET::Octets & value )\
	{\
		return DBBuffer::buf_put( s_map_$tablename, s_mutex_$tablename, \"$tablename\", key, value );\
	}\n\n" );

	$file_dbbridge_cpp->print( "\tGNET::DBBuffer::OctetsMap DBBridge::s_map_$tablename;\n" );
	$file_dbbridge_cpp->print( "\tThread::Mutex DBBridge::s_mutex_$tablename;\n" );
}


