#!/usr/bin/env perl

use strict;
use warnings;

use lib '/afs/athena.mit.edu/org/p/perl-lib/modules';

use POSIX;
use Term::Cap;
use Term::ReadKey;
use Term::ReadLine;
use Data::Dumper;

my $filedir = "/afs/athena.mit.edu/activity/r/random-hall/soda-tab-db/schedule/";

my $term = Tgetent Term::Cap {};
my $r = new Term::ReadLine 'cli';

$|=1;

my ($day,$hr,$bday,$bhr,$twentyfour);

($day,$hr,$twentyfour)=(0,8,0);

my @schedule = ( [],[],[],[],[],[],[] );

my $stime;
{ my @time = localtime(time());
  $time[6]=7 if ($time[6]==0); # shift things to monday-base.
  $stime = time()+(8-$time[6])*86400 };

my @helptext =
("keys that do","     things:"," i move up"," j move left"," k move down",
 " l move right"," <space> toggle"," b start block"," b stop block"," s fill block"," c empty block",
 " ~ other stuff","","~submit","save hours","","~quit ~exit","~cancel","   all quit");
	     
sub gotoxy {
  my ($x,$y)=@_;

  print $term->Tgoto('cm',$x,$y);
}

sub clrcurs {
  gotoxy($day*8+3,$hr-8+2);
  printf ("[%s]",$schedule[$day]->[$hr-8]?'X':' ');
}

sub drwcurs {
  gotoxy($day*8+3,$hr-8+2);
  printf (">%s<",$schedule[$day]->[$hr-8]?'X':' ');
}

sub mpt {
  if (defined $bday) {
    # clear entire block
    my ($ly,$gy,$lx,$gx);
    ($lx,$gx)=($bday>$day)?($day,$bday):($bday,$day);
    ($ly,$gy)=($bhr>$hr)?($hr,$bhr):($bhr,$hr);
    foreach my $y ($ly .. $gy) {
      foreach my $x ($lx .. $gx) {
	gotoxy($x*8+2,$y-8+2);
	printf (" [%s] \n",$schedule[$x]->[$y-8]?'X':' ');
      }
    }
  }
  clrcurs();
}

sub draw {
  if (defined $bday) {
    # draw entire block
    my ($ly,$gy,$lx,$gx);
    ($lx,$gx)=($bday>$day)?($day,$bday):($bday,$day);
    ($ly,$gy)=($bhr>$hr)?($hr,$bhr):($bhr,$hr);
    foreach my $y ($ly .. $gy) {
      foreach my $x ($lx .. $gx) {
	gotoxy($x*8+2,$y-8+2);
	printf ("=[%s]=\n",$schedule[$x]->[$y-8]?'X':' ');
      }
    }
  }
  drwcurs();
}

sub up {
  mpt();
  $hr--;
  if ($hr<8) {
    if ($twentyfour) { $hr=29; }
    else { $hr=25; }
  }
  draw();
}

sub down {
  mpt();
  $hr++;
  if ($hr>=30 or (not $twentyfour and $hr>=26)) {
    $hr=8;
  }
  draw();
}

sub left {
  mpt();
  $day--;
  $day=6 if ($day<0);
  draw();
}

sub right {
  mpt();
  $day++;
  $day=0 if ($day>=7);
  draw();
}

sub toggle {
  mpt();
  $schedule[$day]->[$hr-8] = not $schedule[$day]->[$hr-8];
  draw();
}

sub beginblock {
  if (defined $bday) {
    mpt();
    undef $bday; undef $bhr;
  } else {
    $bday = $day; $bhr = $hr;
    draw();
  }
}

sub set {
  if (defined $bday) {
    my ($ly,$gy,$lx,$gx);
    ($lx,$gx)=($bday>$day)?($day,$bday):($bday,$day);
    ($ly,$gy)=($bhr>$hr)?($hr,$bhr):($bhr,$hr);
    foreach my $y ($ly .. $gy) {
      foreach my $x ($lx .. $gx) {
	$schedule[$x]->[$y-8]=1;
      }
    }
    mpt();
    undef $bday; undef $bhr;
  } else {
    $schedule[$day]->[$hr-8] = 1;
  }
}

sub clr {
  if (defined $bday) {
    my ($ly,$gy,$lx,$gx);
    ($lx,$gx)=($bday>$day)?($day,$bday):($bday,$day);
    ($ly,$gy)=($bhr>$hr)?($hr,$bhr):($bhr,$hr);
    foreach my $y ($ly .. $gy) {
      foreach my $x ($lx .. $gx) {
	$schedule[$x]->[$y-8]=0;
      }
    }
    mpt();
    undef $bday; undef $bhr;
  } else {
    $schedule[$day]->[$hr-8] = 0;
  }
}

sub cli {
  # set clear cancel submit visual show
  gotoxy(1,$twentyfour?24:20); print "\n";
  ReadMode 0;
  my $prompt = '~ ';
  my $incli = 1;
  while ( $incli ) {
    my $line = $r->readline($prompt);
    if ($line =~ /^\s*set /i) {
      $line =~ /set\s*([MTWRFSU]+)\s*(\d+)\s*-\s*(\d+)/i;
      my ($days,$firsthour,$lasthour)=($1,$2,$3);
      $lasthour--;
      if ($firsthour < 8 or $lasthour >= 30 or $lasthour < $firsthour) {
	print "Bad hour range ... only 8<hour<29 allowed\n";
      } else {
	if ($days =~ /m/i) { $schedule[0]->[$_-8]=1 for ($firsthour .. $lasthour); }
	if ($days =~ /t/i) { $schedule[1]->[$_-8]=1 for ($firsthour .. $lasthour); }
	if ($days =~ /w/i) { $schedule[2]->[$_-8]=1 for ($firsthour .. $lasthour); }
	if ($days =~ /r/i) { $schedule[3]->[$_-8]=1 for ($firsthour .. $lasthour); }
	if ($days =~ /f/i) { $schedule[4]->[$_-8]=1 for ($firsthour .. $lasthour); }
	if ($days =~ /s/i) { $schedule[5]->[$_-8]=1 for ($firsthour .. $lasthour); }
	if ($days =~ /u/i) { $schedule[6]->[$_-8]=1 for ($firsthour .. $lasthour); }
      }
    } elsif ($line =~ /\s*clear /i) {
      $line =~ /clear\s*([MTWRFSU]+)\s*(\d+)\s*-\s*(\d+)/i;
      my ($days,$firsthour,$lasthour)=($1,$2,$3);
      $lasthour--;
      if ($firsthour < 8 or $lasthour >= 30 or $lasthour < $firsthour) {
	print "Bad hour range ... only 8<hour<29 allowed\n";
      } else {
	if ($days =~ /m/i) { $schedule[0]->[$_-8]=0 for ($firsthour .. $lasthour); }
	if ($days =~ /t/i) { $schedule[1]->[$_-8]=0 for ($firsthour .. $lasthour); }
	if ($days =~ /w/i) { $schedule[2]->[$_-8]=0 for ($firsthour .. $lasthour); }
	if ($days =~ /r/i) { $schedule[3]->[$_-8]=0 for ($firsthour .. $lasthour); }
	if ($days =~ /f/i) { $schedule[4]->[$_-8]=0 for ($firsthour .. $lasthour); }
	if ($days =~ /s/i) { $schedule[5]->[$_-8]=0 for ($firsthour .. $lasthour); }
	if ($days =~ /u/i) { $schedule[6]->[$_-8]=0 for ($firsthour .. $lasthour); }
      }
    } elsif ($line =~ /^\s*(cancel|quit|exit)/i) {
      exit;
    } elsif ($line =~ /^\s*submit/i) {
      my @stime = localtime $stime;
      my $filename = sprintf '%s_%04d%02d%02d',scalar getpwuid $<,
	$stime[5]+1900,$stime[4],$stime[3];
      print "Would open $filedir$filename\n";
      for (0 .. 10) {
	open SCH,">$filedir$filename-try$_" or next;
	print SCH Data::Dumper->Dump([\@schedule],["*${filename}_schedule"]) or next;
	close SCH or next;
	print "Submission appears successful, on try $_\n";
	exit;
      }
      print "FAILED TO SUBMIT! Something is SERIOUSLY WRONG, or you already\n".
	"submitted 10 timesheets for this week!\n";
    } elsif ($line =~ /^\s*visual/i) {
      ReadMode 4;
      redrw();
      $incli = 0;
    } elsif ($line =~ /^\s*show/i) {
      redrw();
    } else {
      print "\nUnderstood commands: set clear cancel/exit/quit submit visual show\n".
	" set [MTWRFSU][xx-yy] -- sets hour range on specified days\n".
	" clear [MTWRFSU][xx-yy] -- like set, but clears\n".
	" cancel -- immediately quits the editor\n".
	" submit -- immediately submits your timesheet\n".
	" visual -- goes back to visual mode\n".
	" show -- draws the current schedule\n";
    }
  }
}

sub redrw {
  print $term->Tputs('cl',0); # clear screen

  my @btime = localtime($stime);
  my @etime = localtime($stime+6*86400);

  my $ts = sprintf "Week of %s - %s",
    strftime("%Y %B %d",@btime), strftime("%Y %B %d",@etime);
  print " "x((58-length($ts))/2),$ts," "x((59-length($ts))/2),"\n";
  for (0..6) {
    my @ztime = localtime($stime+$_*86400);
    print "  ".strftime("%a %d",@ztime);
  }
  print "\n";

  for my $y (8 .. 25) {
    printf("%2d",$y);
    print " [".($schedule[0]->[$y-8]?'X':' ')."] ";
    if ($y%12==11) { print "___"; } else { print "   "; }
    print " [".($schedule[1]->[$y-8]?'X':' ')."] ";
    if ($y%12==11) { print "___"; } else { print "   "; }
    print " [".($schedule[2]->[$y-8]?'X':' ')."] ";
    if ($y%12==11) { print "___"; } else { print "   "; }
    print " [".($schedule[3]->[$y-8]?'X':' ')."] ";
    if ($y%12==11) { print "___"; } else { print "   "; }
    print " [".($schedule[4]->[$y-8]?'X':' ')."] ";
    if ($y%12==11) { print "___"; } else { print "   "; }
    print " [".($schedule[5]->[$y-8]?'X':' ')."] ";
    if ($y%12==11) { print "___"; } else { print "   "; }
    print " [".($schedule[6]->[$y-8]?'X':' ')."] ";
    printf("%2d\n",$y);
  }

  for (1 .. scalar @helptext) {
    gotoxy(58,$_); print $helptext[$_-1];
  }

  if ($twentyfour) {
    for my $y (8 .. 25) {
      printf("%2d",$y);
      print " [".($schedule[0]->[$y-8]?'X':' ')."] ";
      print "   ";
      print " [".($schedule[1]->[$y-8]?'X':' ')."] ";
      print "   ";
      print " [".($schedule[2]->[$y-8]?'X':' ')."] ";
      print "   ";
      print " [".($schedule[3]->[$y-8]?'X':' ')."] ";
      print "   ";
      print " [".($schedule[4]->[$y-8]?'X':' ')."] ";
      print "   ";
      print " [".($schedule[5]->[$y-8]?'X':' ')."] ";
      print "   ";
      print " [".($schedule[6]->[$y-8]?'X':' ')."] ";
      printf("%2d\n",$y);
    }
  }
}

my ($ku,$kd,$kr,$kl) = ($term->Tputs('ku',0),$term->Tputs('kd',0),$term->Tputs('kr',0),$term->Tputs('kl',0));

my %keybindings =
  ('' => sub { redrw(); draw(); },
   'i' => \&up,
   'j' => \&left,
   'k' => \&down,
   'l' => \&right,
   ' ' => \&toggle,
   $ku => \&up,
   $kd => \&down,
   $kr => \&right,
   $kl => \&left,
   'b' => \&beginblock,
   's' => \&set,
   'c' => \&clr,
   '~' => \&cli);

my $running=1;

redrw();
draw();

print keys %keybindings;

ReadMode 4;
while ($running) {
  my $key = ReadKey(0);
  if (not defined $key) { die "Fatal error in Term::ReadKey" };
  while (defined (my $t = ReadKey(-1))) { $key.=$t; }
  if (defined $keybindings{$key}) {
    $keybindings{$key}->();
  } 
}

END {
ReadMode 0;
}
