package Hexed::Fixed;

use strict;
use warnings;
use include;

use Hexed::Util;
use Curses;

# Set up the window and grid.
sub init {
  my ($class, $dat) = @_;

  # Set up our tile grid.
  foreach my $y (0 .. $dat->{Height}) {
    foreach my $x (0 .. $dat->{Width}) {
      $dat->{Grid}[$y][$x] = {
        char  => " ",  # What character?
        color => 0,    # What color? Numeric attrib value.
        fg    => "",   # Foreground color
        bg    => "",   # Background color
        draw  => 1     # Do we need to redraw this one?
      };
    }
  }

  delete $dat->{Opts};
  return bless $dat, $class;
}

# Modify a tile.
sub mod {
  my ($self, $y, $x, $new) = @_;
  my $tile = $self->{Grid}[$y][$x];
  # Accepts a hash with any of the following {char}, {color}, {fg}, or {bg}. 
  die "Gimme a hash of tile stuff!\n" unless ref $new eq "HASH";
  if (my $color = delete $new->{color}) {
    if ($color eq "/") {
      # Standend
      ($tile->{fg}, $tile->{bg}) = ("grey", "black");
    } else {
      ($tile->{fg}, $tile->{bg}) = split("/", $color);
    }
  }
  %$tile = (%$tile, %$new);
  $tile->{color} = paint $tile->{fg}, $tile->{bg};
  $tile->{draw} = 1;
  return $tile->{char};
}

# Edit a single line of the display.
sub edit {
  my ($self, $y, $x, $msg) = @_;
  ($msg) = wrap(-1, $msg);
  $y = center(1, $self->{Height}) if $y eq "center";
  my $stripped = strip $msg;
  $x = center(length($stripped), $self->{Width}) if $x eq "center";

  # Does it fit?
  if ($y < 0 or $x < 0 or $y > $self->{Height}) {
    die "Message can't fit!\n";
  } elsif ($x + length($stripped) > $self->{Width}) {
    # Fit as many full words as possible.
    my $cut = "";
    my $scut = $cut;
    $x = 0;
    while (length($scut) <= $self->{Width}) {
      $msg =~ s/^([^ ]+) //;
      my $add = $1;
      $add = " $add" if $cut;
      my $sadd = strip $add;
      $scut = strip $cut;
      last if length($scut) + length($sadd) > $self->{Width};
      $cut .= $add;
    }
    $msg = $cut;
  }

  # Split by formatting tags.
  my $color = "grey";
  foreach my $string (split(/(<[^>]+>)/, $msg)) {
    next unless $string;
    if ($string =~ m/<([^>]+)/) {
      $color = $1;
    } else {
      # A normal part of the string.
      foreach my $char (split //, $string) {
        $self->mod($y, $x++, { char => $char, color => $color });
      }
    }
  }

  return $msg;
}

# Edit a portion of the display.
sub place {
  my ($self, $y, $x, $pic, $color) = (@_, "");

  # Format the pic.
  my $new = [];
  my $long = 0;
  chomp($pic);  # Using heredocs leaves an extra newline
  foreach my $line (split(/\n/, $pic)) {
    $long = length($line) if length($line) > $long;
    push @$new, [];
    foreach my $char (split(//, $line)) {
      push @{ $new->[-1] }, $char;
    }
  }

  # Now pad it.
  foreach my $y (0 .. $#{ $new }) {
    push @{ $new->[$y] }, " " until $long == scalar @{ $new->[$y] };
  }

  # Where does it go?
  $y = center(scalar @$new, $self->{Height}) if $y eq "center";
  $x = center($long, $self->{Width}) if $x eq "center";

  die "Attempt to place a picture at $y, $x bounded off!\n"
    if $y < 0 or $x < 0
       or $y + $#{ $new } > $self->{Height} or $x + $long > $self->{Width};

  # Merge the grids.
  foreach my $Y (0 .. $#{ $new }) {
    foreach my $X (0 .. $#{ $new->[$Y] }) {
      $self->mod($y + $Y, $x + $X, { char => $new->[$Y][$X], color => $color });
    }
  }

  return ($y, $x);
}

# Redraw the window.
sub draw {
  my $self = shift;
  my $force = shift;  # After HexedUI does fullscreen nonsense, we have to
                      # literally redraw everything.

  # Clear it...
  $self->{Win}->standend;

  my $off = $self->{Off};
  foreach my $y (0 .. $self->{Height}) {
    my $prev = "";
    foreach my $x (0 .. $self->{Width}) {
      # Do we need to redraw this tile?
      my $tile = $self->{Grid}[$y][$x];
      if ($tile->{color} ne $prev) {
        # Change our color!
        $self->{Win}->standend;
        $self->{Win}->attron($tile->{color}) if $tile->{color};
        $prev = $tile->{color};
      }
      next unless $tile->{draw} or $force;
      $tile->{draw} = 0;

      # Factor in offset.
      $self->{Win}->addch($y + ($off/2), $x + ($off/2), $tile->{char});
    }
  }
  $self->{Win}->refresh;
  $self->{Win}->standend;

  return 1;
}

# Erase the grid and clear the display.
sub cls {
  my $self = shift;

  # Blank me!
  foreach my $row (@{ $self->{Grid} }) {
    foreach (@$row) {
      $_->{char} = " ";
      $_->{fg} = $_->{bg} =  "";
      $_->{draw} = $_->{color} = 0;
    }
  }

  # No point in calling mod() frequently and then doing a draw() when we can
  # just tell Curses to clear the window. (And reborder it if needed.)
  $self->{Win}->standend;
  $self->{Win}->erase;
  $self->{Win}->box(0, 0) if $self->{Border};

  return 1;
}

# Sets a background color for the entire window.
sub bg {
  my $self = shift;
  my $color = shift;
  my ($y1, $x1, $y2, $x2) = (@_) ? @_ : (0, 0, $self->{Height}, $self->{Width});
  ($y1, $y2) = ($y2, $y2) if $y1 > $y2;
  ($x1, $x2) = ($x2, $x2) if $x1 > $x2;
  foreach my $y ($y1 .. $y2) {
    foreach my $x ($x1 .. $x2) {
      $self->mod($y, $x, { bg => $color });
    }
  }
  return $color;
}

42;
