#!/usr/bin/perl

use CGI; 

use DBI;
use MD5; 

$DB = "DBI:mysql:a348"; 

$username = "a348"; 
$password = "a348AG"; 

$DB_TABLE = "dgerman_pprrckscs"; 
$SECRET = "something secret"; 

$EXPIRE = 30 * 60 * 60 * 24; # one month

$MAX_TRIES = 10; 
$ID_LENGTH = 8;

$q = new CGI; 

$DBH = DBI->connect($DB, $username, $password, { PrintError => 0 }) 
       || die "Couldn't open database: ", $DBI::errstr; 

my ($session_id) = &get_session_id(); 

print $q->header, $q->start_html; 

my $state          = &get_state($session_id); 

if (! $state->{message}) { 
  $state = &initialize($state); 
} else { 
  $userChoice = $q->param('user'); 
  $state = &calculate($state, $userChoice); 
} 

&save_state($state, $session_id); 

&report($state); 

&show_form(); # get ready for more input 

print $q->end_html;

$DBH->disconnect; 

#--------------------------------(end of main program)------

sub show_form {
  print $q->start_form(),  qq{
        Please enter your selection: <input type="text" name="user"> <p> }, 
        "When done please press ", 
        $q->submit(-value=>'Proceed'),
        $q->end_form(); 
}
#--------------------------------(this was our basic form)---

sub get_session_id {

  &expire_old_sessions(); 

  my ($id) = $q->path_info =~ m:^/([a-h0-9]{$ID_LENGTH}):o;

  return $id if $id and &check_id($id); 

  my $session_id = &generate_id; 

  die "Couldn't make a new session_id" unless $session_id; 

  print $q->redirect($q->script_name() . "/$session_id"); 

  exit(0);   

}
#--------------------------------(needed above)--------------

sub expire_old_sessions {
  $DBH->do(<<END);
DELETE FROM $DB_TABLE
  WHERE (unix_timestamp() - unix_timestamp(modified)) > $EXPIRE
END
}

#--------------------------------(also needed above)---------
sub generate_id {
  my $tries = 0; 
  my $id = &hash($SECRET . rand()); 
  while ($tries++ < $MAX_TRIES) {
    last if 
      $DBH->do("INSERT INTO $DB_TABLE (session_id) VALUES ('$id')"); 
    $id = &hash($SECRET . rand()); 
  }
  return undef if $tries >= $MAX_TRIES;
  return $id; 
}
sub hash {
  my $value = shift; 
  return substr(MD5->hexhash($value), 0, $ID_LENGTH);   
}

#--------------------------------(last one needed)-----------
sub check_id {
  my $id = shift; 
  return $id 
    if $DBH->do("SELECT 1 FROM $DB_TABLE WHERE session_id = '$id'") > 0; 
  return $id 
    if $DBH->do("INSERT INTO $DB_TABLE (session_id) VALUES ('$id')"); 
  return ''; 
}

#--------------------------------(retrieve acc)--------------
sub get_state {

  my $id = shift; 

  my $query = "SELECT * FROM $DB_TABLE WHERE session_id = '$id'"; 

  my $sth = $DBH->prepare($query) || die "Prepare: ", $DBH->errstr; 

  $sth->execute || die "Execute: ", $sth->errstr;

  my $state = $sth->fetchrow_hashref;

  $sth->finish; 

  return $state; 

}

#--------------------------------(calculate new acc)---------
sub calculate { 

  my $state = shift; #  first param

  my $userChoice = shift; # second param

  my $computerChoice = $state->{computerChoice};

  if (($userChoice + 1) % 3 == $computerChoice) {
     $state->{userScore} += 1; 
     $state->{message} = "You win."; 
  } else { # if you don't win, you lose
     $state->{computerScore}++;
     $state->{message} = "You lose."; 
  }

  $state->{computerChoice} = int(rand(3)); # we work with numbers 

  return $state; 
}

#--------------------------------(store new acc)-------------
sub save_state {

  my ($state, $id) = @_; 

  my $sth = $DBH->prepare(<<END) || die "Prepare: ", $DBH->errstr; 
UPDATE $DB_TABLE
  SET computerChoice = ?, computerScore = ?, userScore = ?, message = ?   
  WHERE session_id = '$id'
END

  $sth->execute(@{$state}{qw(computerChoice computerScore userScore message)}) 
  || die "Execute: ", $DBH->errstr; 

  $sth->finish; 
}

#--------------------------------(print current acc)---------
sub report {
  my ($state) = @_; 
  print  qq{
          Computer score: $state->{computerScore} <p> 
          User score: $state->{userScore} <p> 
          Message: $state->{message} <p> 
          The computer has chosen: $state->{computerChoice} <p> 
  }; # note that computer choice printed is just a number 
}
sub initialize {

  my $state = shift; 

  $state = {} unless $state; 

  $state->{computerScore} = 0; 
  $state->{userScore} = 0; 
  $state->{computerChoice} = int(rand(3));  
  $state->{message} = "Welcome."; 

  return $state; 

}