package Hexed::Menu;

use strict;
use warnings;
use include;

use constant max_pady => 1024;
use constant max_padx => 1024;

use Cursed::Window;
our @ISA = ("Cursed::Window");

use Curses;

# Create and return a new menu window.
sub spawn {
  my $self = shift;

  $self->{KeyHandler} = delete $self->{Opts}{KeyHandler} // sub {};
  $self->{Queue} = delete $self->{Opts}{Queue};
  $self->{ShiftKeys} = delete $self->{Opts}{ShiftKeys} // croak "Enable shifting keys?";
  $self->{DeleteKey} = delete $self->{Opts}{DeleteKey} // croak "Enable delete key?!";

  $self->ui->install_keymap($self, \&keymap);

  $self->{Queue}->hook(\&queue_hook, Menu => $self);

  # Process any entries already on the queue.
  $self->format($_) foreach 0 .. $#{ $self->{Queue}{Queue} };

  $self->{Pad} = newpad(max_pady, max_padx);
  $self->{Offset} = 0;
}

# Overloaded... Sort of. We're the method that actually prints to the screen. Through pads.
sub windraw {
  my $self = shift;

  # TODO: should we go somewhere else to be more efficent?
  foreach my $y (0 .. $#{ $self->{Queue}{Queue} }) {
    $self->{Pad}->standend;
    $self->{Pad}->attron(A_REVERSE) if $y == $self->{Queue}->active;
    $self->{Pad}->addstr($y, 0, $self->{Queue}->get($y)->{Menu});
  }

	$self->{Pad}->prefresh(
	  $self->{Offset}, 0,                                               # Offset in the pad
	  $self->{AbsPosY} + $self->{Y1}, $self->{AbsPosX} + $self->{X1},   # Where in window
	  # TODO: potential offset issue. why is -1 needed?
	  $self->{AbsPosY} + $self->{Y2} - 1, $self->{AbsPosX} + $self->{X2} - 1
	);
	return "-norefresh";
}

# Provide movement functionality for the menu.
sub keymap {
  my ($self, $key) = @_;
  # Are we allowed to shift the queue with default keys?
  my $shift = $self->{ShiftKeys};
  my $del = $self->{DeleteKey};
  if ($key eq "<KEY_DOWN>") {
    $self->{Queue}->move_down;
  } elsif ($key eq "<KEY_UP>") {
    $self->{Queue}->move_up;
  } elsif ($key eq "<KEY_HOME>") {
    $self->{Queue}->move_first;
  } elsif ($key eq "<KEY_END>") {
    $self->{Queue}->move_last;
  } elsif ($shift and $key eq "<") {
    $self->{Queue}->shift_up;
  } elsif ($shift and $key eq ">") {
    $self->{Queue}->shift_down;
  } elsif ($shift and $key eq "T") {
    $self->{Queue}->shift_first;
  } elsif ($shift and $key eq "B") {
    $self->{Queue}->shift_last;
  } elsif ($del and $key eq "<KEY_DC>") {
    $self->{Queue}->del($self->{Queue}->active);
  } else {
    $self->{KeyHandler}->($self, $key);
  }
}

# Every time something happens to our queue, do something. Potentially. Yes.
sub queue_hook {
  my $queue = shift;
  my $win = $queue->{Heap}{Menu};
  my $cmd = shift;

  # Scroll if we need to. For now, just center the current entry.
  if ($queue->all <= $win->{Height}) {
    $win->{Offset} = 0;
  } else {
    my $middle = int($win->{Height} / 2);
    if ($queue->active >= $middle) {
      $win->{Offset} = $queue->active - $middle;
      # And cap it so we don't run off end of entries. Don't ask me how I figured out this.
      my $cap = $queue->all - $middle - $middle - 1;
      $win->{Offset} = $cap if $win->{Offset} > $cap;
    } else {
      $win->{Offset} = 0;
    }
  }

  if ($cmd eq "set" or $cmd eq "add") {
    my $id = shift;
    $win->format($id);
    $win->draw;
  } elsif ($cmd eq "del") {
    $win->redraw;
  } elsif ($cmd eq "move" or $cmd eq "shift") {
    $win->draw;
  }
}

# Make entries nicer looking, mainly handling length-related issues.
sub format {
  my ($self, $id) = @_;
  my $entry = $self->{Queue}{Queue}[$id];
  if ($entry->{MenuFilter}) {
    # Expecting a Template filter object.
    $entry->{Menu} = $entry->{MenuFilter}->filter($entry, $self->{Width});
  } else {
    $entry = $entry->{Menu};
    $entry ||= "<BLANK ENTRY ADDED>";
    $entry = substr($entry, 0, $self->{Width}) if length($entry) > $self->{Width};
    $entry .= " " x ($self->{Width} - length($entry));
    $self->{Queue}{Queue}[$id]{Menu} = $entry;
  }
}

42;
