#!/usr/bin/perl -w

######### setup 

$dokudatadir="/var/www/apps2/data/";
$dokudatadir="/tmp/apps2d/";

######### prepare 

# collect all used tags
@tags = `grep -ri "{tag>" $dokudatadir/pages |grep -vi "\@tag\@" | sed 's/^.*{{tag>\\([^}]*\\)}}.*\$/\\1/g' | sed 's/[ \\t]/\\n/g' | tr "[:upper:]" "[:lower:]" | sort | uniq`;

######### subroutines 

sub add_tag ($;$) {
  my ($file, $tag) = @_;
  $new = "$file.tmp.$$";
  open(OLD, "< $file")        or die "can't open $old: $!";
  open(NEW, "> $new")         or die "can't open $new: $!";
   while (<OLD>) {
   if (/{{tag>.*}}/i) {
     s/>$tag}/>}/i; 
     s/>$tag />/i; 
     s/ $tag}}/}}/i; 
     s/ $tag / /i;
     s/}}/ $tag}}/; 
    }
   (print NEW $_)             or die "can't write to $new: $!";
  }
  close(OLD)                  or die "can't close $old: $!";
  close(NEW)                  or die "can't close $new: $!";
  rename($new, $file)         or die "can't rename $new to $old: $!";
}

sub remove_tag ($;$) {
  my ($file, $tag) = @_;
  $new = "$file.tmp.$$";
  open(OLD, "< $file")        or die "can't open $old: $!";
  open(NEW, "> $new")         or die "can't open $new: $!";
   while (<OLD>) {
   if (/{{tag>.*}}/i) {
     s/>$tag}/>}/i; 
     s/>$tag />/i; 
     s/ $tag}}/}}/i; 
     s/ $tag / /i;
    }
   (print NEW $_)             or die "can't write to $new: $!";
  }
  close(OLD)                  or die "can't close $old: $!";
  close(NEW)                  or die "can't close $new: $!";
  rename($new, $file)         or die "can't rename $new to $old: $!";
}

######### find alike `grep -rli `
sub greprli ($;$;$) {
  use File::Find;

  my $base_dir= shift;
  my $file_pattern  = shift;
  my $search_pattern = shift;
  my @results;

  sub d {
    my $file = $File::Find::name;
    return unless -f $file;
    return unless $file =~ /$file_pattern/;
    open F, $file or print "couldn't open $file\n" && return;
    while (<F>) {
      next unless m/($search_pattern)/i ;
      push @results, $file;
      last;
    }
    close F;
  }

  find(\&d, $base_dir);
  return @results;
}

######### jinxes
my @itempos;

sub clear_item {
  $#itempos = -1;	
}

sub add_item  {
  my %hash = ( ys => shift, xs => shift, xl => shift, key => shift);
  push @itempos , {%hash};
}

sub get_item_at {
  my $y = shift;
  my $x = shift;
  foreach(@itempos) {
    if (( $_->{ys} == $y ) and ($_->{xs} <= $x) and (($_->{xl} < 0) or ($x < ($_->{xs}+$_->{xl}))))  {
      return $_->{key};
    }
  }
  return 0;
}

######### curses 
use Curses;
sub initscreen { 

  initscr();
  start_color();
  init_pair($myblue, COLOR_CYAN,COLOR_BLACK);
  init_pair($myred, COLOR_MAGENTA,COLOR_BLACK);
  init_pair($myhi, COLOR_RED,COLOR_BLACK);
  init_pair($myin, COLOR_GREEN,COLOR_BLACK);

# cbreak();
  noecho();
  raw();
# nonl();
  my $old_mouse_events = 0;
  mousemask(BUTTON1_RELEASED()| BUTTON1_DOUBLE_CLICKED() | BUTTON1_PRESSED() | BUTTON1_CLICKED() , $old_mouse_events);
  clear();
  refresh();
}

sub destroyscreen {
  endwin ();
}

sub handle_mouse_event()
{
  my $MEVENT = 0;
  getmouse($MEVENT);
  my ($id, $x, $y, $z, $bstate) = unpack("sx2i3l", $MEVENT);
  my %MEVENT = ( id=> $id, x=> $x, y=> $y, bstate => $bstate,);
  return %MEVENT;
}

######### curses  UI
sub readcurses($;$;$;) {
  my $win = $_[0];
  my $yo = $_[1];
  my $plsfn = $_[2];
  
  my ($scr_row, $scr_col);
  getmaxyx($win,$scr_row,$scr_col);	
  $win->keypad(1);
  
  my $rv='';
  my $status=0;
  
  $win->addstr($yo,0,"----------------------------------------------------------------");
  $yo++;
  if (-f $plsfn) {
    $win->attron(COLOR_PAIR($myhi));
    foreach (`grep -vi meta "$plsfn"`) {
      $win->addstr($yo,0,$_);
      $yo++;
    }
    $win->attron(COLOR_PAIR($myhi));
  } else {
    $win->attron(COLOR_PAIR($myred));
    $win->addstr($yo,3," - EMPTY FILE -");
    $yo++;
    $win->attron(COLOR_PAIR($myred));
  }

  # menu to bottom of screen 
  $win->attron(COLOR_PAIR($myin));
  $win->addstr($scr_row-1,1,"<back>"); 
  add_item ($scr_row-1,1,6,"prev");
  $win->addstr($scr_row-1,8,"<next>"); 
  add_item ($scr_row-1,8,6,"done");
  $win->addstr($scr_row-1,15,"<untag>"); 
  add_item ($scr_row-1,15,7,"untag");
  $win->addstr($scr_row-1,23,"<tag>"); 
  add_item ($scr_row-1,23,5,"tag");
  $win->addstr($scr_row-1,$scr_col-8,"<exit>"); 
  add_item ($scr_row-1,$scr_col-8,$scr_col-2,"exit");
  $win->attroff(COLOR_PAIR($myin));
  $win->refresh();

  # wait for user input  
  while ( $status eq 0  and ((my $ch = $win->getch()) ne '-1')) {

    $ch = KEY_ENTER() if ( $ch eq "\n" or $ch eq "\cM");
    $ch = KEY_BACKSPACE() if ( ord($ch) == 127 or $ch eq "\cH");
    
    if ($ch eq KEY_ENTER()) { $status = 1; $rv = "done"; }
    elsif ($ch eq KEY_BACKSPACE()) { $status = 1; $rv = "exit";}
    elsif ($ch eq KEY_LEFT()) { $status = 1; $rv = "prev";}
    elsif ($ch eq KEY_RIGHT()) { $status = 1; $rv = "done";}
    elsif ($ch eq ' ') { $status = 1; $rv = "untag";}
    elsif ($ch eq 'a') { $status = 1; $rv = "tag";}
    elsif ($ch eq KEY_NPAGE()) { $status = 1; $rv = "npage";}
    elsif ($ch eq KEY_PPAGE()) { $status = 1; $rv = "ppage";}
    elsif($ch eq KEY_MOUSE()) {
      my %mevent = handle_mouse_event();
      if ( $mevent{bstate} & BUTTON1_CLICKED()) {
      	$rv=get_item_at($mevent{y}, $mevent{x});
      	if ($rv) {
      	  $status=1;
      	}
      } 
    }
    $win->refresh();
  }
  if ($status eq 1) {
    return $rv;
  }
  return ('');
}

######### main 
use Data::Dumper;
#print Dumper(@tags);
#print Dumper( greprli($dokudatadir,".txt\$", "{{tag>.*OPENAL") );
#remove_tag("/tmp/xjadeo.txt", "All_Things_JACK");
#add_tag("/tmp/xjadeo.txt", "Kidding");
#add_tag("/tmp/xjadeo.txt", "Kidding");

foreach (@tags) {
  chomp;
  print $_." ";
}

eval { $ncurses_mouse    = (Curses->can('NCURSES_MOUSE_VERSION') and (NCURSES_MOUSE_VERSION() >= 1 ) ) };
die "Mouse not supported!" unless ($ncurses_mouse );

$myblue = 1;
$myred = 2;
$myhi = 3;
$myin = 4;

initscreen();
my $win = new Curses;

my $fixtag =  $ARGV[0] || "OPENAL"; # TODO look up in @tags 
my $current = 0;
my @files = greprli($dokudatadir,".txt\$", "{{tag>.*".$fixtag);
while (1) {
  $yo = 0;
  clear_item(); 
  $win->clear();
  if ($files[$current] =~ m/categories\// ) {
    $win->attron(COLOR_PAIR($myblue));
  }
  $win->addstr($yo,1,"$current / $#files: ".$files[$current]);
  $yo++;
  if ($files[$current] =~ m/categories\// ) {
    $win->attroff(COLOR_PAIR($myblue));
  }

  $a = readcurses($win,$yo,$files[$current]);
  chomp($a);
  if ($a=~m/^exit$/i )     {last;}
  elsif ($a=~m/^done$/i )  {$current++;}
  elsif ($a=~m/^next$/i )  {$current++;}
  elsif ($a=~m/^prev$/i )  {$current--;}
  elsif ($a=~m/^npage$/i ) {$current+=10;}
  elsif ($a=~m/^ppage$/i ) {$current-=10;}
  elsif ($a=~m/^tag$/i )   {add_tag($files[$current], $fixtag);}
  elsif ($a=~m/^untag$/i ) {remove_tag($files[$current], $fixtag);}
  else { print "unknown command: $a\n"; last; }

  if ($current < 0 ) { $current =0; }
  if ($current > $#files ) { $current = $#files; }
}

destroyscreen();
