#!/usr/pkg/bin/perl

# Includes
use CGI;
use DBI;
use URI::Escape;
use Data::Dumper;
use Time::HiRes qw(gettimeofday tv_interval);

my $start = [gettimeofday];

# Flush all output immediately
$| = 1;

### DEBUG ###
$ENV{DEBUG} = 0;
### DEBUG ###

### VERBOSE ###
$ENV{VERBOSE} = 0;
### VERBOSE ###

# Print the content type, etc.
print CGI::header();

# Name of the datasource
#my $dsn = "dbi:mysql:mechdbtest:localhost:6900";
my $dsn = "dbi:mysql:zax:ol.freeshell.org:3306";


# Login credentials
#my $user = "bt";
#my $pass = "bt";
my $user = "zax";
my $pass = "mechdb";

# Get a handle to the database.
my $dbh = DBI->connect($dsn, $user, $pass) or die "Can't connect to the DB: $DBI::errstr\n";

# Parameters from the HTML form
%P = ();

# Determine the script name so that it can properly reference itself
$0 =~ m,/([^/]+)$,;
my $script = $1;

# Generate some Name->Id and Id->Name lookup tables
my %ClassNameToId       = GenerateLookup("Class", "Name,Id");
my %ClassIdToName       = reverse(%ClassNameToId);
my %TechNameToId        = GenerateLookup("Tech", "ShortName,Id");
my %TechIdToName        = reverse(%TechNameToId);
my %EquipmentNameToId   = GenerateLookup("Equipment", "ShortName,Id");
my %EquipmentIdToName   = reverse(%EquipmentNameToId);
my %WeaponNameToId      = GenerateLookup("Weapon", "ShortName,Id");
my %WeaponIdToName      = reverse(%WeaponNameToId);
my %WeaponClassNameToId = GenerateLookup("WeaponClass", "ShortName,Id");
my %WeaponClassIdToName = reverse(%WeaponClassNameToId);
my %WeaponTypeNameToId  = GenerateLookup("WeaponType", "Name,Id");
my %WeaponTypeIdToName  = reverse(%WeaponTypeNameToId);


# Tooltips!
my %EquipmentTooltip = ( 
	'BAP' => '<i>Beagle Active Probe</i><br>Passive<br>Extends your radar range to 1200 meters',
	'ECM' => '<i>Electronic Countermeasures</i><br>Passive<br>Jams incoming missile radar, making it take longer for an enemy to get a lock',
	'IFF' => '<i>Friend-or-Foe Jammer</i><br>Passive<br>Sends out a neutral signal to all receiving radar, making it impossible to determine if you are friend or foe',
	'LA'  => '<i>Light Amplification</i><br>Active<br>Turn on to increase visibility in low-light conditions',
	'AMS' => '<i>Anti-Missile System</i><br>Passive<br>When a locked missile volley is incoming, a portion of them will be neutralized',
	'JJ'  => '<i>Jump Jets</i><br>Active<br>Provides your mech with limited periods of flight',
);

my %WeaponData = GetWeaponData();
my %WeaponTooltips = GenerateWeaponTooltips(%WeaponData);

# For Low-Bandwidth display
my %ArmorTypeLongToShort = ("Standard" => "Std", "Reflective" => "Rfl", "Reactive" => "Rct", "Solarian" => "Slr", "Ferro" => "Fer");

print "<br><br>\n" if ($ENV{DEBUG});
ParseParams();
my $RestrictMechStatement = BuildRestrictionStatement();

print "DEBUG: CALL GetFullMechConfig('$RestrictMechStatement');<br>\n" if $ENV{DEBUG};
# Prevent hitting the DB unless we're actually searching (e.g. skip getting the mech list on first load)
my @FullMechConfig = (defined $P{'Search'} || defined $P{'special'}) ? ExecSql("CALL GetFullMechConfig('$RestrictMechStatement');") : ();

my @cOrder = ();

PrintPage();

sub PrintPage
{
	print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
	print "<HTML>\n";
	PrintPageHeader();
	print "<BODY>\n";

	if ($P{weapondata})
	{
		print GenerateWeaponList(1);
	        print "</BODY>\n</HTML>\n";
		return;
	}	

#       print "<H1>BattleTech: Firestorm Mech Search <font color=\"red\">BETA</font></H1>\n";
        print "<img style=\"position: relative; top: 12px\" src=\"/img/mechsearch_header_v2.png\"><br>\n";

	# Remove bandwidth param from query string so we can neatly append it to the quick URLs for BW version switching
	$ENV{QUERY_STRING} =~ s/bw=(h|l)//;

	# Feedback link
	print "<div style=\"position: absolute; left: 10px; top: 1px;\"><font style=\"font-size: 7pt;\">FYI: This data is from the loadout spreadsheet authored by Pharaoh (Craig Evans).&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"mailto:zax\@the-airlock.com?subject=Mech Search Feedback\">Please report any inaccuracies or misspellings!</a></li></font></div>";

	# Bandwidth-sensitive link
	if ($P{bw} eq 'h') { print "<div class=\"bw\"><a href=\"?bw=l" . "$ENV{QUERY_STRING}\">Low-Bandwidth</a></div>";  }
	else               { print "<div class=\"bw\"><a href=\"?bw=h" . "$ENV{QUERY_STRING}\">High-Bandwidth</a></div>"; }
	PrintMechFilter(); # As long as this happens after we query the database for results, we can munge with the contents of %P.

	# All columns:
	# Id, Name, VariantName, Chassis, Class, Tech, Era, Weight, Sinks, Armor, ArmorType, Speed, 
	# AlphaStrike, Ballistic, Missile, Beam, Narc, Flare, BAP, JJ, ECM, AMS, IFF, LA
	@cOrder = ('Name', 'VariantName', 'Chassis', 'Class', 'Tech', 'Weight', 'Sinks', 'Armor', 'ArmorType', 'Speed', 
	           'AlphaStrike', 'DPS', 'Ballistic', 'Missile', 'Beam', 'Narc', 'Flare');

	if ($P{bw} eq 'h') { push(@cOrder, 'BAP', 'JJ', 'ECM', 'AMS', 'IFF', 'LA'); }
	else               { push(@cOrder, 'Equipment');                            }

	PrintEventFloater();

	# Only print the HTML list if they click 'Search' (or on one of the special links) -- no list on first load.
	PrintHTMLList('Full Mech Config', \@cOrder, @FullMechConfig) if (defined $P{'Search'} || defined $P{'special'});
	print "<br>*** Note: DPS and AlphaStrike figures are shown only for the weapons that we have data on. If a mech has weapons for which we do not have data, those weapons are not included in the calculation. If a mech has ONLY those weapons, then we are completely unable to calculate any value for these attributes. DPS calcs are the theoretical maximum DPS; they assume the trigger is always held down and there is zero time (other than reloading) between shots, and that every shot lands fully.\n";

	print <<EOT;

<ul>
<br><B>TODO</B>:<br>
<li><strike>Fix the issue with &lt;=1 and =0 weapon searches</strike></li>
<li><strike>Make it more apparent that you can click column headers to sort</strike></li>
<li>Weapon data on weapon mouseover (damage, range, heat, reloadtime)</li>
<li>Mech effective range? (smallest max range to largest max range?)</li>
<li>Mech average range? (average weapon max range)</li>
<li>Optimize the queries on the back end. There is significant hackery afoot.</li>
<li>Make it look pretty.</li>
<li>Whatever other suggestions or requests are made.</li>
</ul>

<br><font size=-3>Disclaimer: This may not work the way you expect. <b>Data current as of March 23, 2011</b>. This tool is still in beta, please be gentle.<br></font>
EOT

print "<br><br><font color=\"#FFFFFF\">RestrictMechStatement: '$RestrictMechStatement'</font><br><br>\n" if ($ENV{VERBOSE});

printf "<font size=-3>Execution completed in %.2f seconds</font><br>\n", scalar tv_interval($start);
print "</body></html>";

	
}

sub PrintEventFloater
{
	my $event = $P{event};
	if ($event eq 'weightloss')
	{
		my @MechWeights = ExecSql("SELECT Weight, COUNT(Weight) 'Count' FROM Mech GROUP BY Weight ORDER BY Weight ASC");
		print "<div class=\"event\">\n";
		print "<table border=1 cellpadding=0 cellspacing=0>\n<tr><td>";
		print "<table border=0 cellpadding=0 cellspacing=1>\n";
		foreach my $mw (@MechWeights)
		{
			my $weight = $mw->{Weight};
			my $count = $mw->{Count};
			print "<tr><td class=event align=right><a href=\"?bw=$P{bw}&event=$P{event}&MinWeight=$weight&MaxWeight=$weight\">$weight&nbsp;Tons</a>:&nbsp;&nbsp;&nbsp;</td><td class=event>" . $mw->{Count} . " Mechs</td></tr>\n";
		}
		print "</table>\n";
		print "</td></tr></table>\n";
		print "</div>\n";
	}
}


sub PrintPageHeader
{
	print <<EOT;
<HEAD>
<TITLE>BattleTech:Firestorm Mech Search</TITLE>
<SCRIPT TYPE="text/javascript">
<!--
function submitenter(myfield,e)
{
  var keycode;
  if (window.event) keycode = window.event.keyCode;
  else if (e) keycode = e.which;
  else return true;

  if (keycode == 13)
  {
    myfield.form.submit();
    return false;
  }
  else
    return true;
}

function formClear ()
{
  for(var i = 0; i < document.forms[0].elements.length; i++)
  {
    if (document.forms[0].elements[i].type == "checkbox")
    {
      document.forms[0].elements[i].checked = false;
    }
    else if (document.forms[0].elements[i].type == "select-one")
    {
      document.forms[0].elements[i].selectedIndex = 0;
      document.forms[0].elements[i].value         = "";
    }
    else if (document.forms[0].elements[i].type == "text")
    {
      document.forms[0].elements[i].value = "";
    }
  }
//  document.forms[0].submit();
}
//-->
</SCRIPT>
EOT
	# Enable sorting for high-bandwidth only
	if ($P{bw} eq 'h') { print "<script src=\"/sorttable.js\"></script>\n"; }
	print <<EOT;
<STYLE>
body { font-size: 10pt; font-family:Verdana,Arial,sans-serif; background: #000000; color: #FFFFFF; }
td { font-size: 9.5pt; font-family:Verdana,Arial,sans-serif; white-space: nowrap; }
td.data { font-size: 9.5pt; font-family:Verdana,Arial,sans-serif; white-space: nowrap; border-width: 1px; border-style: solid; border-color: #999999; margin: 0; }
td.filter 
{ 
	margin-left: 5px; 
	font-size: 10.5pt; 
	font-family:Verdana,Arial,sans-serif;
	background: #000000; 
	color: #FFFFFF; 
}
th.filter 
{ 
	padding-left: 5px; 
	font-size: 10.5pt; 
	font-family:Verdana,Arial,sans-serif;
	background: #000000; 
	color: #FFFFFF; 
}
div.filter
{
	width: 900px;
	margin: 10px;
	padding: 5px;
	border: 1px solid #ffffff;
	size: auto;
}
table.wc
{
	border: 1px solid #ffffff;
}

th.sortable { font-size: 9.5pt; font-family:Verdana,Arial,sans-serif; text-decoration: underline; background: #666666; border-width: 1px; border-style: solid; border-color: #999999; margin: 0; cursor: pointer; }
th { font-size: 9.5pt; font-family:Verdana,Arial,sans-serif; background: #666666; color: #FFFFFF; }
tr.r:hover { background-color: #AAAAAA; color: #000000; }
a { color: #FFFFFF; }
#menu { width: 100%; background: #CCCCCC; cursor: pointer; }
#menu ul { list-style: none; text-align: center; wite-space:nowrap; margin: 0; padding: 0; float: left; }
#menu a  { font: bold 11pt arial, helvetica, sans-serif; display: block; border-width: 2px 0px 2px 0px; border-style: solid; border-color: #efefef; margin: 0; padding: 2px 0px; }
#menu h2 { font: bold 12pt arial, helvetica, sans-serif; display: block; border-width: 5px; border-style: solid; border-color: #000 ; margin: 0; }
#menu h2 { color: #000; background: #CCCCCC; }
#menu a { color: #000; background: #efefef; text-decoration: none; }
#menu a:hover { color: #a00; background: #fff; }
#menu li { position: relative; }
#menu ul ul { position: absolute; z-index: 500; }
#menu ul ul ul { position: absolute; top: 0; left: 100%; }
#menu table { cursor: auto; }

div#menu ul ul,
div#menu ul li:hover ul ul,
div#menu ul ul li:hover ul ul { display: none; }

div#menu ul li:hover ul,
div#menu ul ul li:hover ul,
div#menu ul ul ul li:hover ul { display: block; }

div.bw       { position:absolute; top:5px;  right:5px; font-size:8pt; }
div.event    { position:absolute; top:20px; right:5px; font-size:9pt; }
div.feedback { position:absolute; top:5px;   left:5px; font-size:8pt; }

td.event { font-size:10pt; }
</STYLE>
</HEAD>
EOT
}

sub GenerateQuickMenu
{
	my @MechWeights = ExecSql("SELECT Weight, COUNT(Weight) 'Count' FROM Mech GROUP BY Weight ORDER BY Weight ASC");
	my $MechWeightList = '';
	foreach my $mw (@MechWeights)
	{
		$MechWeightList .= "<li><a href=\"?bw=$P{bw}&special=weight$mw->{Weight}\">$mw->{Weight}&nbsp;Tons&nbsp;&nbsp;&nbsp;($mw->{Count} Mechs)</a></li>\n";
	}

	my $str = <<EOT;
<div id="menu">
<ul>
  <li><h2>&nbsp;Quick Menu&nbsp;</h2>
    <ul>
      <li><a>Mechs by Type&nbsp;&nbsp;&nbsp;&nbsp;&gt;&gt;</a>
        <ul>
	  <li><a href=?special=stock>Stock Mechs</a></li>
	  <li><a href=?special=beta>Beta Mechs</a></li>
	  <li><a href=?special=stockbeta>Stock &amp; Beta Mechs</a></li>
	  <li><a href=?special=4.10>4.10 Mechs<br></a></li>
	  <li><a href=?special=variants>Variant Mechs</a></li>
	</ul>
      </li>
      <li><a>Mechs By Weight&nbsp;&nbsp;&nbsp;&nbsp;&gt;&gt;</a>
        <ul>
	$MechWeightList
	</ul>
      </li>
    <ul>
  </li>
</ul>

</div>
EOT
	return $str;
}

sub GenerateWeaponList
{
	my $standalone = defined($_[0]);
	my $bgcolor    = $standalone ? "000000" : "FFFFFF";
	my $sql = "SELECT W.Id, W.ShortName Name, WT.Name 'Weapon Type', WC.Name 'Weapon Class', W.Damage, W.MaxRange 'Range', W.Heat, W.ReloadTime 'Reload Time', W.DPS FROM Weapon W INNER JOIN WeaponType WT ON W.TypeId = WT.Id INNER JOIN WeaponClass WC ON WC.Id = W.ClassId;";
	my @Weapons = ExecSql($sql);
	my @columns = ("Name", "Weapon Type", "Weapon Class", "Damage", "Range", "Heat", "Reload Time", "DPS");
	my $beginMenuHtml = <<EOT;
<div id=menu>
  <ul>
    <li><h2>&nbsp;Weapon Data&nbsp;</h2>
      <ul>
        <li><h2>
EOT
	my $innerHtml = <<EOT;
          <table border=0 class=sortable cellspacing=0 cellpadding=1 bgcolor=#$bgcolor>
EOT
	$innerHtml .= "<tr class=\"r\">";
	foreach $c (@columns)
	{
		$innerHtml .= "<th>&nbsp;$c&nbsp;</th>";
	}
	$innerHtml .= "</tr>\n";

	foreach $w (@Weapons)
	{
		$innerHtml .= "<tr>\n";
		foreach $c (@columns)
		{
			$w->{$c} = 'N/D' if ($w->{$c} eq '');
			$innerHtml .= "<td>$w->{$c}</td>\n";
		}
		$innerHtml .= "</tr>\n";
	}
	$innerHtml .= "<tfoot>";
	$innerHtml .= "<tr><td align=left colspan=" . scalar(@columns) . "><hr></td></tr>\n";
	$innerHtml .= "<tr><td align=left colspan=" . scalar(@columns) . ">C: Clan</td></tr>\n";
	$innerHtml .= "<tr><td align=left colspan=" . scalar(@columns) . ">ER: Extended Range</td></tr>\n";
	$innerHtml .= "<tr><td align=left colspan=" . scalar(@columns) . ">N/D: No Data</td></tr>\n";
	$innerHtml .= "</tfoot>\n";
	my $endMenuHtml .= "</table></h2></li>\n</ul>\n</ul>\n";
	$endMenuHtml .= "</div>\n</div>\n";
	
	if ($standalone)
	{
		return $innerHtml;
	}
	else
	{
		return $beginMenuHtml . $innerHtml . $endMenuHtml;
	}

	return $innerHtml;
}

sub GenerateWeaponFilters
{
	my @WeaponFilters = ();
	for (my $i = 1; $i <= 5; $i++) { push(@WeaponFilters, GenerateWeaponFilterDropdown($i)); }
	return join "<br><br>\n", @WeaponFilters;
}

sub GenerateWeaponFilterDropdown
{
	my $ID = shift;
	my $html = "";
	$html .= "<select name=\"WCfg$ID\">\n  <option value=\"\" default>\n";
	$html .= "  <optgroup label=\"Weapon Type\">\n";
	foreach my $k (sort { $a <=> $b } keys %WeaponTypeIdToName)
	{
		$html .= "    <option value=\"WT$k\"" . ( ($P{"WCfg$ID"} eq "WT$k") ? " selected" : "") . ">$WeaponTypeIdToName{$k}</option>\n";
	}
	$html .= "  </optgroup>\n";

	$html .= "  <optgroup label=\"Weapon Class\">\n";
	foreach my $k (sort { $a <=> $b } keys %WeaponClassIdToName)
	{
		$html .= "    <option value=\"WC$k\"" . ( ($P{"WCfg$ID"} eq "WC$k") ? " selected" : "") . ">$WeaponClassIdToName{$k}</option>\n";
	}
	$html .= "  </optgroup>\n";
	
	$html .= "  <optgroup label=\"Weapons\">\n";
	foreach my $k (sort { $a <=> $b } keys %WeaponIdToName)
	{
		$html .= "    <option value=\"W$k\"" . ( ($P{"WCfg$ID"} eq "W$k") ? " selected" : "") . ">$WeaponIdToName{$k}</option>\n";
	}
	$html .= "  </optgroup>\n";
	$html .= "</select>\n";
	$html .= "<select name=\"WCComparator$ID\">\n";
	$P{"WCComparator$ID"} =~ s/>/&gt;/;
	$P{"WCComparator$ID"} =~ s/</&lt;/;
	foreach my $c ( ("", "&gt;", "&gt;=","=","&lt;=","&lt;") )
	{
		#print "DEBUG: c: '$c' \$P{WCComparator$ID}: '", $P{"WCComparator$ID"}, "'<br>\n" if $ENV{DEBUG};
		$html .= "<option value=\"$c\"" . ($P{"WCComparator$ID"} eq $c ? " selected" : "") . ">$c</option>\n";
	}
	$html .= "</select>\n";
	$html .= "<input name=\"WCValue$ID\" type=text value=\"" . $P{"WCValue$ID"} . "\">\n\n";

	return $html;
}

sub GenerateLookup
{
	my $table = shift;
	my @columns = split (/,/, shift);
	my %Lookup = ();
	my $sql = "SELECT " . join(",", @columns) . " FROM $table";
	my @results = ExecSql($sql);
	foreach my $r (@results)
	{
		$Lookup{$r->{$columns[0]}} = $r->{$columns[1]} if (defined($r->{$columns[0]}) && defined($r->{$columns[1]}));
	}
	return %Lookup;
}

sub GetWeaponData
{
	my $sql = "SELECT * FROM Weapon";
	my @Weapons = ExecSql($sql);
	my %WD = ();
	foreach $w (@Weapons)
	{
		my $W = $Weapons{$w};
		$WD{$w->{Id}}{ShortName}  = $w->{shortname};
		$WD{$w->{Id}}{Name}       = $w->{Name};
		$WD{$w->{Id}}{TypeId}     = $w->{TypeId};
		$WD{$w->{Id}}{ClassId}    = $w->{ClassId};
		$WD{$w->{Id}}{Damage}     = $w->{Damage};
		$WD{$w->{Id}}{MaxRange}   = $w->{MaxRange};
		$WD{$w->{Id}}{Heat}       = $w->{Heat};
		$WD{$w->{Id}}{ReloadTime} = $w->{ReloadTime};
	}
	return %WD;
}

sub GenerateWeaponTooltips
{
	my %WD = @_;
	my %ToolTips;
	foreach $w (keys %WD)
	{
		my %W = %{$WD{$w}};
		$ToolTips{$w} = "Weapon '$W{ShortName}' Damage: $W{Damage} Heat: $W{Heat} Range: $W{MaxRange} Reload: $W{ReloadTime}";
	}
	return %ToolTips;
}

sub PrintMechFilter
{
	# Hack. To preserve checked-state of checkboxes. I don't like this, 'tis inelegant.
	my @ClassChecked = ('','','','');
	if (defined($P{Class}))
	{
		if (ref($P{Class}) eq 'ARRAY') { foreach my $class (@{$P{Class}}) { $ClassChecked[$class-1] = 'checked'; } }
		else                           { $ClassChecked[$P{Class}-1] = 'checked'; }
	}
	my @TechChecked = ('','','');
	if (defined($P{Tech}))
	{
		if (ref($P{Tech}) eq 'ARRAY') { foreach my $tech (@{$P{Tech}}) { $TechChecked[$tech-1] = 'checked'; } }
		else                          { $TechChecked[$P{Tech}-1] = 'checked'; }
	}
	my @EquipmentChecked = ('','','','','','');
	if (defined($P{Equipment}))
	{
		if (ref($P{Equipment}) eq 'ARRAY') { foreach my $equipment (@{$P{Equipment}}) { $EquipmentChecked[$equipment-1] = 'checked'; }}
		else                               { $EquipmentChecked[$P{Equipment}-1] = 'checked' }
	}
	my $WeaponFilters = GenerateWeaponFilters();
	my $quickMenu = GenerateQuickMenu();
	my $WeaponList = GenerateWeaponList();

	my $str = <<EOT; 
<div class="filter">
<FORM name="MechFilter" action="$script" method="GET">
<INPUT type="hidden" name="bw" value="$P{bw}">
EOT
		
	$str .= "<INPUT type=text size=150 name=\"Zax\" value=\"$P{Zax}\">" if (defined($P{Zax}));
	$str .= <<EOT;
<TABLE border=0 cellspacing=0 cellpadding=3>
  <TR><TH class="filter">Name</TH><TD colspan=3 class="filter"><INPUT onKeyPress="return submitenter(this,event)" type=text size=25 name="Name" value="$P{Name}"></TD><TH class="filter">Equipment</TH><TD class="filter"><INPUT type="checkbox" name="Equipment" value=1 $EquipmentChecked[0]>BAP&nbsp;<INPUT type="checkbox" name="Equipment" value=2 $EquipmentChecked[1]>JJ&nbsp;<INPUT type="checkbox" name="Equipment" value=3 $EquipmentChecked[2]>ECM&nbsp;<INPUT type="checkbox" name="Equipment" value=4 $EquipmentChecked[3]>AMS&nbsp;<!--<INPUT type="checkbox" name="Equipment" value=5 $EquipmentChecked[4]>LA&nbsp;--><INPUT type="checkbox" name="Equipment" value=6 $EquipmentChecked[5]>IFF&nbsp;</TD></TR>
  <TR><TH class="filter">Variant</TH><TD colspan=3 class="filter"><INPUT onKeyPress="return submitenter(this,event)" type=text size=25 name="VariantName" value="$P{VariantName}">
<TD RowSpan=6 colspan=2 class="filter">
    <TABLE Id='ConfigFilter'>
      <TR>
        <th valign=middle>&nbsp;&nbsp;Weapon&nbsp;&nbsp;<br>Config</td></th>
        <TD valign=middle>
$WeaponFilters
	</TD>
      </TR>
    </TABLE>
  </TD>
  
</TR>
  <TR><TH class="filter">Chassis</TH><TD class="filter" colspan=3><INPUT onKeyPress="return submitenter(this,event)" type=text size=25 name="Chassis" value="$P{Chassis}"></TD></TR>
  <TR><TH class="filter">Weight</TH><TD class="filter"><INPUT onKeyPress="return submitenter(this,event)" type=text size=3 name="MinWeight" value="$P{MinWeight}"> to <INPUT type=text size=3 name="MaxWeight" value="$P{MaxWeight}">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  <TH class="filter">Speed</TH><TD class="filter"><INPUT onKeyPress="return submitenter(this,event)" type=text size=3 name="MinSpeed" value="$P{MinSpeed}"> to <INPUT type=text size=3 name="MaxSpeed" value="$P{MaxSpeed}"></TD></TR>
  <TR><TH class="filter">Armor</TH><TD class="filter"><INPUT onKeyPress="return submitenter(this,event)" type=text size=3 name="MinArmor" value="$P{MinArmor}"> to <INPUT type=text size=3 name="MaxArmor" value="$P{MaxArmor}">
  <TH class="filter">Sinks</TH><TD class="filter"><INPUT onKeyPress="return submitenter(this,event)" type=text size=3 name="MinSinks" value="$P{MinSinks}"> to <INPUT type=text size=3 name="MaxSinks" value="$P{MaxSinks}"></TD></TR>
  <TR><TH class="filter">Class</TH><TD colspan=3 class="filter"><INPUT type="checkbox" name="Class" value=1 $ClassChecked[0]>Light&nbsp;<INPUT type="checkbox" name="Class" value=2 $ClassChecked[1]>Medium&nbsp;<INPUT type="checkbox" name="Class" value=3 $ClassChecked[2]>Heavy&nbsp;<INPUT type="checkbox" name="Class" value=4 $ClassChecked[3]>Assault&nbsp;</TD></TR>
  <TR><TH class="filter">Tech</TH><TD colspan=3 class="filter"><INPUT type="checkbox" name="Tech" value=1 $TechChecked[0]>Clan&nbsp;<INPUT type="checkbox" name="Tech" value=2 $TechChecked[1]>Inner Sphere&nbsp;<INPUT type="checkbox" name="Tech" value=3 $TechChecked[2]>Clan&nbsp;&amp;&nbsp;IS</TD></TR>
  <TR><TD colspan=6><table border=0 cellspacing=0 cellpadding=0><tr><TD><Input type=submit name="Search" value="Search">&nbsp;&nbsp;&nbsp;&nbsp;<INPUT type=Button value="Clear" name="Clear" onClick="formClear()">&nbsp;&nbsp;&nbsp;&nbsp;</TD><TD><table border=0 cellspacing=0 cellpadding=0><tr><td>$quickMenu</td><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td><td>$WeaponList</td></tr></table></table></TD></TR>
</TABLE>
</FORM>
</div>
EOT
	print $str;
}

sub ExecSql
{
	my $sql = shift;
	my $sth = $dbh->prepare($sql);
	$sth->execute();

	my @results = ();

	while ($hr = $sth->fetchrow_hashref)
	{
		push(@results, $hr);
	}
	return @results;
}

sub PrintHTMLList
{
	my $title = shift; # currently unused
	my @headers = ();
	if (ref($_[0]) eq 'ARRAY')
	{
		@headers = @{$_[0]};
		shift @_;
	}
	else
	{
		@headers = sort keys %{$_[0]};
	}

	my @listData = @_;
	my $sortable = ($P{bw} eq 'h' ? "class=\"sortable\"" : "");
	my $table = "<TABLE CELLSPACING=0 CELLPADDING=2 $sortable style=\"border-width: 1px; border-style: solid; border-color: #333333; margin: 0; \">\n";
	$table .=   "  <THEAD><TR><TH class='sortable'>" . join ("</TH><TH class='sortable'>", @headers) . "</TH></TR></THEAD>\n";

	my $tr = "";
	foreach (@listData)
	{
		my @rowData = ();
		foreach my $hdr (@headers)
		{
			$_->{$hdr} =~ s/, /<br>/g;
#			$_->{$hdr} =~ s/ /&nbsp;/g;

			if ($P{bw} eq 'h') # Enhanced filtration options for high-bandwidth only
			{	
				# maybe?		
				#$_->{$hdr} =~ s/^(Stock)(.*)$/<a href=\"?VariantName=$1\">$1$2<\/a>/      if ($hdr eq 'VariantName');
				$_->{$hdr} =~ s/^$/***/                                               if ($hdr eq 'AlphaStrike' || $hdr eq 'DPS');
				$_->{$hdr} =~ s/^(.+)$/<a href=\"?Name=$1\">$1<\/a>/                  if ($hdr eq 'Name');
				$_->{$hdr} =~ s/^(.+)$/<a href=\"?Chassis=$1\">$1<\/a>/               if ($hdr eq 'Chassis');
				$_->{$hdr} =~ s/^(.+)$/<a href=\"?Class=$ClassNameToId{$1}\">$1<\/a>/ if ($hdr eq 'Class');
				$_->{$hdr} =~ s/^(.+)$/<a href=\"?Tech=$TechNameToId{$1}\">$1<\/a>/   if ($hdr eq 'Tech');
				if ($hdr =~ m/^(BAP|JJ|ECM|AMS|IFF|LA)$/)
				{
					$_->{$hdr} =~ s/^(.+)$/<a href=\"?Equipment=$EquipmentNameToId{$1}\">$1<\/a>/;
				}
			}
			else # Shorten things for low-bandwidth
			{
				$_->{$hdr} =~ s/^(.).+$/$1/                       if ($hdr eq 'Class');
				$_->{$hdr} =~ s/^Clan/C/                          if ($hdr eq 'Tech');
				$_->{$hdr} =~ s/^(.+)$/$ArmorTypeLongToShort{$1}/ if ($hdr eq 'ArmorType');
				if ($hdr eq 'Equipment')
				{
					my @tmp = ();
					foreach $e (('BAP','JJ','ECM','AMS','IFF')) 
					{
						if ($_->{$e} ne '')
						{
							$_->{$e} =~ m/^(.).+$/;
							push(@tmp, $1);
						}
					}
					$_->{$hdr} = join(",", @tmp);
				}
				$_->{$hdr} =~ s/\(Rear\)/(R)/ if ($hdr ~~ ('Ballistic','Beam','Missile','Narc','Flare'));

				if ($hdr eq 'Ballistic')
				{
					$_->{$hdr} =~ s/Heavy Gauss/H.Gauss/;
					$_->{$hdr} =~ s/Light Gauss/L.Gauss/;
				}
				elsif ($hdr eq 'Beam')
				{
					$_->{$hdr} =~ s/Pulse/Pls/g;
					$_->{$hdr} =~ s/Medium/Med/g;
					$_->{$hdr} =~ s/Small/Sml/g;
					$_->{$hdr} =~ s/Large/Lrg/g;
				}
			}
			push(@rowData, (defined($_->{$hdr}) && $_->{$hdr} ne '' ? $_->{$hdr} : '&nbsp;'));
		}
		my $trClass = ($P{bw} eq 'h' ? ' class="r"' : '');
		$tr .= "<tr" . $trClass . "><td class=\"data\">" . join ("</td><td class=\"data\">", @rowData) . "</td></tr>\n";
	}
	$table .= $tr;
	$table .= "<TFOOT><TR><TD class=\"data\" colspan=" . scalar(@headers) . ">Found " . scalar(@listData) . " mechs</TD></TR></TFOOT>\n";
	$table .= "</TABLE>\n";
	print $table;
	
}

sub ParseParams 
{
	## TODO: Consolidate all of the WC, WT and W options into an array so we can do a "HAVING (WT,WT,WT)" clause. might be faster than what we have now.
	foreach my $p (split (/&/, $ENV{'QUERY_STRING'}))
	{
		my ($k,$v) = split (/=/, $p);
                if ($v ne "")
		{
			$v =~ s/\+/ /g;
			$v = uri_unescape($v);
			$v =~ s/%27/''/g;
			$v =~ s/%A0/ /g;
			$v =~ s/%3C/</ if $k =~ m/^WCComparator/;
			$v =~ s/%3D/=/ if $k =~ m/^WCComparator/;
			$v =~ s/%3E/>/ if $k =~ m/^WCComparator/;
			print "DEBUG: \$k: $k&nbsp;&nbsp;&nbsp;&nbsp;\$v: $v<br>\n" if ($ENV{DEBUG});
			if (defined($P{$k})) # Arrays!
			{
				print "Existing key found" if ($ENV{DEBUG});
				if (ref($P{$k}) eq '')
				{
					print ", converting to array...<br>\n" if ($ENV{DEBUG});
					my @arr = ();
					push(@arr, $P{$k});
					push(@arr, $v);
					$P{$k} = \@arr;
				}
				elsif (ref($P{$k}) eq 'ARRAY')
				{
					print ", adding to array...<br>\n" if ($ENV{DEBUG});
					push(@{$P{$k}}, $v);
				}
			}
			else
			{
				$P{$k} = $v;
			}
		}
	}

	$P{bw} = "h" if (!defined($P{bw}));

	if ($ENV{DEBUG})
	{
		print "ParseParams:<br>\n";
		foreach my $k (keys %P) 
		{ 
			if (ref($P{$k}) eq 'ARRAY')
			{
				foreach my $a (@{$P{$k}})
				{
					print "$k: $a<br>\n";
				}
			}
			else
			{
				print "$k: $P{$k}<br>\n"; 
			}
		}
		print "<BR>\n";
	}
	print "<hr><br>\n" if $ENV{DEBUG};
}


# The meat. This takes all of the selected options and constructs a query out of them. 
# It actually only constructs half the query; this query is passed as a parameter to
# a stored procedure which concatenates it in the middle of a query, evals it and execs it.
sub BuildRestrictionStatement
{
	if (defined($P{Zax}))
	{
		$tmp = $P{Zax};
		$tmp =~ s/'/''/g;
		return $tmp;
	}

	# Special cases that return established strings
	if (defined($P{special}))
	{
		return "SELECT Id FROM Mech WHERE Weight = $1"                               if ($P{special} =~ m/weight(\d\d\d?)/);
		return "SELECT Id FROM Mech WHERE VariantName = ''Stock Test''"              if ($P{special} eq 'beta');
		return "SELECT Id FROM Mech WHERE VariantName = ''Stock''"                   if ($P{special} eq 'stock');
		return "SELECT Id FROM Mech WHERE VariantName IN (''Stock Test'',''Stock'')" if ($P{special} eq 'stockbeta');
		return "SELECT Id FROM Mech WHERE RIGHT(VariantName,4) = ''4.10''"           if ($P{special} eq '4.10');
		return "SELECT Id FROM Mech WHERE Id <= 453"                                 if ($P{special} eq 'variants');
	}

	my @WHERES = ();
	my @JOINS  = ();
	my @GROUPBY = ();
	my @HAVING = ();

	# Arrays for OR-ing within a given attribute
	my @CLASSES        = ();
	my @TECHS          = ();
	my @EQUIPMENT      = ();
	my @WEAPONTYPES    = ();
	my @WEAPONTYPEIDS  = ();
	my @WEAPONCLASSES  = ();
	my @WEAPONCLASSIDS = ();
	my @WEAPONS        = ();
	my $WC = 0;
	foreach my $p (keys %P)
	{
		push(@WHERES, "$1 >= " . $P{$p})        if ($p =~ m/^Min(.+)$/i);
		push(@WHERES, "$1 <= " . $P{$p})        if ($p =~ m/^Max(.+)$/i);
		push(@WHERES, "Name LIKE ''\%$P{$p}\%'' OR ISName LIKE ''\%$P{$p}\%'' OR ClanName LIKE ''\%$P{$p}\%''") if ($p eq 'Name');
		push(@WHERES, "VariantName LIKE ''\%$P{$p}\%''") if ($p eq 'VariantName');
		if ($p eq 'Chassis')
		{
			push(@JOINS,  "INNER JOIN Chassis C ON M.ChassisId = C.Id");
			push(@WHERES, "C.Name LIKE ''\%$P{$p}\%''");
		}
		if ($p eq 'Class')
		{
			if (ref($P{$p}) eq 'ARRAY') { foreach my $c (@{$P{$p}}) { push(@CLASSES, "ClassId = $c");   } }
			else                        { push(@CLASSES, "ClassId = $P{$p}"); }
		}
		if ($p eq 'Tech') 
		{
			if (ref($P{$p}) eq 'ARRAY') { foreach my $t (@{$P{$p}}) { push(@TECHS, "TechId = $t"); } }
			else                        { push(@TECHS, "TechId = $P{$p}"); }
		}
		if ($p eq 'Equipment')
		{
			if (ref($P{$p}) eq 'ARRAY') { foreach my $e (@{$P{$p}}) { push(@EQUIPMENT, "$e"); } }
			else                        { push(@EQUIPMENT, "$P{$p}"); }
		}

		if ($p =~m/^WCfg(\d)/)
		{
			my $comparator = uri_unescape($P{"WCComparator$1"}) || '>';
			my $value      = defined($P{"WCValue$1"}) ? uri_unescape($P{"WCValue$1"}) : 0;
			next unless(defined($comparator) && $comparator ne "" && defined($value) && $value ne "");

			if ($P{$p} =~ m/^WC(\d+)/)
			{
				push(@WEAPONCLASSES, 
					"M.Id IN (
						SELECT Id FROM (
							SELECT Id, 
								(SELECT COUNT(WeaponId)
								 FROM WeaponConfig WC
								 INNER JOIN Weapon W ON WC.WeaponId = W.Id
								 WHERE MM.Id = WC.MechId
								   AND W.ClassId = $1) nWeapon
							FROM Mech MM
							HAVING nWeapon $comparator $value) x )");
			}
			elsif ($P{$p} =~ m/^WT(\d+)/)
			{
				push(@WEAPONTYPES, 
					"M.Id IN (
						SELECT Id FROM (
							SELECT Id,
								(SELECT COUNT(WeaponId)
								 FROM WeaponConfig WC
								 INNER JOIN Weapon W ON WC.WeaponId = W.Id
								 WHERE MM.Id = WC.MechId
								   AND W.TypeId = $1) nWeapon
							FROM Mech MM
							HAVING nWeapon $comparator $value) x )");
			}
			elsif ($P{$p} =~ m/^W(\d+)/)
			{
				push(@WEAPONS, 
					"M.Id IN (
						SELECT Id FROM (
							SELECT Id,
								(SELECT COUNT(WeaponId)
								 FROM WeaponConfig WC
								 WHERE MM.Id = WC.MechId
								   AND WC.WeaponId = $1) nWeapon
							FROM Mech MM
							HAVING nWeapon $comparator $value) x )");
			}
		}
	}
	my $WeaponTypeString  = "(" . join(") AND (", @WEAPONTYPES)   . ")" if (scalar(@WEAPONTYPES));
	my $WeaponClassString = "(" . join(") AND (", @WEAPONCLASSES) . ")" if (scalar(@WEAPONCLASSES));
	my $WeaponString      = "(" . join(") AND (", @WEAPONS)       . ")" if (scalar(@WEAPONS));

	my $TechString      = "(" . join(" OR ",  @TECHS)     . ")" if (scalar(@TECHS));
	my $ClassString     = "(" . join(" OR ",  @CLASSES)   . ")" if (scalar(@CLASSES));

	# This is a complete hack and totally inelegant. But it works!
	my $EquipmentString = "M.Id IN (SELECT MechId FROM EquipmentConfig EC GROUP BY MechId HAVING GROUP_CONCAT(EquipmentId ORDER BY EquipmentId ASC, '''') LIKE ''%" . join("%", @EQUIPMENT) . "%'')" if (scalar(@EQUIPMENT));
	
	push(@WHERES, $ClassString)       if ($ClassString ne '');
	push(@WHERES, $TechString)        if ($TechString ne '');
	push(@WHERES, $EquipmentString)   if ($EquipmentString ne '');
	push(@WHERES, $WeaponTypeString)  if ($WeaponTypeString ne '');
	push(@WHERES, $WeaponClassString) if ($WeaponClassString ne '');
	push(@WHERES, $WeaponString)      if ($WeaponString ne '');

	my $WhereString   = " " . join(" AND ", @WHERES) if (scalar(@WHERES));
	my $JoinString    = " " . join(" ", @JOINS) if (scalar(@JOINS));
	my $RestrictionStatement = "SELECT DISTINCT M.Id FROM Mech M";

	$RestrictionStatement .= $JoinString if (length($JoinString) > 0);
	$RestrictionStatement .= " WHERE $WhereString" if (length($WhereString) > 0);
	return $RestrictionStatement;
}

