#!/usr/bin/perl -w
#
# poke.pl
#
# J. Uckelman (uckelman@iastate.edu)
# 20 December 2000
#

require 5.005;
use DBI;

my $version = 1;
my $config = '/home/g1/bin/scripts.conf';

# load the config file 
eval { require $config; }; die "$0: $config: $@\n" if $@;

# connect to game database
$dbh = DBI->connect($dsn, $user, $password);
$dsn = $user = $password = 0; # avoid annoying "variable used only once" warnings

# pretty output
print "\npoke.pl version $version, by J. Uckelman\n\n";

# get data
my $data = join '', <STDIN>;

my $dry = 0;
if ($data =~ m/^dry\n/s) { 
	$dry = 1;
	$data = $';
	print "dry run\n";
}

# get records
while ($data) {
	my ($command, $type) = $data =~ m/^(.*?) (.*?)\n/ or die "bad command or type";
	$data = $';
	if ($type eq 'proposal') {
		if ($command eq 'add') {
			
			# Format:
			#	
			# number/revision
			# vitality,activity
			# mdate
			# rdate
			# owner
			# title
			#
			# text
			#
			# ----
		
			# get prop parts
			my ($number, $revision, $vitality, $activity, $mdate, $rdate, $owner, $title, $text) = $data =~ m/^(\d+)\/(\d+)\n(\w),(\w)\n(.*?)\n(.*?)\n(.*?)\n(.*?)\n\n(.*?)\n-{4}\n/s or die "bad format";
			$data = $';
	
			# get owner id
			$owner = quotemeta($owner);
			my $owner_id = &getid($owner);
		
			# convert made and recognized dates
			$mdate = &convdate($mdate);
			$rdate = &convdate($rdate);

			# guard title and text
			$title = quotemeta($title);
			$text = quotemeta($text);

			# poke into db
			unless ($dry) {
				$dbh->do("insert into proposal (number, revision, owner, title, text) values ($number, $revision, $owner_id, '$title', '$text')") or die "oh god no";
		
				if ($revision == 0) { $dbh->do("insert into event (date, event) values ('$mdate', '$owner made a Proposal [$number]')"); }
				else {
					$dbh->do("insert into event (date, event) values ('$mdate', '$owner revised Proposal $number')");
					$dbh->do("update proposal set vitality = 'd' where number = $number and revision <> $revision");
				}

				$dbh->do("insert into proposal0history (number, revision, id) values ($number, $revision, last_insert_id())");
				$dbh->do("insert into event (date, event) values ('$rdate', 'Administrator recognized Proposal $number/$revision')");
				$dbh->do("insert into proposal0history (number, revision, id) values ($number, $revision, last_insert_id())");
			}
	
			print "P$number/$revision added\n";
		}
		elsif ($command eq 'withdraw') {
			
			# Format:
			#
			# number/revision
			# date
			# owner
			#
			# ----
		
			# get parts
			my ($number, $revision, $date, $owner) = $data =~ m/^(\d+)\/(\d+)\n(.*?)\n(.*?)\n\n-{4}\n/s or die "bad format";
			$data = $';

			# convert date
			$date = &convdate($date);

			# get owner id
			$owner = quotemeta($owner);
			my $owner_id = &getid($owner);

			# poke into db
			unless ($dry) {
				$dbh->do("update proposal set vitality = 'd' where number = $number and revision = $revision") or die "oh god no";
				$dbh->do("insert into event (date, event) values ('$date', '$owner withdrew Proposal $number/$revision')");
				$dbh->do("insert into proposal0history (number, revision, id) values ($number, $revision, last_insert_id())");
			}
		
			print "P$number/$revision withdrawn\n";		
		}
		elsif ($command eq 'activate' or $command eq 'deactivate') {
			
			# Format:
			#
			# number/revision
			# date
			# owner
			#
			# ----

			# get parts
			my ($number, $revision, $date, $owner) = $data =~ m/^(\d+)\/(\d+)\n(.*?)\n(.*?)\n\n-{4}\n/s or die "bad format";
			$data = $';

			# convert date
			$date = &convdate($date);

			# get owner id
			$owner = quotemeta($owner);
			my $owner_id = &getid($owner);

			my $activity = $command eq 'activate' ? 'a' : 'i';

			# poke into db
			unless ($dry) {
				$dbh->do("update proposal set activity = '$activity' where number = $number and revision = $revision")or die "oh god no";
				$dbh->do("insert into event (date, event) values ('$date', '$owner ${command}d Proposal $number/$revision')");
				$dbh->do("insert into proposal0history (number, revision, id) values ($number, $revision, last_insert_id())");
			}
			
			print "P$number/$revision ${command}d\n";
		}
		else { die "bad command"; }
	}
	elsif ($type eq 'rfj') {
		if ($command eq 'add') {
			
			# Format:
			#	
			# number/revision
			# vitality
			# mdate
			# rdate
			# plaintiff
			# statement
			# judge
			#
			# p_analysis
			#
			# ----
		
			# get rfj parts 
			my ($number, $revision, $vitality, $mdate, $rdate, $plaintiff, $statement, $judge, $p_analysis) = $data =~ m/^(\d+)\/(\d+)\n(\w)\n(.*?)\n(.*?)\n(.*?)\n(.*?)\n(.*?)\n\n(.*?)\n-{4}\n/s or die "bad format";
			$data = $';
	
			# get plaintiff and judge ids
			my $plaintiff_id = &getid($plaintiff);
			my $judge_id = &getid($judge);

			# convert made and recognized dates
			$mdate = &convdate($mdate);
			$rdate = &convdate($rdate);

			# guard statement and p_analysis
			$statement = quotemeta($statement);
			$p_analysis = quotemeta($p_analysis);

			# poke into db
			unless ($dry) {
				$dbh->do("insert into judgment (number, revision, plaintiff, statement, p_analysis) values ($number, $revision, $plaintiff_id, '$statement', '$p_analysis')") or die "oh god no";
				$dbh->do("insert into event (date, event) values ('$mdate', '$plaintiff made an RFJ [$number]')");
				$dbh->do("insert into judgment0history (number, revision, id) values ($number, $revision, last_insert_id())");
				$dbh->do("insert into event (date, event) values ('$rdate', 'Administrator recognized RFJ $number/$revision')");
				$dbh->do("insert into judgment0history (number, revision, id) values ($number, $revision, last_insert_id())");
				$dbh->do("insert into event (date, event) values ('$rdate', 'Administrator selected $judge to judge RFJ $number/$revision')");
				$dbh->do("insert into judgment0history (number, revision, id) values ($number, $revision, last_insert_id())");
				$dbh->do("insert into judgment0judge (number, revision, id) values ($number, $revision, $judge_id)");
			}

			print "RFJ$number/$revision added\n";
		}
		elsif ($command eq 'reassign') {
			
			# Format
			#
			# number/revision
			# date
			# old judge
			# new judge
			#
			# ----

			# get recusal parts
			my ($number, $revision, $date, $ojudge, $njudge) = $data =~ m/^(\d+)\/(\d+)\n(.*?)\n(.*?)\n(.*?)\n\n-{4}\n/s or die "bad format";
			$data = $';
	
			# get old and new judge ids
			$ojudge = quotemeta($ojudge);
			$njudge = quotemeta($njudge);
			my $ojudge_id = &getid($ojudge);
			my $njudge_id = &getid($njudge);

			# convert date
			$date = &convdate($date);
			
			# poke into db
			unless ($dry) {
				$dbh->do("insert into event (date, event) values ('$date', 'Administrator recused $ojudge from RFJ $number/$revision')");
				$dbh->do("insert into judgment0history (number, revision, id) values ($number, $revision, last_insert_id())");
				$dbh->do("insert into event (date, event) values ('$date', 'Administrator selected $njudge to judge RFJ $number/$revision')");
				$dbh->do("insert into judgment0history (number, revision, id) values ($number, $revision, last_insert_id())");
				$dbh->do("insert into judgment0judge (number, revision, id) values ($number, $revision, $njudge_id)");
				$dbh->do("update judgment0judge set recused = 'y' where number = $number and revision = $revision and id = $ojudge_id");
			}

			print "RFJ$number/$revision reassigned\n";
		}
		else { die "bad command"; }	
	}
	elsif ($type eq 'judgment') {
		if ($command eq 'add') {
			
			# Format:
			#	
			# number/revision
			# date
			# judge
			# ruling
			#
			# j_analysis
			#
			# ----
		
			# get judgment parts 
			my ($number, $revision, $date, $judge, $ruling, $j_analysis) = $data =~ m/^(\d+)\/(\d+)\n(.*?)\n(.*?)\n(.*?)\n\n(.*?)\n-{4}\n/s or die "bad format";
			$data = $';
	
			# convert date
			$date = &convdate($date);

			# guard j_analysis
			$j_analysis = quotemeta($j_analysis);
			$judge = quotemeta($judge);

			# poke into db
			unless ($dry) {
				$dbh->do("update judgment set ruling='$ruling', j_analysis='$j_analysis', vitality = 'd' where number = $number and revision = $revision") or die "oh god no";
				$dbh->do("insert into event (date, event) values ('$date', '$judge ruled \"$ruling\" on RFJ $number/$revision')");
				$dbh->do("insert into judgment0history (number, revision, id) values ($number, $revision, last_insert_id())");
			}

			print "J$number/$revision added\n";
		}
		else { die "bad command"; }
	}
	elsif ($type eq 'rule') {
		if ($command eq 'add') {
		
			# Format:
			#
			# number/revision
			# vitality
			# date
			# title
			# pnum/prev
			#
			# text
			#
			# ----

			# get rule parts
			my ($number, $revision, $vitality, $date, $title, $pnum, $prev, $text) = $data =~ m/^(\d+)\/(\d+)\n(\w)\n(.*?)\n(.*?)\n(\d+)\/(\d+)\n\n(.*?)\n\n-{4}\n/s or die "bad format";
			$data = $';

			# convert date
			$date = &convdate($date);

			# guard title and text
			$title = quotemeta($title);
			$text = quotemeta($text);

			my $orevision = $revision - 1;

			# poke into db
			unless ($dry) {
				$dbh->do("insert into rule (number, revision, vitality, title, text) values ($number, $revision, '$vitality', '$title', '$text')");
				$dbh->do("update rule set vitality = 'd' where number = $number and revision < $revision");
				
				if ($revision == 0) { $dbh->do("insert into event (date, event) values ('$date', 'R$number/0 created by P$pnum/$prev')"); }
				else { $dbh->do("insert into event (date, event) values ('$date', 'R$number/$orevision amended by P$pnum/$prev')"); }
			
				$dbh->do("insert into rule0history (number, revision, id) values ($number, $revision, last_insert_id())");
			}

			print "R$number/$revision added\n";
		}
		elsif ($command eq 'repeal') {

				# Format:
				#
				# number/revision
				# date
				# pnum/prev
				#
				# ----
	
				# get repeal parts
				my ($number, $revision, $date, $pnum, $prev) = $data =~ m/^(\d+)\/(\d)\n(.*?)\n(\d+)\/(\d+)\n\n-{4}\n/s or die "bad format";
				$data = $';

				# convert date
				$date = &convdate($date);

				# poke into db
				unless ($dry) {
					$dbh->do("update rule set vitality = 'd' where number = $number and revision = $revision");
					$dbh->do("insert into event (date, event) values ('$date', 'R$number/0 repealed by P$pnum/$prev')");
					$dbh->do("insert into rule0history (number, revision, id) values ($number, $revision, last_insert_id())");
				}

				print "R$number/$revision repealed\n";
		}

		else { die "bad command"; }
	}
	elsif ($type eq "player") {
		if ($command eq 'add') { 
			
			# Format:
			#
			# name
			# address
			# date
			#
			# ----

			# get player parts
			my ($name, $address, $date) = $data =~ m/^(.*?)\n(.*?@.*?)\n(.*?)\n\n-{4}\n/s or die "bad format";
			$data = $';

			# convert date
			$date = &convdate($date);

			# get max entity id
			my $sth = $dbh->prepare("select max(id) from entity");
			$sth->execute;
			my ($id) = $sth->fetchrow_array;
			$sth->finish;
			$id++;

			$name = quotemeta($name);

			# poke into db
			unless ($dry) {
				$dbh->do("insert into entity (id, type) values ($id, 'player')");
				$dbh->do("insert into name (id, name) values ($id, '$name')");
				$dbh->do("insert into mail (id, address) values ($id, '$address')");
				$dbh->do("insert into player (id) values ($id)");
				$dbh->do("into event (date, event) values ('$date', '$name declares emself a Player')");
			}

			print "Player $name added\n";
		}
		else { die "bad command"; }
	}
	elsif ($type eq 'voting') {
		if ($command eq 'add') {

			# Format:
			#
			# bdate
			# edate
			# quorum
			#
			# prop/rev[, prop/rev]...
			#
			# name
			# vote[, vote]...
			#
			# ----

			# get parts
			my ($bdate, $edate, $quorum, $props, $votes) = $data =~ m/^(.*?)\n(.*?)\n(.*)\n\n(.*?)\n\n(.*?)\n-{4}\n/s or die "bad format";
			$data = $';

			# convert dates
			$bdate = &convdate($bdate);
			$edate = &convdate($edate);
			
			# get prop numbers
			my (@prop, @rev, @tmp, @vote, @name) = ();
			@tmp = split / /, $props;
			for $i (0 .. $#tmp) { ($prop[$i], $rev[$i]) = $tmp[$i] =~ m/(\d+)\/(\d+)/ or die "bad format"; } 

			# get names and votes
			@tmp = split /\n/, $votes;
			for $i (0 ..  int($#tmp / 2)) {
				$name[$i] = quotemeta($tmp[2*$i]);
				push @vote, [ split / /, $tmp[2*$i+1] ];
			}

			# calculate quorum
			if ($quorum eq 'vote') {
				$quorum = 0;
				for $i (0 .. $#vote) { 
						VOTE: for $j (0 .. $#prop) {
							if ($vote[$i][$j] =~ m/[yna]/) {
								$quorum++;
								last VOTE;
							}
						}
					}
			}
	
			# poke voting period into db
			unless ($dry) {
				$dbh->do("insert into event (date, event) values ('$bdate', 'Voting began')");
				$dbh->do("insert into event (date, event) values ('$edate', 'Voting ended, quorum was $quorum')");
			}
			
			# calculate passage
			for $i (0 .. $#prop) {
				my ($yea, $nay, $abs, $not, $result) = (0, 0, 0, 0, 0);
				for $j (0 .. $#name) {
					$yea++ if $vote[$j][$i] eq 'y';
					$nay++ if $vote[$j][$i] eq 'n';
					$abs++ if $vote[$j][$i] eq 'a';
					$not++ if $vote[$j][$i] eq '-';	
				
					# poke individual voting into db
					unless ($dry) {				
						my $voter_id = &getid($name[$j]);
						$dbh->do("insert into proposal0voting (number, revision, id, date, vote) values ($prop[$i], $rev[$i], $voter_id, '$edate', '$vote[$j][$i]')");
					}
				}
				
				$result = ($yea + $nay + $abs) >= $quorum ? ($yea > $nay ? "passed" : "failed") : "held";
				printf("\nP%d/%d %s (%d-%d-%d-%d)", $prop[$i], $rev[$i], $result, $yea, $nay, $abs, $not);
			
				# poke voting result into db
				unless ($dry) {
					$dbh->do("update proposal set vitality = 'd' where number = $prop[$i] and revision = $rev[$i]") unless $result eq 'held';
					$dbh->do("insert into event (date, event) values ('$edate', 'Proposal $prop[$i]/$rev[$i] $result ($yea-$nay-$abs-$not)')") or die 'oh god no';
					$dbh->do("insert into proposal0history (number, revision, id) values ($prop[$i], $rev[$i], last_insert_id())");
				}

				# calculate scoring
				my @score = ();
				if ($result ne 'held') {
					my $sth = $dbh->prepare("select owner from proposal where number = $prop[$i] and revision = $rev[$i]");
					$sth->execute;
					my ($owner_id) = $sth->fetchrow_array;
					$sth->finish;

					if ($result	eq 'passed') {
						push @score, ($owner_id, int(20 * ($yea / ($yea + $nay))));
						
						for $j (0 .. $#name) {
							if ($vote[$j][$i] eq 'n') { 
								push @score, (&getid($name[$j]), int(5 * ($yea / ($yea + $nay))));
							}
						}
					}
					elsif ($result eq 'failed') {	push @score, ($owner_id, -5); }
				}
			
				# poke scoring into db
				unless ($dry) {
					while (@score) {
						my $id = shift @score;
						my $newpoints = shift @score;
	
						$sth = $dbh->prepare("select score from entity where id = $id and activity = 'a'");
						$sth->execute;
						my ($oldpoints) = $sth->fetchrow_array;
						$sth->finish;
		
						my $action = ($newpoints >= 0) ? "credited" : "debited";
						my $nowpoints = $newpoints + $oldpoints;	
						my $scorer = &getname($id);				
	
						$dbh->do("update entity set score = $nowpoints where id = $id and activity = 'a'");
						$dbh->do("insert into event (date, event) values ('$edate', 'Administrator $action $scorer $newpoints Points for Proposal $prop[$i]/$rev[$i]')");
					}
				}
			}

			print "\n";
		}
		else { die "bad command"; }
	}
	else { die "bad type"; }
}

$dbh->disconnect;

#
# getid
#
sub getid ($) {
	my ($name) = @_;
	my $sth = $dbh->prepare("select id from name where name = '$name' and activity = 'a'");
	$sth->execute;
	my ($name_id) = $sth->fetchrow_array;
	$sth->finish;
	return $name_id;
}

#
# getname
#
sub getname ($) {
	my ($id) = @_;
	my $sth = $dbh->prepare("select name from name where id = $id and activity = 'a'");
	$sth->execute;
	my ($name) = $sth->fetchrow_array;
	$sth->finish;
	return quotemeta($name);
}

#
# convdate
#
sub convdate ($) {
	my ($date) = @_;
	($date = `date -d '$date' +"%Y-%m-%d %T"`) =~ s/\n//;
	return $date;
}
