#!/usr/bin/perl

# Copyright 1993 Frank Adelstein.  All Rights Reserved.
#
# Permission to use, copy, modify, and distribute this
# software is hereby granted without fee, provided that
# the copyright notice and permission notice are not removed.
#
# --FNA 8/29/93

# a voyage into Perl and termcaps.  Let's see what we can do.
# Initial termcap stuff shamelessly stolen from Tom Fine.

# Minor updates to support Term::Cap library instead of the old termcap.pl
# -- FNA 06/20/09

use Getopt::Std;
use Term::Cap;
require POSIX;

#deal with the options
#
getopts("f:bc") || die ("usage: $0 [-bc] [-f addressfile] \n
where -b enables batch_mode, and -c checks for birthdays and exits");

if (defined($opt_f)) {
  $abfile = $opt_f;
} else {
  $abfile = $ENV{'HOME'} . "/.addresses";
}
if (defined($opt_b)) { $SAVEONEXIT = 1; } 
else { $SAVEONEXIT = 0; 
}
if (defined($opt_c)) {
  # skip all the fun stuff, just read in the file
  # and see if we can find any birthdays.
  
  &check_for_birthdays();
  exit();
}


my $termios = new POSIX::Termios;
$termios->getattr;
my $ospeed = $termios->getospeed;

$terminal = Tgetent Term::Cap { TERM => undef, OSPEED => $ospeed };

# require regional scrolling capability and insert/delete
$terminal->Trequire(qw/cs al dl/);

$HAS_SCROLLING_REGIONS = 1;
$HAS_INSERT_DELETE     = 1;


$SIG{'INT'} = 'sig_int';
$SIG{'TSTP'} = 'sig_stop';
$SIG{'CONT'} = 'sig_cont';

# check out scrolling capability...if we have
# scrolling regions, use it, otherwise use
# insert and delete line, otherwise, you're screwed.

$HAS_SCROLLING_REGIONS = 0;
$HAS_INSERT_DELETE     = 0;

&cbreak;

&clearscreen;
&readdata;

$index    = 0;
$top      = 12;
$bottom   = $rows - 3;
$current  = $top;
$needsave = 0;
&redraw;

&mvcurs(0,$rows);
&cleartoeol;
print "read in $addindex messages";
&mvcurs(0,$top);

$c = getc;
while ($c ne "q") {
  if ($c eq "") {
    $c = getc;
    if ($c eq "[" || $c eq "O") {
      $c = getc;
      if ($c eq "A") {$c = "k"; }
      elsif ($c eq "B") {$c = "j"; }
      elsif ($c eq "C") {$c = "l"; }
      elsif ($c eq "D") {$c = "h"; }
    } 
  }
  if ($c eq "") { &redraw; }
  elsif ($c eq "k" || $c eq "") { &moveline(-1); }
  elsif ($c eq "j" || $c eq "") { &moveline(1); }
  elsif ($c eq "") { &bigmove($bottom-$top); }
  elsif ($c eq "") { &bigmove($top-$bottom); }
  elsif ($c eq "\n") { &longdataredraw; &mvcurs(0,$top); }
  elsif ($c eq "/")  { &dosearch; }
  elsif ($c eq "e")  { &doedit(1); &mvcurs(0,$top); }
  elsif ($c eq "d")  { &deleteentry; }
  elsif ($c eq "a")  { &addentry; }
  else { print ""; }
  $c = getc;
}

if ($needsave) { &writedata; }
&mvcurs(0,$rows);
print "\n";
&reset;
exit;

sub moveline {
  if ($_[0] > 0) {
    if ($_[0] + $index >= $addindex) { print ""; return;}
    else { 
      &scrollup;
      &redrawline ($index + 1 + $bottom - $top);
    }
  } else {
    if ($index + $_[0] < 0) { print ""; return;}
    else {
      &scrolldown;
      &redrawline ($index - 1);
    }   
  }
  $index += $_[0];
  &mvcurs(0,$top);
}

sub bigmove {
  if ($_[0] > 0) {
    if ($_[0] + $index >= $addindex) { print ""; return;}
  } else {
    if ($index + $_[0] < 0) { print ""; return;}
  }
  $index += $_[0];
  &shortdataredraw;
  &mvcurs(0,$top);
}

sub readdata {
  if (! -e $abfile) { 
    #file doesn't exist...yet
    return;
  }
  if (!open(ADDRESSES, $abfile)) {
    &showmess("Warning: can't open address file for read.");
    return;
  }
  $addindex = 0;
  while (<ADDRESSES>) {
    chop;
    $name[$addindex] = $_;
    $number1[$addindex] = <ADDRESSES>;
    $number2[$addindex] = <ADDRESSES>;
    $address1[$addindex] = <ADDRESSES>;
    $address2[$addindex] = <ADDRESSES>;
    $address3[$addindex] = <ADDRESSES>;
    $birthday[$addindex] = <ADDRESSES>;
    $comment1[$addindex] = <ADDRESSES>;
    $comment2[$addindex] = <ADDRESSES>;
    $comment3[$addindex] = <ADDRESSES>;
    $email[$addindex] = <ADDRESSES>;
    chop $number1[$addindex];
    chop $number2[$addindex];
    chop $address1[$addindex];
    chop $address2[$addindex];
    chop $address3[$addindex];
    chop $birthday[$addindex];
    chop $comment1[$addindex];
    chop $comment2[$addindex];
    chop $comment3[$addindex];
    chop $email[$addindex];
    $addindex++;
  }
  close (ADDRESSES);
}

sub writedata {
  if (!&yorn("Save file?", 1)) { return; }

  if (!open(ADDRESSES, ">$abfile")) {
    &showmess("Warning: can't open address file for write.");
    return;
  }
  for ($i = 0; $i < $addindex; $i++) {
    print ADDRESSES $name[$i], "\n";
    print ADDRESSES $number1[$i], "\n";
    print ADDRESSES $number2[$i], "\n"; 
    print ADDRESSES $address1[$i], "\n";
    print ADDRESSES $address2[$i], "\n";
    print ADDRESSES $address3[$i], "\n";
    print ADDRESSES $birthday[$i], "\n";
    print ADDRESSES $comment1[$i], "\n";
    print ADDRESSES $comment2[$i], "\n";
    print ADDRESSES $comment3[$i], "\n";
    print ADDRESSES $email[$i], "\n";
  }
  close ADDRESSES;
  $needsave = 0;
}

sub longdataredraw {
  # long listing of that entry
  &longdatafield(0,$name[$index]);
  &longdatafield(1,$number1[$index]);
  &longdatafield(2,$number2[$index]);
  &longdatafield(3,$address1[$index]);
  &longdatafield(4,$address2[$index]);
  &longdatafield(5,$address3[$index]);
  &longdatafield(6,$birthday[$index]);
  &longdatafield(7,$email[$index]);
  &longdatafield(8,$comment1[$index]);
  &longdatafield(9,$comment2[$index]);
  &longdatafield(10,$comment3[$index]);
}

sub longdatafield {
  &mvcurs(18,$_[0]);
  &cleartoeol;
  print $_[1];
}

sub shortdataredraw {
  # short listing...start at index and go till end of page
  $linesleft = $bottom - $top + 1;
  $i = 0;
  while ($linesleft && $i + $index < $addindex) {
    &mvcurs(0,$i+$top);
    &cleartoeol;
    &redrawline ($i + $index);
    $linesleft--; $i++;
  }
  while ($linesleft) { 
    &mvcurs(0, $i+$top);
    &cleartoeol;
    $linesleft--; $i++;
  }
  &mvcurs(0,$top);
}

sub redrawline {
  if ($_[0] < $addindex) { 
    printf ("%-45s%-15s", $name[$_[0]], $number1[$_[0]]);
  }
}

sub redraw {
  &clearscreen;
  &mvcurs(0,$top-1);
  &underlineon;
  print "         ---------thhhhhhhhhhhhhppppppppppt------------                        ";
  &underlineoff;
  &mvcurs(0,$bottom+1);
  &standouton;
  print "         ---------thhhhhhhhhhhhhppppppppppt------------                        ";
  &standoutoff;
  &drawtemplate;
  &longdataredraw;
  &shortdataredraw;
  &mvcurs(0,$top);
}

sub dosearch {
  &mvcurs(0, $rows);
  &cleartoeol;
  print "/";
  $tempstring = "";
  $c = getc;
  while (1) {
    if ($c eq "" || $c eq "") {
      print " ";
      if (length($tempstring)) {
        chop $tempstring;
      } else {
        &mvcurs(0,$top);
        return;
      }
    } elsif ($c eq "\n") {
      last;
    } else {
      print $c;
      $tempstring .= $c;
    }
    $c = getc;
  }
  # use the old search string if only <return> was hit
  if ($tempstring ne "") { $searchstring = $tempstring; }      

  # go ahead and do the searches...
  # ...first on names then on birthdays

  $found = 0;
  &searchmatch($index + 1, $addindex, *name);
  if (! $found) {
    &searchmatch(0, $index, *name);
  }
  if (! $found) {
    &searchmatch($index+1, $addindex, *birthday);
  }
  if (! $found) {
    &searchmatch(0, $index, *birthday);
  }
  if ($found) {
    if ($index != $i) {
      $index = $i;
      &longdataredraw;
      &shortdataredraw;
      &mvcurs(0, $top);
    }
  } else {
    &mvcurs(0,$rows);
    &cleartoeol;
    &standouton;
    print "not found";
    &standoutoff;
  } 
}

sub searchmatch {

  local(*searchpattern) = $_[2];
  for ($i = $_[0]; $i < $_[1]; $i++) {
    # we're only doing searches on names initially
    if ($searchpattern[$i]=~ /$searchstring/i) {
      $found = 1;
      last;
    }
  } 
}

sub check_for_birthdays {

  #read in the data file
  &readdata;

  # check today, tomorrow, the day after, and a week from today
  foreach $current (0, 1, 2, 7) {

    # convert it from 'days until event' to 'seconds until event'
    $newcurrent = time+(60*60*24*$current);

    #convert to an ascii representation (it handles months, leap years, etc.)
    local ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = 
	  localtime($newcurrent);
    local (@month);
    @month = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", 
       		"Aug", "Sep", "Oct", "Nov", "Dec");

#  #do a search for birthdays today and tomorrow and the day after tomorrow
#  foreach $searchstring ("$month[$mon].*$mday", 
#			 "$month[$mon].*".($mday+1),
#			 "$month[$mon].*".($mday+2)) {

    $searchstring = "$month[$mon].*$mday";
    $i = -1;
    $found = 1;
    while ($found == 1) {
      $found = 0;
      &searchmatch($i+1, $addindex, *birthday);
      if ($found) {
        print $name[$i], " has a birthday $birthday[$i]!!!\n";
      }
    }
  }
}

sub doedit {
  $modified = 0;
  $needshortredraw = 0;
  &longdataredraw;
  #edit name
  $tn = &editfield(0,$name[$index]);

  #numbers
  $tn1 = &editfield(1,$number1[$index]);
  if ($modified) { $needshortredraw = 1; }
  $tn2 = &editfield(2,$number2[$index]);

  #addresses
  $ta1 = &editfield(3,$address1[$index]);
  $ta2 = &editfield(4,$address2[$index]);
  $ta3 = &editfield(5,$address3[$index]);

  #birthday
  $tb  = &editfield(6,$birthday[$index]);

  #email
  $te = &editfield(7,$email[$index]);

  #comments
  $tc1 = &editfield(8,$comment1[$index]);
  $tc2 = &editfield(9,$comment2[$index]);
  $tc3 = &editfield(10,$comment3[$index]);

  if ($modified) { 
    $needsave = 1;
    if (! $_[0] || &yorn("Entry modified -- ok?", 1)) {
      $name[$index] = $tn;
      $number1[$index] = $tn1;
      $number2[$index] = $tn2;
      $address1[$index] = $ta1;
      $address2[$index] = $ta2;
      $address3[$index] = $ta3;
      $birthday[$index] = $tb;
      $comment1[$index] = $tc1;
      $comment2[$index] = $tc2;
      $comment3[$index] = $tc3;
      $email[$index] = $te;
      if ($_[0] && ! $SAVEONEXIT) { &writedata; }
      if ($needshortredraw && $_[0]) { &shortdataredraw; }
    } else { &longdataredraw; }
  }
}

sub editfield {
  &mvcurs(18,$_[0]);
  &cleartoeol;
  &standouton;
  print $_[1];
  &mvcurs(18,$_[0]);
  &getstring;
  if ($tempstring ne "" && $tempstring ne $_[1]) { 
    $modified = 1;
    &cleartoeol;
  } else {
    $tempstring = $_[1];
  }
  &mvcurs(18,$_[0]);
  &standoutoff;
  print $tempstring;
  $tempstring;
}

sub addentry {
  $saveindex = $index;
  $index = $addindex + 1;
  &doedit(0);
  &mvcurs(0,$rows);
  &cleartoeol;
  print "Add entry (b)efore or (a)fter current entry (or (q)uit) [a/b/q]?";
  $c = getc;
  while ($c ne "b" && $c ne "a" && $c ne "q") {
    &mvcurs(0,$rows);
    &cleartoeol;
    print "Enter b for before, a for after or q to quit";
    $c = getc;
  }  
  if ($c eq "b") { $startmove = $saveindex; }
  elsif ($c eq "a") { $startmove = $saveindex + 1; }
  else { 
    &clearentry($index); 
    $index = $saveindex;
    &longdataredraw;
    &mvcurs(0,$top);
    return;
  }
  @name[$startmove+1..$addindex] = @name[$startmove..$addindex-1];
  @number1[$startmove+1..$addindex] = @number1[$startmove..$addindex-1];
  @number2[$startmove+1..$addindex] = @number2[$startmove..$addindex-1];
  @address1[$startmove+1..$addindex] = @address1[$startmove..$addindex-1];
  @address2[$startmove+1..$addindex] = @address2[$startmove..$addindex-1];
  @address3[$startmove+1..$addindex] = @address3[$startmove..$addindex-1];
  @birthday[$startmove+1..$addindex] = @birthday[$startmove..$addindex-1];
  @comment1[$startmove+1..$addindex] = @comment1[$startmove..$addindex-1];
  @comment2[$startmove+1..$addindex] = @comment2[$startmove..$addindex-1];
  @comment3[$startmove+1..$addindex] = @comment3[$startmove..$addindex-1];
  @email[$startmove+1..$addindex] = @email[$startmove..$addindex-1];
  $name[$startmove] = $name[$index];
  $number1[$startmove] = $number1[$index];
  $number2[$startmove] = $number2[$index];
  $address1[$startmove] = $address1[$index];
  $address2[$startmove] = $address2[$index];
  $address3[$startmove] = $address3[$index];
  $birthday[$startmove] = $birthday[$index];
  $comment1[$startmove] = $comment1[$index];
  $comment2[$startmove] = $comment2[$index];
  $comment3[$startmove] = $comment3[$index];
  $email[$startmove]    = $email[$index];

  &clearentry($index);
  $index = $startmove;
  $addindex++;
  $needsave = 1;
  if (! $SAVEONEXIT) { &writedata; }
  &longdataredraw;
  &shortdataredraw;
}


sub deleteentry {
  if (&yorn("Confirm: Delete entry for $name[$index]?", 0)) {
    if ($index != $addindex-1) {
      @name[$index..$addindex-2] = @name[$index+1..$addindex-1];
      @number1[$index..$addindex-2] = @number1[$index+1..$addindex-1];
      @number2[$index..$addindex-2] = @number2[$index+1..$addindex-1];
      @address1[$index..$addindex-2] = @address1[$index+1..$addindex-1];
      @address2[$index..$addindex-2] = @address2[$index+1..$addindex-1];
      @address3[$index..$addindex-2] = @address3[$index+1..$addindex-1];
      @birthday[$index..$addindex-2] = @birthday[$index+1..$addindex-1];
      @comment1[$index..$addindex-2] = @comment1[$index+1..$addindex-1];
      @comment2[$index..$addindex-2] = @comment2[$index+1..$addindex-1];
      @comment3[$index..$addindex-2] = @comment3[$index+1..$addindex-1];
      @email[$index..$addindex-2] = @email[$index+1..$addindex-1];
    } else { 
      $index--; 
    }
    &clearentry($addindex);
    $addindex--;
    $needsave = 1;
    if (! $SAVEONEXIT) { &writedata; }
    &longdataredraw;
    &shortdataredraw;
  }
  &mvcurs(0,$top);
}

sub clearentry {
    $name[$_[0]] = "";
    $number1[$_[0]] = "";
    $number2[$_[0]] = "";
    $address1[$_[0]] = "";
    $address2[$_[0]] = "";
    $address3[$_[0]] = "";
    $birthday[$_[0]] = "";
    $comment1[$_[0]] = "";
    $comment2[$_[0]] = "";
    $comment3[$_[0]] = "";
    $email[$_[0]] = "";
}

sub drawtemplate {

  &titlefield(0,"Name:");
  &titlefield(1,"Primary Number:");
  &titlefield(2,"Secondary Number:");
  &titlefield(3,"Address:");
  &titlefield(4,"");
  &titlefield(5,"");
  &titlefield(6,"Birthday:");
  &titlefield(7,"Email:");
  &titlefield(8,"Comments:");

  &mvcurs(0,9);
  &cleartoeol;
}

sub titlefield {
  &mvcurs(0,$_[0]);
  &cleartoeol;
  &mvcurs(17 - length($_[1]), $_[0]);
  &underlineon;
  print $_[1];
  &underlineoff;
}

## These should be moved into a full screen library
sub cbreak {
  open(ROW,"stty size |");
  while (<ROW>) {
    chop;
    s/ .*//;
    $rows=$_;
    if ($rows == 0) { $rows = $TC{'li'}; }
    last;
  }
  close(ROW);

  open(SV,"stty -g |");
  $savetty=<SV>;
  close(SV);
  chop $savetty;

  $erase = $savetty;
  $erase =~ s/([^:]*:){6}([^:]*).*/$2/;
  $erase = hex ($erase);

  $|=1;
  system("stty -icanon min 1 -echo");
}

sub reset {
  system("stty $savetty");
}

sub sig_int {
  &reset;
  &clearscreen;
  exit(0);
}

sub sig_stop {
  &reset;
  &clearscreen;
  kill "STOP",$$;
}

sub sig_cont {
  &cbreak;
  &redraw;
}


sub showmess {
  &mvcurs(0,$rows);
  &cleartoeol;
  print $_[0], " ('any' key to continue)";
  getc;
  &mvcurs(0,$rows);
  &cleartoeol;
  &mvcurs($curscol,$cursrow);
}

sub yorn {
  $ret=0;
  &mvcurs(0,$rows);
  &cleartoeol;
  print $_[0], " (y or n)";
  $c = getc;
  while ($_[1] && ($c ne 'y' && $c ne 'n')) {
    &mvcurs(0,$rows);
    &cleartoeol;
    print "", $_[0], " (y or n)";
    $c = getc;
  }
  &mvcurs(0,$rows);
  &cleartoeol;
  if ($c eq "y") { $ret=1; }
  $ret;
}

sub getstring {
  $tempstring = "";
  $c = getc;
  while (1) {
    if ($c eq "" || $c eq "") {
      if (length($tempstring)) {
        print " ";
        chop $tempstring;
      }
    } elsif ($c eq "\n") {
      last;
    } else {
      print $c;
      $tempstring .= $c;
    }
    $c = getc;
  }
}

sub standouton {
  $terminal->Tputs('so',1,*STDOUT);
}

sub standoutoff {
  $terminal->Tputs('se',1,*STDOUT);
}

sub underlineon {
  $terminal->Tputs('us',1,*STDOUT);
}

sub underlineoff {
  $terminal->Tputs('ue',1,*STDOUT);
}

sub cleartoeol {
  $terminal->Tputs('ce',1,*STDOUT);
}

sub clearscreen {
  $terminal->Tputs('cl',1,*STDOUT);
}

sub scrolldown {
  if ($HAS_SCROLLING_REGIONS) {
    &setregion($top, $bottom);
    &mvcurs(0,$top);  
    $terminal->Tputs('sr', 1, *STDOUT);
    &setregion(-1,-1);
    &mvcurs(0,$top);
  } else {
    &mvcurs(0,$bottom);
    $terminal->Tputs('dl', 1, *STDOUT);
    &mvcurs(0,$top);
    $terminal->Tputs('al', 1, *STDOUT);
    &mvcurs(0,$top);
  }
}

sub scrollup {
  if ($HAS_SCROLLING_REGIONS) {
    &setregion($top, $bottom);
    &mvcurs(0, $bottom);
    $terminal->Tputs('do', 1, *STDOUT);
    &setregion(-1,-1);
    &mvcurs(0,$bottom);
  } else {
    &mvcurs(0,$top);
    $terminal->Tputs('dl',1,*STDOUT);
    &mvcurs(0,$bottom);
    $terminal->Tputs('al',1,*STDOUT);
    &mvcurs(0,$bottom);
  }
}

sub mvcurs {
  $terminal->Tgoto('cm',$_[0],$_[1],*STDOUT);
}

sub setregion {
  $terminal->Tgoto('cs',$_[1],$_[0], *STDOUT);
}

