##############################################################################
#
# Jarl - SXPM Code
#   Perl code to handle generating sxpm shapes based off either a generic
# shape or compressing an existing grid of points.
#
##############################################################################

##############################################################################
#
#  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/
#
##############################################################################


##############################################################################
#
# jarlSXPM_AddMessage - function to add the <message/> to the sxpm.
#
##############################################################################
sub jarlSXPM_AddMessage {
  my ($JID,$message,$server) = @_;

  $Debug->Log1("jarlSXPM_AddMessage: start");

  $server = 0 unless defined($server);

  my $fromMe = 0;
  my $sxpmID;
  my $myID;
  my $nickID;
  my $myJID;
  my $sxpmJID;
  my $currentID;

  if ($JID == $jabber{myJID}) {
    $myJID = $JID;
    $sxpmJID = $message->GetTo("jid");
    $fromMe = 1;
  } else {
    $myJID = $jabber{myJID};
    $sxpmJID = $JID;
  }

  $Debug->Log3("jarlSXPM_Addmessage: sxpmJID($sxpmJID) ref(".ref($sxpmJID).")");
  $Debug->Log3("jarlSXPM_Addmessage: sxpmJID(".$sxpmJID->GetJID("full").")")
    if (ref($sxpmJID) eq "Net::Jabber::JID");

  $sxpmID = $Roster->
    GetValue($sxpmJID->GetJID(),"name");
  $sxpmID = $sxpmJID->GetUserID() if (!defined($sxpmID) || ($sxpmID eq ""));
  $myID = $myJID->GetUserID();
  $nickID = $sxpmJID->GetUserID();
  
  if (exists($groupchat{jids}->{$sxpmJID->GetJID()})) {
    my $channel = $sxpmID;
    (($channel) = ($sxpmID =~ /^([^\@]+)\@/)) if ($channel =~ /\@/);
    $sxpmID = $channel."/".$sxpmJID->GetResource();
    $myID = $channel."/".$groupchat{tags}->{&jarlGroupChat_Tag($sxpmJID->GetJID())}->{nick};
    $nickID = $sxpmJID->GetResource();
  } else {
    $sxpmJID->SetResource();
  }

  if ($fromMe == 1) {
    $currentID = $myID;
  } else {
    $currentID = $sxpmID;
  }

  my $tag = "";
  
  my $reply = "";
  my $fullJID;
  if (exists($groupchat{jids}->{$sxpmJID->GetJID()})) {
    $fullJID = $sxpmJID->GetJID("full");
  } else {
    $fullJID = $sxpmJID->GetJID();	
  }

  $Debug->Log2("jarlSXPM_AddMessage: fullJID($fullJID)");

  if (ref($message) eq "Net::Jabber::Message") {
    if (!($message->DefinedFrom())) {
      $message->SetFrom($message->GetTo());
      $message->SetTo($jabber{myJID});
    }	
    if (!exists($sxpm{jids}->{$fullJID})) {
      $reply = $message->Reply();
      if ($message->GetThread() ne "") {
	$tag = "sxpm-$$-".$message->GetThread();
      } else {
	$tag = "sxpm-$$-jarl".$jabber{threadCount}.time;
	$jabber{threadCount}++;
      }
      $sxpm{jids}->{$fullJID} = $tag;
    } else {
      $tag = $sxpm{jids}->{$fullJID};
    }
  } else {
    if (!exists($sxpm{jids}->{$fullJID})) {
      $reply = new Net::Jabber::Message();
      $reply->SetMessage(from=>$jabber{myJID},
			 to=>$sxpmJID,
			 type=>"chat",
			 thread=>"jarl".$jabber{threadCount}.time);
      $tag = "sxpm-$$-".$reply->GetThread();
      $jabber{threadCount}++;
    
      $sxpm{jids}->{$fullJID} = $tag;
    } else {
      $tag = $sxpm{jids}->{$fullJID};
    }
  }
  
  if (&jarlSXPMIF_SXPMExists($tag) == 0) {
    &jarlSXPM_BoardName($tag,$sxpmID);
    &jarlSXPM_Type($tag,$reply->GetType());
    &jarlSXPM_SXPMID($tag,$sxpmID);
    &jarlSXPM_NickID($tag,$nickID);
    &jarlSXPM_Reply($tag,$reply);
    &jarlSXPM_JID($tag,$reply->GetTo("jid"));
    &jarlSXPMIF_NewSXPM($tag);
#    &jarlSXPM_UserSecure($tag,-1);
#    &jarlSXPM_SecureSXPM($reply->GetTo("jid"));
#    &jarlSXPM_UserSecure($tag,&jarlSXPM_Secure($tag));
  } else {
    if ($message eq "") {
      &jarlSXPMIF_RaiseSXPM($tag);
    }
  }

  $Debug->Log1("jarlSXPM_AddMessage: Use tag($tag)");

  if (ref($message) eq "Net::Jabber::Message") {
    
    my @sxpms = $message->GetX("jabber:x:sxpm");
    foreach my $sxpm (@sxpms) {
      if ($sxpm->DefinedBoard()) {
	&jarlSXPM_BoardHeight($tag,$sxpm->GetBoardHeight());
	&jarlSXPM_BoardWidth($tag,$sxpm->GetBoardWidth());
	my %newXPM = &jarlSXPM_NewXPM($sxpm->GetBoardWidth(),
				      $sxpm->GetBoardHeight());
	&jarlSXPM_BaseXPM($tag,\%newXPM);

	&jarlSXPMIF_ResizeBoard($tag);
	print "New board or resize....\n";
	print "Make resize function for base XPM...\n";
      }
      if ($sxpm->DefinedData()) {
	my %overlayXPM = &jarlSXPM_BuildXPM($sxpm->GetDataWidth(),
					    $sxpm->GetData());

	my %newXPM = &jarlXPM_Overlay(&jarlSXPM_BaseXPM($tag),
				      \%overlayXPM,
				      $sxpm->GetDataX(),
				      $sxpm->GetDataY());
	
	&jarlSXPM_BaseXPM($tag,\%newXPM);

	&jarlSXPMIF_AddXPM($tag,&jarlXPM_Create(%newXPM));

	print "Modify the sxpm...\n";
      }
      my @maps = $sxpm->GetMaps();
      if ($#maps > -1) {
	print "Modify the map...\n";
      }
    }
  }

  $Debug->Log1("jarlSXPM_AddMessage: finish");

  return $tag;
}


##############################################################################
#
# jarlSXPM_DeleteSXPM - delete the memory taken up by this sxpm session.
#
##############################################################################
sub jarlSXPM_DeleteSXPM {
  my ($tag) = @_;

  foreach my $jid (keys(%{$sxpm{jids}})) {
    delete($sxpm{jids}->{$jid}) if ($sxpm{jids}->{$jid} eq $tag);
  }
  delete($sxpm{tags}->{$tag});
}


##############################################################################
#
# jarlSXPM_Leave - leaves the current channel.
#
##############################################################################
sub jarlSXPM_Leave {
  my ($tag) = @_;

  &jarlSXPM_DeleteSXPM($tag);

  $Debug->Log1("jarlSXPMIF_Leave: tag($tag)");
}


##############################################################################
#
# jarlSXPM_NewXPM - takes sxpm data and build an XPM hash.
#
##############################################################################
sub jarlSXPM_NewXPM {
  my ($width,$height) = @_;

  my %xpm;
  
  foreach my $y (0..$height-1) {
    $xpm{xpm}[$y] = "$width ";
  }

  $xpm{width} = $width;
  $xpm{height} = $height;

  #
  # From Map to color list....
  #
  $xpm{colors}->{'#000000'} = "a";
  $xpm{colors}->{'None'} = " ";
  $xpm{symbols}->{'a'} = '#000000';
  $xpm{symbols}->{' '} = 'None';

  return %xpm;
}



##############################################################################
#
# jarlSXPM_BuildXPM - takes sxpm data and build an XPM hash.
#
##############################################################################
sub jarlSXPM_BuildXPM {
  my ($width,$data) = @_;

  my %xpm;

  my $newData = &jarlSXPM_Uncompress($data);

  my $y = 0;
  while($newData ne "") {
    $xpm{xpm}[$y] = substr($newData,0,$width);
    substr($newData,0,$width) = "";
    $y++;
  }
  $xpm{width} = $width;
  $xpm{height} = $y+1;


  #
  # From Map to color list....
  #
  $xpm{colors}->{'#000000'} = "a";
  $xpm{colors}->{'None'} = " ";
  $xpm{symbols}->{'a'} = '#000000';
  $xpm{symbols}->{' '} = 'None';

  return %xpm;
}



##############################################################################
#
# jarlSXPM_BaseXPM - if $value is not defined then it returns the basexpm
#                    pointer.  Otherwise, it sets the basexpm pointer.
#
##############################################################################
sub jarlSXPM_BaseXPM {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $sxpm{tags}->{$tag}->{basexpm} = $value;
  } else {
    return $sxpm{tags}->{$tag}->{basexpm};
  }
}


##############################################################################
#
# jarlSXPM_BoardName - if $value is not defined then it returns the boardname.
#                      Otherwise, it sets the boardname.
#
##############################################################################
sub jarlSXPM_BoardName {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $sxpm{tags}->{$tag}->{boardname} = $value;
  } else {
    return $sxpm{tags}->{$tag}->{boardname};
  }
}


##############################################################################
#
# jarlSXPM_BoardHeight - if $value is not defined then it returns the 
#                        boardheight.  Otherwise, it sets the boardheight.
#
##############################################################################
sub jarlSXPM_BoardHeight {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $sxpm{tags}->{$tag}->{boardheight} = $value;
  } else {
    return $sxpm{tags}->{$tag}->{boardheight} 
      if exists($sxpm{tags}->{$tag}->{boardheight});
    return 10;
  }
}


##############################################################################
#
# jarlSXPM_BoardWidth - if $value is not defined then it returns the 
#                       boardwidth.  Otherwise, it sets the boardwidth.
#
##############################################################################
sub jarlSXPM_BoardWidth {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $sxpm{tags}->{$tag}->{boardwidth} = $value;
  } else {
    return $sxpm{tags}->{$tag}->{boardwidth}
      if exists($sxpm{tags}->{$tag}->{boardwidth});
    return 10;
  }
}


##############################################################################
#
# jarlSXPM_Reply - if $value is not defined then it returns the reply.  
#                   Otherwise, it sets the reply.
#
##############################################################################
sub jarlSXPM_Reply {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $sxpm{tags}->{$tag}->{reply} = $value;
  } else {
    return $sxpm{tags}->{$tag}->{reply};
  }
}


##############################################################################
#
# jarlSXPM_Type - if $value is not defined then it returns the nickid.  
#                   Otherwise, it sets the nickid.
#
##############################################################################
sub jarlSXPM_Type {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $sxpm{tags}->{$tag}->{type} = $value;
  } else {
    return $sxpm{tags}->{$tag}->{type};
  }
}


##############################################################################
#
# jarlSXPM_NickID - if $value is not defined then it returns the nickid.  
#                   Otherwise, it sets the nickid.
#
##############################################################################
sub jarlSXPM_NickID {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $sxpm{tags}->{$tag}->{nickid} = $value;
  } else {
    return $sxpm{tags}->{$tag}->{nickid};
  }
}


##############################################################################
#
# jarlSXPM_SXPMID - if $value is not defined then it returns the sxpmid.  
#                   Otherwise, it sets the sxpmid.
#
##############################################################################
sub jarlSXPM_SXPMID {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $sxpm{tags}->{$tag}->{sxpmid} = $value;
  } else {
    return $sxpm{tags}->{$tag}->{sxpmid};
  }
}


##############################################################################
#
# jarlSXPM_JID - if $value is not defined then it returns the jid.  Otherwise,
#                it sets the jid.
#
##############################################################################
sub jarlSXPM_JID {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $sxpm{tags}->{$tag}->{jid} = $value;
  } else {
    return $sxpm{tags}->{$tag}->{jid};
  }
}


##############################################################################
#
# jarlSXPM_Init - Initialize the grid with transparent pixels.
#
##############################################################################
sub jarlSXPM_Init {
  my ($width,$height,$transparent) = @_;
  
  my @grid;
  foreach my $y (0..$height) {
    foreach my $x (0..$width) {
      $grid[$y][$x] = $transparent;
    }
  }
  return @grid;
}
    

##############################################################################
#
# jarlSXPM_Draw - debug function to generate a string suitable for drawing on
#                 te terminal.
#
##############################################################################
sub jarlSXPM_Draw {
  my ($grid,$transparent) = @_;
  my @grid = @{$grid};
  my $sxpm = "";
  foreach my $y (0..$#grid) {
    foreach my $x (0..$#{$grid[$y]}) {
      $sxpm .= $grid[$y][$x];
    }
    $sxpm .= "\n";
  }
  return ($#{$grid[0]}+1,$sxpm);
}


##############################################################################
#
# jarlSXPM_CompressGrid - function to turn a grid of points and the colors
#                         the sxpm format and returns it in a string.
#
##############################################################################
sub jarlSXPM_CompressGrid {
  my ($grid) = @_;
  my @grid = @{$grid};

  my $newData = "";
  foreach my $y (0..$#grid) {
    $newData .= join("",@{$grid[$y]});
  }

  return ($#{$grid[0]}+1,&jarlSXPM_Compress($newData));
}


##############################################################################
#
# jarlSXPM_Compress - function to turn a string into a compressed sxpm format
#                     string.
#
##############################################################################
sub jarlSXPM_Compress {
  my ($data) = @_;
  my $newData = "";
  while($data ne "") {
    my ($char,$restChar) = ($data =~ /^(.)(\1*)/);
    my $subData = $char.$restChar;
    $data =~ s/^(.)(\1*)//;
    my $length = length($subData);
    $length = "" if ($length == 1);
    $newData .= $length.$char;
  }
  return $newData;
}


##############################################################################
#
# jarlSXPM_Uncompress - function to turn sxpm data back into a string.
#
##############################################################################
sub jarlSXPM_Uncompress {
  my ($sxpm) = @_;

  while($sxpm =~ /(\d+)(.)/) {
    my $newString = $2x$1;
    $sxpm =~ s/\d+./$newString/;
  }
  return $sxpm;
}


##############################################################################
#
# jarlSXPM_Rect - function to generate a rect based on starting (x,y), ending
#                 (x,y), thickness for border, and the colors.
#
##############################################################################
sub jarlSXPM_Rect {
  my($x0,$y0,$x1,$y1,$thickness,$transparent,$color,$fill) = @_;
  
  my $shiftX = ($x0 < $x1) ? $x0 : $x1;
  my $shiftY = ($y0 < $y1) ? $y0 : $y1;

  my $width = abs($x1-$x0);
  my $height = abs($y1-$y0);

  my @rect = &jarlSXPM_Init($width,$height,$transparent);

  foreach my $y (0..$height) {
    foreach my $x (0..$width) {
      $rect[$y][$x] = $color;
    }
  }

  foreach my $y ($thickness..($height-$thickness)) {
    foreach my $x (0..($thickness-1)) {
      $rect[$y][$x] = $color;
    }
    foreach my $x ($thickness..($width-$thickness)) {
      $rect[$y][$x] = $fill;
    }
    foreach my $x (($width-$thickness+1)..$width) {
      $rect[$y][$x] = $color;
    }
  }

  foreach my $y (($height-$thickness+1)..$height) {
    foreach my $x (0..$width) {
      $rect[$y][$x] = $color;
    }
  }
#  print "($shiftX,$shiftY,",&jarlSXPM_Draw(\@rect),")\n";
  return ($shiftX,$shiftY,&jarlSXPM_CompressGrid(\@rect));
}


##############################################################################
#
# jarlSXPM_Line - function to generate a line based on starting (x,y), ending
#                 (x,y), thickness for border, and the colors.
#
##############################################################################
sub jarlSXPM_Line {
  my($x0,$y0,$x1,$y1,$thickness,$transparent,$color,$fill) = @_;
  
  my $shiftX = ($x0 < $x1) ? $x0 : $x1;
  my $shiftY = ($y0 < $y1) ? $y0 : $y1;

  my $oddThick = ($thickness/2)-int($thickness/2);
  my $leftThick = (($oddThick >= .5) ? 
		   -int($thickness/2) : 
		   -(int($thickness/2)-1));
  
  $shiftX += $leftThick;

  my $width = abs($x1-$x0)+abs($leftThick)+int($thickness/2);
  my $height = abs($y1-$y0);

  print "width($width) height($height) thickness($thickness) leftThick($leftThick)\n";

  my @line =  &jarlSXPM_Init($width,$height,$transparent);

  my $dx = $x1 - $x0;
  my $dy = $y1 - $y0;

  my $x = $x0;
  my $y = $y0;

  foreach my $plotThick ($leftThick..int($thickness/2)) {
    print "$plotThick\n";
    print $x0-$shiftX+$plotThick,"\n";
    $line[$y-$shiftY][$x0-$shiftX+$plotThick] = $color;
  }

  if (abs($dx) > abs($dy)) {
    my $m = $dy / $dx;
    my $b = $y - ($m*$x);
    $dx = ($dx < 0) ? -1 : 1;
    while ($x != $x1) {
      $x += $dx;
      my $plotY = int(($m*$x) + $b)-$shiftY;
      print "($x,$plotY)\n";
      $plotY = 0 if ($plotY < 0);

      foreach my $plotThick ($leftThick..int($thickness/2)) {
	$line[$plotY][$x-$shiftX+$plotThick] = $color;
      }
    }
  } else {
    if ($dy != 0) {
      my $m = $dx / $dy;
      my $b = $x - ($m*$y);
      $dy = ($dy < 0) ? -1 : 1;
      while ($y != $y1) {
	$y += $dy;

	my $plotX = int(($m*$y) + $b)-$shiftX;

	foreach my $plotThick ($leftThick..int($thickness/2)) {
	  $line[$y-$shiftY][$plotX+$plotThick] = $color;
	}
      }
    }
  }
  print "($shiftX,$shiftY,",&jarlSXPM_Draw(\@line),")\n";
  return ($shiftX,$shiftY,&jarlSXPM_CompressGrid(\@line));
}


##############################################################################
#
# jarlSXPM_Oval - function to generate a oval based on starting (x,y),
#                 ending (x,y), thickness for border, and the colors.
#
##############################################################################
sub jarlSXPM_Oval {
  my ($x0,$y0,$x1,$y1,$thickness,$transparent,$color,$fill) = @_;
  
  my $shiftX = ($x0 < $x1) ? $x0 : $x1;
  my $shiftY = ($y0 < $y1) ? $y0 : $y1;

  my $width = abs($x1-$x0);
  my $height = abs($y1-$y0);

  my @oval = &jarlSXPM_Init($width,$height,$transparent);

  my $halfHeight = ($height/2);
  my $halfWidth = ($width/2);
  
  my $aSqr = ($halfWidth)**2;
  my $bSqr = ($halfHeight)**2;
  
  my $quarterX = int($halfWidth);
  my $quarterY = int($halfHeight);
  
  my $maxY = 0;
  my $maxX = 0;
  
  foreach my $y (0..($quarterY-1)) {
    my $x = int(sqrt((($halfWidth)**2)*(1-($y**2)/$bSqr)));

    $x = int($halfWidth) if ($x > int($halfWidth));
    
    foreach my $thick (0..($thickness-1)) {
      my $plotThick = $thick;
      $plotThick = 0 if (($x-$plotThick) < 0);
      
      my $plotPosX = ($x-$plotThick)+$quarterX;
      my $plotNegX = -($x-$plotThick)+$quarterX;
      my $plotPosY = $y+$quarterY;
      my $plotNegY = -$y+$quarterY;
      
      $oval[$plotPosY][$plotPosX] = $color;
      $oval[$plotPosY][$plotNegX] = $color;
      $oval[$plotNegY][$plotPosX] = $color;
      $oval[$plotNegY][$plotNegX] = $color; 
      $maxX = $plotPosX if ($plotPosX > $maxX);
    }
  }
  
  foreach my $x (0..($quarterX-1)) {
    my $y = int(sqrt((($halfHeight)**2)*(1-($x**2)/$aSqr)));
    
    foreach my $thick (0..($thickness-1)) {
      my $plotThick = $thick;
      $plotThick = 0 if (($y-$plotThick) < 0);
      
      my $plotPosX = $x+$quarterX;
      my $plotNegX = -$x+$quarterX;
      my $plotPosY = ($y-$plotThick)+$quarterY;
      my $plotNegY = -($y-$plotThick)+$quarterY;
      
      $oval[$plotPosY][$plotPosX] = $color;
      $oval[$plotPosY][$plotNegX] = $color;
      $oval[$plotNegY][$plotPosX] = $color;
      $oval[$plotNegY][$plotNegX] = $color;
      $maxY = $plotPosY if ($plotPosY > $maxY); 
    }
  }
  
  if (defined($fill)) {
    foreach my $x (0..$quarterX) {
      foreach my $y (0..$quarterY) {
	last if ($oval[$y+$quarterY][$x+$quarterX] eq $color);
	
	$oval[$y+$quarterY][$x+$quarterX] = $fill;
	$oval[$y+$quarterY][-$x+$quarterX] = $fill;
	$oval[-$y+$quarterY][$x+$quarterX] = $fill;
	$oval[-$y+$quarterY][-$x+$quarterX] = $fill;
      }
    }
  }

#  print "($shiftX,$shiftY,",&jarlSXPM_Draw(\@oval),")\n";
  return ($shiftX,$shiftY,&jarlSXPM_CompressGrid(\@oval));
}


##############################################################################
#
# jarlSXPM_Text - function to generate text starting (x,y), color, size and
#                 the text.
#
##############################################################################
sub jarlSXPM_Text {
  my ($x0,$y0,$size,$transparent,$color) = @_;

  my @text;

  return &jarlSXPM_Draw(\@text);
}


1;
