##############################################################################
#
# Jarl - XPM Code
#   Perl code to handle manipulating XPM images.  Overlaying and merging them
# for whiteboard and icon usage.
#
##############################################################################

##############################################################################
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#  Jabber
#  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
#
##############################################################################


##############################################################################
#
# jarlXPM_Parse - read in an XPM file and build a hash that other XPM
#                 functions can parse.
#
##############################################################################
sub jarlXPM_Parse {
  my $file = shift;

  my %colors;
  my %symbols;
  my @xpm;

  my @xpmData = split("\n", &jarlXPM_Cache($file));
  shift(@xpmData);
  shift(@xpmData);
  $_ = shift(@xpmData);
  my ($width,$height,$numcolors,$pixelwidth) = /^\"(\d+) (\d+) (\d+) (\d+)\",$/;

  foreach (1..$numcolors) {
    $_ = shift(@xpmData);
    my ($symbol,$rgb) = /^\"(.{$pixelwidth})\s+c\s+(\S+)\",/;
    $colors{$rgb} = $symbol;
    $symbols{$symbol} = $rgb;
  }

  foreach my $index (0..($height-1)) {
    $xpm[$index] = "";
    $_ = shift(@xpmData);
    my ($data) = /^\"(.+)\"/;
    $xpm[$index] = $data;
  }

  return ( height=>$height,
	   width=>$width,
	   pixelwidth=>$pixelwidth,
	   colors=>\%colors,
	   symbols=>\%symbols,
	   xpm=>\@xpm );
}


##############################################################################
#
# jarlXPM_Merge - takes a list of paths to files, uses the first as a base
#                 image, and overlays the rest of the files on top of it in
#                 that order.  It returns a new XPM in string format.
#
##############################################################################
sub jarlXPM_Merge {
  my ($baseFile,@overlayFiles) = @_;

  my %baseXPM = &jarlXPM_Parse($baseFile);
  foreach my $overlayFile (@overlayFiles) {
    my %overlayXPM = &jarlXPM_Parse($overlayFile);
    %baseXPM = &jarlXPM_Overlay(\%baseXPM,\%overlayXPM);
  }
  return &jarlXPM_Create(%baseXPM);
}


##############################################################################
#
# jarlXPM_Overlay - take a base image, an overlay image, and an offset of
#                   where to overlay the overlay xpm.
#
##############################################################################
sub jarlXPM_Overlay {
  my $baseXPM = shift;
  my $overlayXPM = shift;
  my $startX = shift;
  my $startY = shift;

  $startX = 0 unless defined($startX);
  $startY = 0 unless defined($startY);

  my %baseXPM = %{$baseXPM};
  my %overlayXPM = %{$overlayXPM};
  my %symbolMap;

  if ($overlayXPM{pixelwidth} != $baseXPM{pixelwidth}) {
    if ($overlayXPM{pixelwidth} < $baseXPM{pixelwidth}) {

      foreach my $symbol (keys(%{$overlayXPM{symbols}})) {
	my $newsymbol =
	  $symbol."z"x($baseXPM{pixelwidth}-$overlayXPM{pixelwidth});
	$overlayXPM{symbols}->{$newsymbol} = $overlayXPM{symbols}->{$symbol};
	$overlayXPM{colors}->{$overlayXPM{symbols}->{$symbol}} = $newsymbol;
	delete($overlayXPM{symbols}->{$symbol});
      }

      foreach my $row (0..$#{$overlayXPM{xpm}}) {
	$overlayXPM{xpm}->[$row] = join("z"x($baseXPM{pixelwidth}-$overlayXPM{pixelwidth}),grep { $_ ne ""; } split(/(.{$overlayXPM{pixelwidth}})/,$overlayXPM{xpm}->[$row]))."z";
      }
      $overlayXPM{pixelwidth} = $baseXPM{pixelwidth};
    } else {
      foreach my $symbol (keys(%{$baseXPM{symbols}})) {
	my $newsymbol =
	  $symbol."z"x($baseXPM{pixelwidth}-$baseXPM{pixelwidth});
	$baseXPM{symbols}->{$newsymbol} = $baseXPM{symbols}->{$symbol};
	$baseXPM{colors}->{$baseXPM{symbols}->{$symbol}} = $newsymbol;
	delete($baseXPM{symbols}->{$symbol});
      }

      foreach my $row (0..$#{$baseXPM{xpm}}) {
	$baseXPM{xpm}->[$row] = join("z"x($baseXPM{pixelwidth}-$baseXPM{pixelwidth}),grep { $_ ne ""; } split(/(.{$baseXPM{pixelwidth}})/,$baseXPM{xpm}->[$row]));
      }
      $baseXPM{pixelwidth} = $overlayXPM{pixelwidth};
    }
  }

  foreach my $color (keys(%{$overlayXPM{colors}})) {
    if (exists($baseXPM{colors}->{$color})) {
      $symbolMap{$overlayXPM{colors}->{$color}} = $baseXPM{colors}->{$color};
      $overlayXPM{colors}->{$color} = $baseXPM{colors}->{$color};
      $overlayXPM{symbols}->{$overlayXPM{colors}->{$color}} = $color;
    } else {
      my @symbolList = ("a".."z");
      push(@symbolList,"A".."Z");
      foreach my $symbol (@symbolList) {
	my $newsymbol = $symbol."z"x($baseXPM{pixelwidth}-1);
	if (!exists($baseXPM{symbols}->{$newsymbol}) &&
	    !exists($overlayXPM{symbols}->{$newsymbol})) {
	  $symbolMap{$overlayXPM{colors}->{$color}} = $newsymbol;
	  $overlayXPM{colors}->{$color} = $newsymbol;
	  $overlayXPM{symbols}->{$newsymbol} = $color;
	  last;
	}
      }
    }
  }

  foreach my $y (0..$#{$overlayXPM{xpm}}) {
    next if (($y+$startY) > $#{$baseXPM{xpm}});

    my $uncompressedBase =
      $baseXPM{xpm}[$y+$startY];

    my $compressedData = $overlayXPM{xpm}[$y];
    my $overlayIndex = $startX;
    while($compressedData ne "") {
      my $char = substr($compressedData,0,$overlayXPM{pixelwidth});
      $char = $symbolMap{$char};
      if ($overlayXPM{symbols}->{$char} ne "None") {
	if ($overlayXPM{symbols}->{$char} ne "") {
	  substr($uncompressedBase,$overlayIndex,$baseXPM{pixelwidth}) = $char;
	} else {
	  substr($uncompressedBase,$overlayIndex,$baseXPM{pixelwidth}) =
	    $overlayXPM{colors}->{'None'};
	}
      }
      $overlayIndex += $baseXPM{pixelwidth};
      substr($compressedData,0,$overlayXPM{pixelwidth},"");
      $baseXPM{symbols}->{$char} = $overlayXPM{symbols}->{$char}
	if !exists($baseXPM{symbols}->{$char});
    }
    $baseXPM{xpm}[$y+$startY] = $uncompressedBase;
  }	

  foreach my $symbol (keys(%{$baseXPM{symbols}})) {
    $baseXPM{colors}->{$baseXPM{symbols}->{$symbol}} = $symbol;
  }

  return %baseXPM;
}


##############################################################################
#
# jarlXPM_Create - takes an XPM hash and builds the string for the file.
#
##############################################################################
sub jarlXPM_Create {
  my (%baseXPM) = @_;

  my $xpm = "/* XPM */\n";
  $xpm .= "static char * xpm[] = {\n";
  $xpm .= "\"$baseXPM{width} $baseXPM{height} ".scalar(keys(%{$baseXPM{colors}}))." $baseXPM{pixelwidth}\",\n";
  foreach my $color (keys(%{$baseXPM{colors}})) {
    $xpm .= "\"$baseXPM{colors}->{$color} c $color\",\n";
  }
  foreach my $y (0..$#{$baseXPM{xpm}}) {
    $xpm .= "\"";
    $xpm .= $baseXPM{xpm}[$y];
    $xpm .= "\"\,\n";
  }
  $xpm =~ s/\,\n$//;
  $xpm .= "\}\;\n";

  return $xpm;
}


##############################################################################
#
# jarlXPM_Cache - goes to the cache for the xpm data, reads it in if it is not
#                 in the cache, and returns the xpm file in a string.
#
##############################################################################
sub jarlXPM_Cache {
  my $filename = shift;

#  if (!exists($xpmcache{$filename})) {
    open(XPM,$filename);
    my @xpm = <XPM>;
    close(XPM);
#    $xpmcache{$filename} = join("",@xpm);
#  }
#  return $xpmcache{$filename};
  return join("",@xpm);
}


1;
