
package App::FQStat::Drawing;
# App::FQStat is (c) 2007-2008 Steffen Mueller
#
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.

use strict;
use warnings;
use Time::HiRes qw/sleep time/;
use Term::ANSIScreen qw/:color :constants :screen :cursor :keyboard/;
use App::FQStat::Debug;

use base 'Exporter';
our %EXPORT_TAGS = (
  'all' => [qw(
    printline
    update_display
  )],
);
our @EXPORT_OK = @{$EXPORT_TAGS{'all'}};

# print line and buffer with space to line length
sub printline {
  warnenter if ::DEBUG > 2;
  my $line = shift;
  my $offset = shift || 0;
  $line .= ' ' x ($::Termsize[0]-length($line)-$offset);
  print $line, "\n";
}


# draws the first, title line
sub draw_title_line {
  warnenter if ::DEBUG > 1;
  locate(1,1);
  my $line;

  if ($::MenuMode) {
    print $::MenuColor;
    $line = App::FQStat::Menu::get_menu_title_line();
  }
  else {
    lock($::RecordsReversed);
    lock($::Interval);
    lock($::User);

    # reversed list indicator
    my $status = color("blue on white").'[';
    if ($::RecordsReversed) { $status .= 'R' }
    if ($status =~ /\[$/) { $status = '' }
    else { $status .= ']'.RESET.' ' }

    my $progress = ::PROGRESS_INDICATORS()->[$::ProgressIndicator];
    $progress = ' ' if not defined $progress;
    $line = sprintf(
      'fqstat v%.1f %s %s%sJobs:%i Upd:%.1fs [h]elp [F10] Menu (c) S. Mueller, Nodes:%i',
      $App::FQStat::VERSION||0,
      $progress,
      $status||'',
      ($::User ? "User:$::User " : ""),
      scalar(@{$::Records})||0,
      $::Interval||0,
      $::NoActiveNodes||0,
    );
  }
  ::GetTermSize();
  $line = substr($line, 0, $::Termsize[0]) if $::Termsize[0] < length($line);
  printline($line);
  print RESET if $::MenuMode;
}

# draws the column header line
sub draw_header_line {
  warnenter if ::DEBUG > 1;
  my @highlight = @_;
  my %highlight = map {($_ => 1)} @highlight;

  # Header Line
  locate(2,1);
  my ($line, $width);

  my $high = 'bold white on red';
  my $norm = 'bold white on black';

  color($norm);
  if (exists $highlight{1}) { $line = color($high).'Stat'.color($norm) }
  else { $line = 'Stat' }

  $width = 4;
  my $colno = 1;
  foreach my $col (@::Columns) {
    my $c = $::Columns{$col};
    $colno++;
    $line .= (' 'x($::Termsize[0]-$width-1)).'>', last if $width+2+$c->{width} > $::Termsize[0];
    $width += 1+$c->{width};
    $line .= ' ';
    if (exists $highlight{$colno}) { $line .= color($high).sprintf("\%-".$c->{width}."s", $c->{name}).color($norm) }
    else                           { $line .= sprintf("\%-".$c->{width}."s", $c->{name}) }
  }
  printline($line);
  print RESET;
}


my $last_update;
sub update_display {
  warnenter if ::DEBUG;
  my $force = shift;
  $last_update ||= time();
  my $time = time();

  if ($force or $time-$last_update > $::Interval) {
    $last_update = $time;
    $::ProgressIndicator++;
    $::ProgressIndicator = 0
      if $::ProgressIndicator >= scalar(@{::PROGRESS_INDICATORS()});
    App::FQStat::Scanner::run_qstat($force);
  }

  cls();
  ::GetTermSize();

  draw_title_line(); # first line
  draw_header_line(); # second line
  draw_job_display(); # list of jobs
  if ($::MenuMode) {
    App::FQStat::Menu::draw_menu();
  }

  locate(1,1);
}

# Draws the list of jobs
# Optional argument: hash reference of items to highlight
sub draw_job_display {
  warnenter if ::DEBUG;
  my $highlight = shift || {};
  my $mark      = shift || {};

  my $jobs = $::Records;
  check_display_offset();
  lock($::DisplayOffset);

  my $drawUpperScrollbar = need_upper_scrollbar();
  draw_upper_scrollbar() if $drawUpperScrollbar;

  my $drawLowerScrollbar = need_lower_scrollbar();

  
  my $max_job_index = space_for_jobs() - $drawUpperScrollbar - $drawLowerScrollbar;

  locate(3+$drawUpperScrollbar,1);
  my $last = (@$jobs-$::DisplayOffset <= $max_job_index ? @$jobs-1 : $::DisplayOffset + $max_job_index);

  # Calculate "scroll bar" marker
  # XXX this is only necessary if there is enough space.
  my $marker_start = 0;
  my $marker_end   = 1e99;
  if (@$jobs) {
    my $jobs_per_display_line = int(@$jobs / $max_job_index)||1;
    my $display_lines_before_marker = int($::DisplayOffset / $jobs_per_display_line);
    my $display_lines_of_marker     = int(($last-$::DisplayOffset) / $jobs_per_display_line);
    my $display_lines_after_marker  = (@$jobs-$last) / $jobs_per_display_line;
    $marker_start = int $display_lines_before_marker;
    $marker_end   = int($display_lines_before_marker+$display_lines_of_marker);
    $marker_end += 1 if $marker_start == $marker_end;
  }

  my $no = 0;
  foreach my $jobIndex  ($::DisplayOffset .. $last) {
    clline();
    my $job = $jobs->[$jobIndex];
    last if $no >= $max_job_index;

    # band-aid fix for uninit... bug
    if (not defined $job) {
      next;
    }

    $no++;

    my $highlightcolor = $highlight->{$jobIndex};

    # highlight selected user name
    $highlightcolor = $::HighlightUserColor
      if not defined $highlightcolor
         and defined $::HighlightUser
         and $job->[::F_user] =~ $::HighlightUser;

    # Print STATUS
    my $status = $job->[::F_status];
    if ($status =~ /^r|t$/)         { color("black on green") }
    elsif ($status =~ /E/)          { color("black on red") }
    elsif ($status =~ /h(?:qw|r)/)  { color("black on yellow") }
    elsif ($status =~ /w/)          { color("blue on white") }
    else                            { color("black on yellow") }

    printf('%-4s', $status);
    print RESET;

    print $highlightcolor if defined $highlightcolor;

    # print marking if applicable
    if (exists($mark->{$jobIndex})) {
      my $markstring = $mark->{$jobIndex};
      $markstring = "*" if not defined $markstring;
      printf("%1s", $markstring);
    }
    else { print " " }
    
    my $width = 4;
    my $first = 1;
    my $too_short = 0;
    foreach my $col (@::Columns) {
      my $c = $::Columns{$col};
      $too_short = 1, last if $width+3+$c->{width} > $::Termsize[0];
      $width += 1+$c->{width};
      print ' ' unless $first;
      $first = 0;
      printf( $c->{format}, $job->[ $c->{index} ] );
    }

    # padding for "position bar" of scroll bar
    print ' 'x($::Termsize[0]-$width-1);

    if ($too_short) {
      # not enough space
      print '>';
    }
    else {
      # draw "position bar" if there is enough space
      my $marker;
      if ($no >= $marker_start and $no <= $marker_end) {
        $marker = colored(" ", "black on white");
      }
      else {
        $marker = colored(" ", "white on black");
      }
      print $marker;
    }
    
    print RESET;


    print "\n";
  }

  draw_lower_scrollbar() if $drawLowerScrollbar;
  locate(1,1);
}

sub draw_upper_scrollbar {
  warnenter if ::DEBUG > 1;
  locate(3,1);
  printline("^" x ($::Termsize[0]-2));
}

sub draw_lower_scrollbar {
  warnenter if ::DEBUG > 1;
  locate($::Termsize[1]-1, 1);
  printline("v" x ($::Termsize[0]-2));
}

sub need_upper_scrollbar {
  warnenter if ::DEBUG > 2;
  return 1 if $::DisplayOffset;
  return 0;
}

sub need_lower_scrollbar {
  warnenter if ::DEBUG > 2;
  my $jobs = $::Records;
  my $drawLowerScrollbar = (@$jobs-$::DisplayOffset > space_for_jobs()-need_upper_scrollbar() ? 1 : 0);
  return $drawLowerScrollbar;
}

sub space_for_jobs {
  warnenter if ::DEBUG > 1;
  my $space = $::Termsize[1]-3;
  return $space;  
}

sub show_warning {
  warnenter if ::DEBUG > 1;
  my $text = shift;
  # Header Line
  locate(2,1);
  my $color = color('bold red on black');
  printline($color.$text);
  print RESET;
  sleep 2;
  return;
}

sub check_display_offset {
  lock($::DisplayOffset);
  return if not $::DisplayOffset;
  if (@$::Records <= $::DisplayOffset) {
    $::DisplayOffset = $#{$::Records};
  }
  $::DisplayOffset = 0 if $::DisplayOffset < 0;
}

1;


