##############################################################################
#
# Jarl - GroupChat Code
#
#   Perl code to handle groupchats in Jarl.  Core functions, non interface
# related.
#
##############################################################################

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

##############################################################################
#
# jarlGroupChat_AddMessage - function to add the <message/> to the groupchat.
#
##############################################################################
sub jarlGroupChat_AddMessage {
  my ($fromJID,$message) = @_;

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

  if (ref($message eq "Net::Jabber::Message") && ($message->GetBody() eq "")) {
    $Debug->Log1("jarlGroupChat_AddMessage: no body...bailing");
    return;
  }

  my $server = 0;
  $server = 1 if ($fromJID->GetResource() eq "");

  my $tag = &jarlGroupChat_Tag($fromJID->GetJID());

  $groupchat{jids}->{$fromJID->GetJID()} = $tag;

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

  my $reply = "";

  if (ref($message) eq "Net::Jabber::Message") {
    $reply = $message->Reply(template=>"client");
    $reply->SetTo($fromJID->GetJID());
  } else {
    $reply = new Net::Jabber::Message();
    $reply->SetMessage(from=>$jabber{myJID},
		       to=>$fromJID->GetJID(),
		       type=>"groupchat");
  }

  if (&jarlGroupChatIF_GroupChatExists($tag) == 0) {
    &jarlGroupChat_Channel($tag,$fromJID->GetJID());
    &jarlGroupChat_ChannelName($tag,$fromJID->GetUserID());
    &jarlGroupChat_Reply($tag,$reply);
    &jarlGroupChat_TabRotate($tag,0);
    &jarlGroupChatIF_NewGroupChat($tag);
  } else {
    if ($message eq "") {
      &jarlGroupChatIF_RaiseGroupChat($tag);
    }
  }

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

  my $myID = &jarlGroupChat_Nick($tag);
  my $currentID = $fromJID->GetResource();

  my $fromMe = ($myID eq $currentID);

  if (ref($message) eq "Net::Jabber::Message") {
    my $timestamp;
    my $history;
    my @xDelays = $message->GetX("jabber:x:delay");
    if ($#xDelays == -1) {
      $timestamp = &Net::Jabber::GetTimeStamp("local",time,"shortest");
      $history = 0;
    } else {
      my $xTag = $xDelays[0];
      my $time = &Net::Jabber::GetTimeStamp("utcdelaytime",$xTag->GetStamp());
      $time += $timeoffsets{$fromJID->GetServer()}
	if exists($timeoffsets{$fromJID->GetServer()});
      $timestamp = &Net::Jabber::GetTimeStamp("local",$time,"shortest");
      $history = 1;
    }


    my $subject = $message->GetSubject();
    if ($subject ne "") {
      &jarlGroupChat_Topic($tag,$subject);
      &jarlGroupChatIF_ShowTopic($tag,&jarlParser_ParseText(&jarlGroupChat_Topic($tag,$subject),&jarlGroupChat_Nick($tag)));
    }

    my $body = $message->GetBody();
    my @body = &jarlParser_ParseText($body,$myID);

    my $type;
    if ($body =~ /^\/me/) {
      my $metype = shift(@body);
      my $me = shift(@body);
      $me =~ s/^\/me/\* $currentID/;
      unshift(@body,$me);
      unshift(@body,$metype);
      $type = "action";
    } else {
      if ($server == 1) {
	unshift(@body,"%% ");
	unshift(@body,"normal");
	$type = "server";
      } else {
	unshift(@body,"<$currentID> ");
	unshift(@body,"me") if ($fromMe == 1);
	unshift(@body,"notme") if ($fromMe == 0);
	$type = "say";
      }
    }

    &jarlGroupChatIF_AddMessage($tag,$type,$timestamp,$history,$currentID,@body);
  }

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

  return $tag;
}


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


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


##############################################################################
#
# jarlGroupChat_PreviousNick - if $value is not defined then it returns the
#                              preivous matched nick. Otherwise, it sets the
#                              nick.
#
##############################################################################
sub jarlGroupChat_PreviousNick {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $groupchat{tags}->{$tag}->{previousnick} = $value;
  } else {
    return $groupchat{tags}->{$tag}->{previousnick};
  }
}


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


##############################################################################
#
# jarlGroupChat_ChannelName - if $value is not defined then it returns the
#                             channel name (minus server).  Otherwise, it sets
#                             the channel name.
#
##############################################################################
sub jarlGroupChat_ChannelName {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $groupchat{tags}->{$tag}->{channelname} = $value;
  } else {
    return $groupchat{tags}->{$tag}->{channelname};
  }
}


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


##############################################################################
#
# jarlGroupChat_TabRotate - if $value is not defined then it returns the value
#                           of tab rotate.  Otherwise, it sets the tab rotate.
#
##############################################################################
sub jarlGroupChat_TabRotate {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $groupchat{tags}->{$tag}->{tabrotate} = $value;
  } else {
    return $groupchat{tags}->{$tag}->{tabrotate};
  }
}


##############################################################################
#
# jarlGroupChat_HistoryUp -  function to provide the interfaces with a 
#                            consistent way of supporting scrolling up 
#                            through the history.
#
##############################################################################
sub jarlGroupChat_HistoryUp {
  my ($tag) = @_;

  $Debug->Log3("jarlGroupChat_HistoryUp: tag($tag)");

  my $current = &jarlGroupChatIF_CurrentSay($tag);

  $Debug->Log3("jarlGroupChat_HistoryUp: current($current)");

  return $current if ($#{$groupchat{tags}->{$tag}->{history}} < 0);
  return $current if ($groupchat{tags}->{$tag}->{historyPtr} == 
		      $#{$groupchat{tags}->{$tag}->{history}});

  $Debug->Log3("jarlGroupChat_HistoryUp: pointer($groupchat{tags}->{$tag}->{historyPtr})");

  if ($groupchat{tags}->{$tag}->{historyPtr} == -1) {
    $groupchat{tags}->{$tag}->{historySaved} = $current;
    $Debug->Log3("jarlGroupChat_HistoryUp: saved($groupchat{tags}->{$tag}->{historySaved})");
  }

  $groupchat{tags}->{$tag}->{historyPtr}++;

  $Debug->Log3("jarlGroupChat_HistoryUp: pointer($groupchat{tags}->{$tag}->{historyPtr})");
  $Debug->Log3("jarlGroupChat_HistoryUp: return($groupchat{tags}->{$tag}->{history}->[$groupchat{tags}->{$tag}->{historyPtr}])");

  return $groupchat{tags}->{$tag}->{history}->[$groupchat{tags}->{$tag}->{historyPtr}];
}


##############################################################################
#
# jarlGroupChat_HistoryDown - function to provide the interfaces with a consistent
#                        way of supporting scrolling down through the history.
#
##############################################################################
sub jarlGroupChat_HistoryDown {
  my ($tag) = @_;

  my $current = &jarlGroupChatIF_CurrentSay($tag);

  return $current if ($#{$groupchat{tags}->{$tag}->{history}} < 0);
  return $current if ($groupchat{tags}->{$tag}->{historyPtr} == -1);

  $groupchat{tags}->{$tag}->{historyPtr}--;

  return $groupchat{tags}->{$tag}->{historySaved}
    if ($groupchat{tags}->{$tag}->{historyPtr} == -1 );

  return $groupchat{tags}->{$tag}->{history}->[$groupchat{tags}->{$tag}->{historyPtr}];
}


##############################################################################
#
# jarlGroupChat_AddHistory - function to add a line to the chat history and reset
#                       the pointer.
#
##############################################################################
sub jarlGroupChat_AddHistory {
  my ($tag,$text) = @_;

  unshift(@{$groupchat{tags}->{$tag}->{history}},$text);
  $groupchat{tags}->{$tag}->{historyPtr} = -1;
}


##############################################################################
#
# jarlGroupChat_SendSay - function to provide the interfaces a consistent way to
#                    send the message to the chatee.
#
##############################################################################
sub jarlGroupChat_SendSay {
  my ($tag) = @_;

  my $text = &jarlGroupChatIF_CurrentSay($tag);

  return if ($text eq "");

  &jarlGroupChat_AddHistory($tag,$text);

  if ($text =~ /^\s*\/nick/i) {
    my ($newNick) = ($text =~ /^\s*\/nick\s(.*)$/i);
    &jarlGroupChat_ChangeNick($tag,$newNick);
  } else {
    if ($text =~ /^\s*\/topic/i) {
      my ($newTopic) = ($text =~ /^\s*\/topic\s(.*)$/i);
      $jabber{client}->
	MessageSend(to=>&jarlGroupChat_Reply($tag)->GetTo(),
		    subject=>$newTopic,
		    body=>"/me has changed the topic to: $newTopic");
    } else {
      &jarlGroupChat_Reply($tag)->SetMessage(body=>$text);
      $jabber{client}->Send(&jarlGroupChat_Reply($tag));
    }
  }
  &jarlGroupChatIF_CurrentSay($tag,"");
}


##############################################################################
#
# jarlGroupChat_CompleteNick - function to provide the interfaces with a
#                              consistent way to support nick completion.
#
##############################################################################
sub jarlGroupChat_CompleteNick {
  my ($tag) = @_;

  my $current = &jarlGroupChatIF_CurrentSay($tag);
  my $nick;
  my $prevNick;

  $Debug->Log1("jarlGroupChat_CompleteNick: tab_rotate(",&jarlGroupChat_TabRotate($tag),")");
  $Debug->Log1("jarlGroupChat_CompleteNick: current($current)");

  if (&jarlGroupChat_TabRotate($tag) eq "0") {
    ($prevNick) = ($current =~ /\s*(\S*)$/);

    &jarlGroupChat_TabRotate($tag,$current);

    $nick = &jarlParser_NickComplete(type=>"groupchat",
				     string=>$current,
				     channel=>&jarlGroupChat_Channel($tag));

    &jarlGroupChat_PreviousNick($tag,$nick);

    if ($nick ne "") {
      $nick .= ":" if ($current =~ /^\s*${prevNick}$/);
      $nick .= " ";
    }
  } else {
    $prevNick = &jarlGroupChat_PreviousNick($tag);

    $nick = &jarlParser_NickComplete(type=>"groupchat",
				     string=>&jarlGroupChat_TabRotate($tag),
				     previous=>$prevNick,
				     channel=>&jarlGroupChat_Channel($tag));
    &jarlGroupChat_PreviousNick($tag,$nick);
  }

  $Debug->Log1("jarlGroupChat_CompleteNick: matched($nick)");
  $Debug->Log1("jarlGroupChat_CompleteNick: prev($prevNick)");
  $Debug->Log1("jarlGroupChat_CompleteNick: current($current)");

  if ($nick ne "") {
    $prevNick =~ s/([\(\)\+\?\*\$])/\\$1/g;

    $Debug->Log1("jarlGroupChat_CompleteNick:   prev($prevNick)");
    $Debug->Log1("jarlGroupChat_CompleteNick:   current($current)");
    $current =~ s/(\s?)${prevNick}(\:?\s*)$/$1$nick$2/;
    $Debug->Log1("jarlGroupChat_CompleteNick:   current($current)");
  }

  $Debug->Log1("jarlGroupChat_CompleteNick: current($current)");

  &jarlGroupChatIF_CurrentSay($tag,$current);
}


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

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


##############################################################################
#
# jarlGroupChat_Join - function to join a groupchat channel.  Handles the nick
#                      interchange and settles on the nick before exiting.
#
##############################################################################
sub jarlGroupChat_Join {
  my ($channel,$nick) = @_;

  my ($server) = ($channel =~ /\@(.*)$/);

  if (!exists($timeoffsets{$server})) {
    my %result = $jabber{client}->TimeQuery(to=>$server,
					    waitforid=>1);
    if (defined(%result) && ($result{utc} ne "")) {
      my $time = time;
      $timeoffsets{$server} = $time - &Net::Jabber::GetTimeStamp("utcdelaytime",$result{utc});
    } else {
      $timeoffsets{$server} = 0;
    }
  }

  my $tag = &jarlGroupChat_Tag($channel);

  my $message = new Net::Jabber::Message();
  $message->SetMessage(from=>$channel,
		       to=>$channel."/".$nick,
		       type=>"groupchat",
		       body=>"Creating channel $channel");

  &jarlGroupChat_AddMessage($message->GetFrom("jid"),$message);

  &jarlGroupChat_Nick($tag,$nick);
  $groupchat{tags}->{$tag}->{presence} =
    $jabber{presence}->AddGroupchat($channel,$nick);
}


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

  $jabber{presence}->DelGroupchat(&jarlGroupChat_Channel($tag));
  &jarlGroupChat_DeleteGroupChat($tag);

  $Debug->Log1("jarlGroupChatIF_Leave: tag($tag) mytag(groupchat-$$-".$channel.")");
}


##############################################################################
#
# jarlGroupChat_ChangeNick - function to handle changing your nick.
#
##############################################################################
sub jarlGroupChat_ChangeNick {
  my ($tag,$nick) = @_;

  $Debug->Log1("jarlGroupChat_ChangeNick: tag($tag) nick($nick)");

  &jarlGroupChat_Nick($tag,$nick);

  $jabber{presence}->ModGroupchat(&jarlGroupChat_Channel($tag),
				  nick=>$nick);
}


##############################################################################
#
# jarlGroupChat_Disconnect - function to handle getting disconnected from
#                            the channel.
#
##############################################################################
sub jarlGroupChat_Disconnect {
  my ($tag) = @_;

  my $exitMessage = new Net::Jabber::Message();
  $exitMessage->SetMessage(from=>&jarlGroupChat_Reply($tag)->GetTo("jid"),
			   body=>"******** You were disconnected from this channel ********");

  &jarlGroupChat_AddMessage(&jarlGroupChat_Reply($tag)->GetTo("jid"),
			    $exitMessage);
  $groupchatRosters{$tag}->Clear();
}


##############################################################################
#
# jarlGroupChat_Tag - get the unique tag for the groupchat.
#
##############################################################################
sub jarlGroupChat_Tag {
  my ($channel) = @_;
  return "groupchat-$$-".$channel;
}


1;
