package Hexed::Util;

use strict;
use warnings;
use include;

use Exporter;
our @ISA = ("Exporter");
our @EXPORT = (qw( &strip &wrap &center &paint ));

use Curses;

use Inline C => "DATA";

use Memoize;

# Strip the formatting tags out of a string.
sub strip {
  my $orig = shift;
  $orig =~ s/<[^>]+>//g;
  return $orig;
}

# Turns a string into a list of strings by word wrapping to the specified
# number of fixed-width columns. Any embedded formatting tags (denoted by
# <string>) are preserved, but do not count towards a line's width.
sub wrap {
  my ($width, $in) = @_;
  $width = length($in) if $width == -1;
  my @msgs;
  my ($real, $line) = ("", "");

  # Leading whitespace SHOULD be preserved!!!
  my $lead;
  if ($in =~ s/^(\s+)//) {
    $real = $line = $1;
    $lead = length($line);
  } else {
    $lead = 0;
  }

  # Formatting and wrapping... mmm, text bondage.
  $in =~ s/\n/ /g;    # Collapse editor-inserted newlines
  $in =~ s/\s+/ /g;   # Crunch extra whitespace

  # My homegrown word wrap disregards <formatting> tags!
  foreach my $realword (split(/\s/, $in)) {
    # Strip the word first.
    my $word = strip $realword;
    if (length($line) + length($word) + 1 <= $width) {
      # We have enough room on this line.
      unless (length($line) == 0 or $lead) {
        # If we're at the start of the line (including leading whitespace),
        # don't add another space.
        $word = " $word";
        $realword = " $realword";
      }
      $line .= $word;
      $real .= $realword;
    } elsif (length($line) == $lead) {
      # If we're at the beginning of a line (including leading whitespace) and
      # the next word is just too big for one line, put as much of it as we can
      # on this line and the rest on the next.

      # TODO: WARNING. If we encounter a huge line like this and there are
      # formatting tags, just give up.

      # First get as much of $word as we can handle for this line.

      # TODO: On second thought...
      die "Word wrap exploded. Too big. Ugh.\n";
    } else {
      # We've hit the edge and have to wrap.
      push @msgs, $real;
      # Start a new line with this current word.
      $line = $word;
      $real = $realword;
    }
    $lead = 0;
  }
  push @msgs, $real;  # Leftovers?

  return @msgs;
}

# Tells where a string of the specified length should start on a line segment
# of the specified length so that the string is centered. May be one space off
# due to the fixed-width nature of terminal fonts.
sub center {
  my ($strlen, $total) = @_;
  return int($total / 2) - int($strlen / 2);
}

# Caching color codes is highly useful in big maps. Believe me.
memoize("paint");

# Returns the attribute for the specified color.
sub paint {
  my ($fg, $bg);
  if (@_ == 1) {
    ($fg, $bg) = ($_[0] eq "/") ? ("grey", "black") : (split("/", shift));
  } else {
    ($fg, $bg) = @_;
  }
  # If the foreground and background color are the same, something screws up.
  # So prevent that.
  # Used to be "if (!$fg)"... but that'd be something!
  $bg ||= "black";
  $fg = "black" if $bg =~ m/(grey|grey|white)/i;
  #debug [$fg, $bg];
  return trans_color($fg, $bg);
}

42;

__DATA__
__C__

#include <ncurses.h>

// Return the attribute for the specified foreground and background color.
int trans_color (char *fg, char *bg) {

  // Uppercase first letter denotes bright, lowercase denotes normal
  int attrib;
  if (islower(fg[0])) {
    attrib = A_NORMAL;
  } else {
    attrib = A_BOLD;
  }

  int fgcolor = color_attrib(fg);
  // Some foreground colors require brightness or something.
  if ( strcasecmp(fg, "orange") == 0) {
    attrib = A_NORMAL;
  } else if ( strcasecmp(fg, "yellow") == 0) {
    attrib = A_BOLD;
  } else if ( strcasecmp(fg, "grey") == 0) {
    attrib = A_NORMAL;
  } else if ( strcasecmp(fg, "gray") == 0) {
    attrib = A_NORMAL;
  } else if ( strcasecmp(fg, "white") == 0) {
    attrib = A_BOLD;
  }

  int bgcolor = color_attrib(bg);

  int color = fgcolor + (8 * (bgcolor - 1));
  return attrib | COLOR_PAIR(color);
}

// Return the attribute for just one color.
int color_attrib (char *color) {
  int attrib;
  if ( strcasecmp(color, "black") == 0) {
    attrib = 1;
  } else if ( strcasecmp(color, "red") == 0) {
    attrib = 2;
  } else if ( strcasecmp(color, "green") == 0) {
    attrib = 3;
  } else if ( strcasecmp(color, "yellow") == 0) {
    attrib = 4;
  } else if ( strcasecmp(color, "orange") == 0) {
    attrib = 4;
  } else if ( strcasecmp(color, "blue") == 0) {
    attrib = 5;
  } else if ( strcasecmp(color, "purple") == 0) {
    attrib = 6;
  } else if ( strcasecmp(color, "aqua") == 0) {
    attrib = 7;
  } else if ( strcasecmp(color, "cyan") == 0) {
    attrib = 7;
  } else if ( strcasecmp(color, "grey") == 0) {
    attrib = 8;
  } else if ( strcasecmp(color, "gray") == 0) {
    attrib = 8;
  } else if ( strcasecmp(color, "white") == 0) {
    attrib = 8;
  } else {
    fprintf(stderr, "Error! Don't recognize color %s!\n", color);
  }
  return attrib;
}
