#####################
# Interface::Curses #
#####################

package Roguelike::Interface::Curses;

use strict;
use warnings;
use Roguelike::Game;
use Roguelike::Utility;

use Curses;

END {
  endwin();
}

sub create {
  my $class = shift;
  my $layout = shift;
  # Initialize Curses and color and stuff.
  initscr();
  noecho();
  curs_set(0);
  start_color();
  keypad(1);
  ungetch(".");
  getch();
  my @colors = (qw(BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE));
  my $cnt = 0;
  no strict "refs";
  foreach my $bg (@colors) {
    foreach my $fg (@colors) {
      my $fg_ = $fg;
      my $bg_ = $bg;
      $fg_ = "COLOR_$fg";
      $bg_ = "COLOR_$bg";
      init_pair(++$cnt, &$fg_, &$bg_);
    }
  }
  use strict "refs";
  getmaxyx(my $rows, my $cols);
  my $self = bless { MaxY => $rows, MaxX => $cols }, $class;
  # Create every window in the layout.
  while (my ($window, $info) = each %{$layout}) {
    $info->{Size}[0] =~ s#(\d+)%#$1 * $rows / 100#;
    $info->{Size}[1] =~ s#(\d+)%#$1 * $cols / 100#;
    $info->{At}[0] =~ s#(\d+)%#$1 * $rows / 100#;
    $info->{At}[1] =~ s#(\d+)%#$1 * $cols / 100#;
    my $sizeY = int eval($info->{Size}[0]);
    my $sizeX = int eval($info->{Size}[1]);
    my $posY = int eval($info->{At}[0]);
    my $posX = int eval($info->{At}[1]);
    my $win = newwin($sizeY, $sizeX, $posY, $posX);
    $win->box(0, 0) if $info->{Border};
    $win->refresh;
    $self->{$window} = $win;
    $self->{"${window}Y"} = $sizeY - 4;
    $self->{"${window}X"} = $sizeX - 6;
  }
  $self->{ConsoleY} = $rows - 4;
  $self->{ConsoleX} = $cols - 6;
  return $self;
}

sub trans_symbol {
  shift;
  my $symbol = shift;
  return Curses->can($symbol) if $symbol =~ m/^ACS_/;
  return $symbol;
}

sub trans_color {
  shift;
  my $fg = shift;
  my $bg = shift;
  $bg ||= "black";
  my %colors = (
    b => 5, # Blue
    r => 2, # Red
    g => 3, # Green
    a => 7, # Aqua/Cyan
    p => 6, # Purple/Magenta
    o => 4, # Orange
    y => 4, # Yellow
    k => 1, # Black
    e => 8, # Grey
    w => 8, # White
    # These should be self-explanatory, I hope.
    blue   => 5,
    red    => 2,
    green  => 3,
    aqua   => 7,
    cyan   => 7,
    purple => 6,
    orange => 4,
    yellow => 4,
    black  => 1,
    grey   => 8,
    gray   => 8,
    white  => 8
  );
  # Remember, no attributes with the background!
  # Foreground first.
  my $bright = 0;
  $bright = 1 if $fg =~ s/^\!//;
  my $color = $colors{lc $fg};
  # Now we determine whether we're bright or dim.
  # If our first letter is capitalized, we're bright.
  $bright = 1 if substr($fg, 0, 1) eq uc substr($fg, 0, 1);
  # But then we have all these special cases. Simplify input.
  $fg = lc $fg;
  $bright = 0 if $fg eq "o" or $fg eq "orange";
  $bright = 1 if $fg eq "y" or $fg eq "yellow";
  $bright = 0 if $fg eq "e" or $fg eq "grey" or $fg eq "gray";
  $bright = 1 if $fg eq "w" or $fg eq "white";
  my $attrib = $bright ? A_BOLD() : A_NORMAL();
  # Now factor in background.
  $bg = $colors{lc $bg};
  $color += 8 * ($bg - 1);
  return $attrib | COLOR_PAIR($color);
}

sub draw {
  my $self = shift;
  $self->{Display}->erase;  # /clear
  $self->{Display}->standend;
  $self->{Display}->box(0, 0) if $Game->{UI}{Layout}{Display}{Border};
  my $y = 0;
  foreach my $row (@_) {
    my $x = 1;
    foreach my $tile (@{ $row }) {
      $self->{Display}->addch(2 + $y, 2 + $x, $tile->{_DISPLAY_});
      $x++;
    }
    $y++;
  }
  $self->{Display}->refresh;
}

sub msgdraw {
  my $self = shift;
  $self->{MsgConsole}->erase;  # /clear
  $self->{MsgConsole}->standend;
  $self->{MsgConsole}->box(0, 0) if $Game->{UI}{Layout}{MsgConsole}{Border};
  my $y = 0;
  my $last = "";
  foreach my $line (@_) {
    $self->{MsgConsole}->standend;
    $self->{MsgConsole}->attron($self->trans_color($last)) if $last;
    my $x = 0;
    # Split by <\w+> and print the color codes in the right spots.
    foreach my $string (split(/(<\w+>)/, $line)) {
      if ($string =~ m/<(\w+)>/) {
        $self->{MsgConsole}->standend;
        $self->{MsgConsole}->attron($self->trans_color($1));
        $last = $1;
      } else {
        $self->{MsgConsole}->addstr($y, 2 + $x, $string);
        $x += length $string;
      }
    }
    $y++;
  }
  $self->{MsgConsole}->refresh;
}

sub prompt {
  $Game->{Input}{Queued} = [] unless $Game->{Input}{Queued};
  my $in;
  if (@{ $Game->{Input}{Queued} }) {
    $in = shift @{ $Game->{Input}{Queued} };
  } else {
    $in = getch;
  }
  push @{ $Game->{Input}{Used} }, $in;
  return $in;
}

sub display {
  my $self = shift;
  my $mode = shift; # 0=Init, 1=Display, 2=Close
  if ($mode == 0) {
    # Get ready to rumble
    $self->{Console}->erase;
    return 1;
  } elsif ($mode == 2) {
    # Get ready to... um... do the opposite of rumbling
    $Game->{UI}->msgrefresh(1);
    $Game->{UI}->statrefresh;
    return 1;
  }
  # Display some stuff
  $self->{Console}->erase;  # /cl
  $self->{Console}->standend;
  $self->{Console}->box(0, 0) if $Game->{UI}{Layout}{Console}{Border};;
  my $y = 0;
  foreach my $line (@_) {
    $self->{Console}->standend;
    next unless defined $line;
    my $x = 0;
    # Split by <\w+> and print the color codes in the right spots.
    foreach my $string (split(/(<\w+>)/, $line)) {
      if ($string =~ m/<(\w+)>/) {
        $self->{Console}->standend;
        $self->{Console}->attron($self->trans_color($1));
      } else {
        $self->{Console}->addstr(2 + $y, 3 + $x, $string);
        $x += length $string;
      }
    }
    $y++;
  }
  $self->{Console}->refresh;
}

sub statdraw {
  my $self = shift;
  $self->{Status}->erase;  # /clear
  $self->{Status}->standend;
  $self->{Status}->box(0, 0) if $Game->{UI}{Layout}{Status}{Border};
  my $y = 0;
  my $inv = 0;
  foreach my $line (@_) {
    $line = "<Blue><Green>[MORE]  Press ?" if $y == $self->{StatusY};
    $inv = 1 if $line eq "<LINE>";
    $self->{Status}->standend;
    my $x = 0;
    # Split by <\w+> and print the color codes in the right spots.
    foreach my $string (split(/(<\w+>)/, $line)) {
      if ($string =~ m/<(\w+)>/) {
        $self->{Status}->standend;
        $self->{Status}->attron($self->trans_color($1));
      } else {
        if ($inv and $line =~ m/^<B/) {
          $self->{Status}->addstr(1 + $y, 1 + $x, $string);
        } else {
          $self->{Status}->addstr(1 + $y, 2 + $x, $string);
        }
        $x += length $string;
      }
    }
    last if $y == $self->{StatusY};
    $y++;
  }
  my $x = 0;
  my $line = "<Blue>Position:<grey> $Player->{Y}, $Player->{X} ($Player->{Z})";
  foreach my $string (split(/(<\w+>)/, $line)) {
    if ($string =~ m/<(\w+)>/) {
      $self->{Status}->standend;
      $self->{Status}->attron($self->trans_color($1));
    } else {
      $self->{Status}->addstr($self->{StatusY} + 2, 1 + $x, $string);
      $x += length $string;
    }
  }
  $self->{Status}->refresh;
}

sub msgbox {
  my $self = shift;
  my $mode = shift;
  return 1 if $mode == 0;
  if ($mode == 2) {
    # Unrumble, guys!
    $Game->{UI}->msgrefresh(1);
    $Game->{UI}->statrefresh;
    return 1;
  }
  # Mode 0 is useless... we have to erase every time anyway.
  # Get the Console going
  $self->{Console}->standend;
  $self->{Console}->erase;
  $self->{Console}->box(0, 0) if $Game->{UI}{Layout}{Console}{Border};;
  # Longest line? Align left.
  my $length = 0;
  foreach (@_) {
    my $testline = $_;
    $testline =~ s/<\w+>//g;
    $length = length $testline if length $testline > $length;
  }
  my @lines = (" " x ($length + 2));
  foreach (@_) {
    my $line = $_;
    my $testline = $line;
    $testline =~ s/<\w+>//g;
    $line .= " " x ($length - length($testline));
    $line = " $line ";
    push @lines, $line;
  }
  push @lines, " " x ($length + 2);
  # Add space to ends of lines. PAD!
  my $y = ($self->{MaxY} - @_) / 2 - 1;
  my $last = "black";
  foreach my $line (@lines) {
    # Set background to white.
    $self->{Console}->attron($self->trans_color($last, "white"));
    my $x = ($self->{MaxX} - $length) / 2 - 2;
    # Split by <\w+> and print the color codes in the right spots.
    foreach my $string (split(/(<\w+>)/, $line)) {
      if ($string =~ m/<(\w+)>/) {
        $self->{Console}->standend;
        $self->{Console}->attron($self->trans_color($1, "white"));
        $last = $1;
      } else {
        $self->{Console}->addstr(1 + $y, 2 + $x, $string);
        $x += length $string;
      }
    }
    $y++;
  }
  $self->{Console}->refresh;
  while (1) {
    my $in = $self->prompt;
    return $in if $mode == 5;
    last if $in eq " ";
    $self->msgbox($mode, @_);
    last;
  }
  return 1;
}

sub cursor {
  my ($self, $y, $x, $color, $char) = @_;
  # Set y,x on Display to attrib and color.
  $self->{Display}->standend;
  $self->{Display}->move($y, $x);
  $self->{Display}->attron($color);
  $self->{Display}->echochar($char);
  return 1;
}

sub clearjunk {
  my $self = shift;
  # Screen junk... Bah humbug.
  $self->{Display}->clear;
  $self->{MsgConsole}->clear;
  $self->{Status}->clear;
}


42;
