#!/usr/bin/perl

use Getopt::Long;
use Mysql;

use Env qw(LSBUSER LSBDBPASSWD LSBDB LSBDBHOST);

# Uncomment to trace SQL statments
#$trace=1;

$nameonly=0;

sub
usage()
{
die "mkdatadef -h <headername> -a <archname>";
}

sub
displaytype($)
{
local ($type) = @_;
local (*entry,*tentry,*tmentry);
local($th);
local($tmh);

#print $$type{'Tname'};
#print $$type{'Ttype'};
#if( $$type{'Tname'} =~ "anon" ) { return; }

#switch ($ttype{'Ttype'})

if( $$type{'Ttype'} eq "Intrinsic" ) {
	print $$type{'Tname'}."\t";
	return;
	}

if( $$type{'Ttype'} eq "Literal" ) {
	print $$type{'Tname'}."\t";
	return;
	}

if( $$type{'Ttype'} eq "Const" ) {
	$select = "SELECT * FROM Type WHERE Tid=".$$type{'Tbasetype'};
	$sth = $Dbh->query($select) || die $Dbh->errmsg();
	%type=$sth->fetchhash;
	if( $type{'Ttype'} eq "Pointer" ) {
		displaytype(\%type);
		print "const ";
	} else {
		print "const ";
		displaytype(\%type);
	}
	return;
	}

if( $$type{'Ttype'} eq "Typedef" ) {
	if (!$nameonly) { print "typedef "; }
	$basetype=$$type{'Tbasetype'};
	$tselect="SELECT * FROM Type ";
	$tselect.= "LEFT JOIN ArchType ON ATaid=Tarch AND ATtid=Tid ";
	$tselect.= "WHERE Tid=$basetype";
	$tth = $Dbh->query($tselect) || die $Dbh->errmsg();
	%entry=$tth->fetchhash;
# Something about anon or not & wether to set nameonly
	if (!$nameonly) {
		if( ( $entry{'Ttype'} eq 'Typedef' ||
		    $entry{'Ttype'} eq 'FuncPtr' ) ||
		    $entry{'Theadergroup'} != $HGid ) {
			$nameonly=1;
			displaytype(\%entry);
			$nameonly=0;
		} else {
			displaytype(\%entry);
			}
		}
	if( $entry{'Ttype'} ne 'FuncPtr' || $nameonly ) {
		print $$type{'Tname'}."\t";
		}
	if( $entry{'Ttype'} eq 'Array' && $nameonly==0) {
		print "[".$entry{'ATsize'}."]";
		}
	if( !$nameonly && $$type{'Tattribute'} ) {
		print "__attribute__ (".$$type{'Tattribute'}.")";
		}
	return;
	}

if( $$type{'Ttype'} eq "Pointer" ) {
	$basetype=$$type{'Tbasetype'};
	$tselect="SELECT * FROM Type WHERE Tid=$basetype";
	$tth = $Dbh->query($tselect) || die $Dbh->errmsg();
	%entry=$tth->fetchhash;
	if (!$nameonly) {
		if( $entry{'Ttype'} eq 'Typedef' ||
		    $entry{'Ttype'} eq 'FuncPtr' ) {
			$nameonly=1;
			displaytype(\%entry);
			$nameonly=0;
		} else {
			displaytype(\%entry);
			}
		print "* ";
	} else {
		displaytype(\%entry);
		print "* ";
	}
	return;
	}

if( $$type{'Ttype'} eq "Struct" ) {
	print "struct ";

	if( $$type{'Tname'} =~ "anon" ) {
		$$type{'Tname'} ="";
		$nameonly = 0;
		}
	print $$type{'Tname'}."\t";
	$Tid=$$type{'Tid'};
	if( $nameonly ) { return; }
	#print $$type{'Tcomment'}."\n";

	$tmselect = "SELECT * FROM TypeMember ";
	#$tmselect.= "LEFT JOIN ArchTypeMem ON ATMtmid = TMid ";
	#$tmselect.= "LEFT JOIN Architecture ON ATMaid = Aid ";
	$tmselect.= "WHERE TMmemberof=$Tid ";
	$tmselect.= "ORDER BY TMposition";
	$tmh = $Dbh->query($tmselect) || die $Dbh->errmsg();
	if ($tmh->numrows ) { print "{\n"; }
	for(1..$tmh->numrows) {
		%tmentry=$tmh->fetchhash;
		$TMtypeid=$tmentry{'TMtypeid'};
		$tselect = "SELECT * FROM Type WHERE Tid=$TMtypeid";
		$th = $Dbh->query($tselect) || die $Dbh->errmsg();
		%entry=$th->fetchhash;
		$nameonly=1;
		displaytype(\%entry);
		if( $entry{'Ttype'} ne 'FuncPtr' ) {
			print $tmentry{'TMname'};
			}
		if( $entry{'Ttype'} eq 'Array' ) {
			print "[".$tmentry{'TMarray'}."]";
			}
		if( $tmentry{'TMbitfield'} != 0 ) {
			print ":".$tmentry{'TMbitfield'};
			}
		print ";\t";
		#if( $tmentry{'TMcomment'} ) {
		#	print "/* ".$tmentry{'TMcomment'}." */";
		#	}
		print "\n";
		$nameonly=0;
		}
	if ($tmh->numrows ) { print "}"; }
	if( $$type{'Tattribute'} ) {
		print "__attribute__ (".$$type{'Tattribute'}.")";
		}
	print "\n";
	return;
	}

if( $$type{'Ttype'} eq "Union" ) {
	print "union ";

	if( $$type{'Tname'} =~ "anon" ) {
		$$type{'Tname'} ="";
		$nameonly = 0;
		}
	print $$type{'Tname'}."\t";
	$Tid=$$type{'Tid'};
	if( $nameonly ) { return; }
	#print $$type{'Tcomment'}."\n";

	$tmselect="SELECT * FROM TypeMember WHERE TMmemberof=$Tid";
	$tmselect.=" ORDER BY TMposition";
	$tmh = $Dbh->query($tmselect) || die $Dbh->errmsg();
	if ($tmh->numrows ) { print "{\n"; }
	for(1..$tmh->numrows) {
		%tmentry=$tmh->fetchhash;
		$TMtypeid=$tmentry{'TMtypeid'};
		$tselect="SELECT * FROM Type WHERE Tid=$TMtypeid";
		$th = $Dbh->query($tselect) || die $Dbh->errmsg();
		%entry=$th->fetchhash;
		$nameonly=1;
		displaytype(\%entry);
		if( $entry{'Ttype'} ne 'FuncPtr' ) {
			print $tmentry{'TMname'};
			}
		if( $entry{'Ttype'} eq 'Array' ) {
			print "[".$tmentry{'TMarray'}."]";
			}
		print ";\t";
		#if( $tmentry{'TMcomment'} ) {
		#	print "/* ".$tmentry{'TMcomment'}." */";
		#	}
		print "\n";
		$nameonly=0;
		}
	if ($tmh->numrows ) { print "}\n"; }
	return;
	}

if( $$type{'Ttype'} eq "Enum" ) {
	print "enum ";
	$Tid=$$type{'Tid'};
	if( $$type{'Tname'} =~ "anon" ) {
		$$type{'Tname'} ="";
		}
	print $$type{'Tname'}."\t";
	if( $nameonly ) { return; }
	#print $$type{'Tcomment'}."\n";

	$tmselect="SELECT * FROM TypeMember WHERE TMmemberof=$Tid";
	$tmselect.=" ORDER BY TMposition";
	$tmh = $Dbh->query($tmselect) || die $Dbh->errmsg();
	if ($tmh->numrows ) { print "{\n"; }
	for(1..$tmh->numrows) {
		%tmentry=$tmh->fetchhash;
		# It's an enum, don't print out the types, just the names
		#$TMtypeid=$tmentry{'TMtypeid'};
		#$tselect="SELECT * FROM Type WHERE Tid=$TMtypeid";
		#$th = $Dbh->query($tselect) || die $Dbh->errmsg();
		#%entry=$th->fetchhash;
		#$nameonly=1;
		#displaytype(\%entry);
		print $tmentry{'TMname'};
		# An array doesn't make sense for an enum, so use this to
		# specify a fixed value.
		if( $tmentry{'TMarray'} ) {
			print " = ".$tmentry{'TMarray'};
			}
		if( $_ != $tmh->numrows ) {
			print ",\t";
			}
		#print $tmentry{'TMcomment'}."\n";
		$nameonly=0;
		}
	if ($tmh->numrows ) { print "}\n"; }
	return;
	}

if( $$type{'Ttype'} eq "FuncPtr" ) {
	$basetype=$$type{'Tbasetype'};
	$tselect="SELECT * FROM Type WHERE Tid=$basetype";
	$tth = $Dbh->query($tselect) || die $Dbh->errmsg();
	%entry=$tth->fetchhash;
	if( !$nameonly ) {
		$nameonly=1;
		displaytype(\%entry);
		$nameonly=0;
	} else {
		displaytype(\%entry);
		}
	print "(*"; 
	$Tid=$$type{'Tid'};
	if( $$type{'Tname'} =~ "fptr" ) {
		$$type{'Tname'} =~ s/fptr-//;
		}
	print $$type{'Tname'}.")";
	print $$type{'Tcomment'}."(";

	$tmselect="SELECT * FROM TypeMember WHERE TMmemberof=$Tid";
	$tmselect.=" ORDER BY TMposition";
	$tmh = $Dbh->query($tmselect) || die $Dbh->errmsg();
	if($tmh->numrows == 0) {
		print "void";
		}
	for(1..$tmh->numrows) {
		%tmentry=$tmh->fetchhash;
		$TMtypeid=$tmentry{'TMtypeid'};
		$tselect="SELECT * FROM Type WHERE Tid=$TMtypeid";
		$th = $Dbh->query($tselect) || die $Dbh->errmsg();
		%entry=$th->fetchhash;
		$nameonly=1;
		displaytype(\%entry);
		print $tmentry{'TMname'};
		if( $tmentry{'TMarray'} ) {
			print "[".$tmentry{'TMarray'}."]";
			}
		if( $_ != $tmh->numrows ) {
			print ",";
			}
		$nameonly=0;
		}
	print ")\n";
	return;
	}

if( $$type{'Ttype'} eq "Array" ) {
	$basetype=$$type{'Tbasetype'};
	$tselect="SELECT * FROM Type WHERE Tid=$basetype";
	$tth = $Dbh->query($tselect) || die $Dbh->errmsg();
	%entry=$tth->fetchhash;
	if( !$nameonly ) {
		$nameonly=1;
		displaytype(\%entry);
		$nameonly=0;
	} else {
		displaytype(\%entry);
		}
	if( $$type{'Tname'} =~ "fptr" ) {
		$$type{'Tname'} =~ s/fptr-//;
		}
	#print $$type{'Tname'};
	#print "[".$$type{'Tsize'}."]";
	return;
	}

print "Unknown Type: \".$$type{'Ttype'}.\"\n";
}

sub
displayconstant($)
{
local ($const) = @_;

print "#define ";
print $$const{'Cname'};
print "\t";
if( $$const{'Ctype'} eq 'string' ) {
	print "\"".$$const{'ACvalue'}."\"";
} else {
	print $$const{'ACvalue'};
}
print "\n";
}

GetOptions("h=s" => \$headname,
	   "a=s" => \$archname);
 
if( !$headname ) { usage(); }  
if( !$archname ) { usage(); }  

$headname =~ s/^\.\///;

$Dbh = Mysql->connect($LSBDBHOST,$LSBDB,$LSBUSER, $LSBDBPASSWD) || die $Mysql::db_errstr;

#
# Get the Architecture id
#
$select = "SELECT Aid FROM Architecture WHERE Aname='$archname'";
print $select,"\n" if $trace;
$sth = $Dbh->query($select) || die $Dbh->errmsg();

if( !$sth->numrows ) { exit 0; }
%entry=$sth->fetchhash;
$Aid=$entry{'Aid'};

#
# Get the Header id
#
$select = "SELECT Hid FROM Header WHERE Hname='$headname'";
print $select,"\n" if $trace;
$sth = $Dbh->query($select) || die $Dbh->errmsg();

if( !$sth->numrows ) { exit 0; }
%entry=$sth->fetchhash;
$Hid=$entry{'Hid'};

#
# Get the return types
#
$select = "SELECT Ireturn FROM Interface ";
$select.= "WHERE Iheader=$Hid ";
$select.= "AND ( Istatus='Included' OR Istatus='Deprecated' ) ";
$select.= "AND Iarch=$Aid";
print $select,"\n" if $trace;
$sth = $Dbh->query($select) || die $Dbh->errmsg();
for(1..$sth->numrows) {
	%entry=$sth->fetchhash;
	$type{$entry{'Ireturn'}}=1;
	}

#
# Get the parameter types
#
$select = "SELECT Ptype FROM Interface,Parameter ";
$select.= "WHERE Iheader=$Hid ";
$select.= "AND Pint=Iid ";
#$select.= "WHERE Pint=Iid ";
$select.= "AND ( Istatus='Included' OR Istatus='Deprecated' ) ";
$select.= "AND Iarch=$Aid";
print $select,"\n" if $trace;
$sth = $Dbh->query($select) || die $Dbh->errmsg();
for(1..$sth->numrows) {
	%entry=$sth->fetchhash;
	$type{$entry{'Ptype'}}=1;
	}

#
# Get any other type that is assigned to this header
#
$select = "SELECT Tid,Tname FROM Type ";
$select.= "LEFT JOIN HeaderGroup ON Theadergroup=HGid ";
$select.= "WHERE HGheader=$Hid ";
$select.= "AND Tstatus != 'Excluded' ";
print $select,"\n" if $trace;
$sth = $Dbh->query($select) || die $Dbh->errmsg();
for(1..$sth->numrows) {
	%entry=$sth->fetchhash;
	$type{$entry{'Tid'}}=1;
	#print "Type ".$entry{'Tname'}."\n";
	}

$typelist=join ',', keys(%type);

#
# Get the base types of Typedefs
#

if( $typelist ne "" ) { 
	$select = "SELECT Tbasetype,Tname FROM Type ";
	$select.= "WHERE Tid IN ($typelist) ";
	$select.= "AND Ttype = 'Typedef' ";
	print $select,"\n" if $trace;
	$sth = $Dbh->query($select) || die $Dbh->errmsg();
	for(1..$sth->numrows) {
		%entry=$sth->fetchhash;
		$type{$entry{'Tbasetype'}}=1;
		}
	}

$typelist=join ',', keys(%type);

#
# Get the base types of Pointers
#

if( $typelist ne "" ) { 
	$select = "SELECT Tbasetype,Tname FROM Type ";
	$select.= "WHERE Tid IN ($typelist) ";
	$select.= "AND Ttype = 'Pointer' ";
	print $select,"\n" if $trace;
	$sth = $Dbh->query($select) || die $Dbh->errmsg();
	for(1..$sth->numrows) {
		%entry=$sth->fetchhash;
		$type{$entry{'Tbasetype'}}=1;
		#print "Pointer ".$entry{'Tname'}."\n";
		}
	}

$typelist=join ',', keys(%type);

#
# Get the base types of Struct/Union members
#

if( $typelist ne "" ) { 
	$select = "SELECT TMtypeid,TMname FROM TypeMember ";
	$select.= "WHERE TMmemberof IN ($typelist)";
	print $select,"\n" if $trace;
	$sth = $Dbh->query($select) || die $Dbh->errmsg();
	for(1..$sth->numrows) {
		%entry=$sth->fetchhash;
		$type{$entry{'TMtypeid'}}=1;
		#print "TypeMember ".$entry{'TMname'}."\n";
		}
	}

$typelist=join ',', keys(%type);

#
# Get the info from the types in the $type hash
#
# Use the algorithm from admin/headers.php3

$select = "SELECT HGid,HGdescription,HGorder FROM HeaderGroup ";
$select.= "WHERE HGheader=$Hid ";
$select.= "ORDER BY HGorder";
print $select,"\n" if $trace;
$hgh = $Dbh->query($select) || die $Dbh->errmsg();
for(1..$hgh->numrows) {
	%entry=$hgh->fetchhash;
	$HGid=$entry{'HGid'};
	$HGdesc=$entry{'HGdescription'};
	$HGorder=$entry{'HGorder'};
	# Make sure a blank line is present between every group
	print "\n";

	# Display the Constants
	$select = "SELECT * FROM Constant ";
	$select.= "LEFT JOIN ArchConst ON Cid=ACcid ";
	
	$select.= "WHERE Cheadgroup=$HGid ";
	$select.= "AND ACaid=$Aid ";
	$select.= "AND Cstd ='Yes' ";
	$select.=" ORDER BY ACvalue, Cname, ACaid";
	$ch = $Dbh->query($select) || die $Dbh->errmsg();
	print $ch->numrows," rows\n" if $trace;
	for(1..$ch->numrows) {
		%centry=$ch->fetchhash;
		displayconstant(\%centry);
		}
	print "\n\n";
	
	# Display the Types
	$select = "SELECT * FROM Type ";
	$select.= "LEFT JOIN Architecture ON Tarch=Aid ";
	$select.= "LEFT JOIN ArchType ON ATaid=Aid AND ATtid=Tid ";
	$select.= "WHERE Theadergroup=$HGid ";
	$select.= "AND ( Tstatus = 'Referenced' OR Tstatus = 'Conly' )";
	$select.= "AND Tarch = $Aid ";
	$select.= "ORDER BY Tid";
	print $select,"\n" if $trace;
	$th = $Dbh->query($select) || die $Dbh->errmsg();
	print $th->numrows," rows\n" if $trace;
	for(1..$th->numrows) {
		%tentry=$th->fetchhash;
		if( $tentry{'Aid'} && $tentry{'Aname'} eq "None" ) { next; }
		displaytype(\%tentry);
		print ";";
		#print $tentry{'Tcomment'}."\n\n";
		}
	}
