# -*- mode: perl; coding: utf-8 -*-
# Keitairc::IrcBuffer
#
# Copyright (c) 2008 Jun Morimoto <morimoto@mrmt.net>
# This program is covered by the GNU General Public License 2

package Keitairc::IrcBuffer;
use strict;
use warnings;

################################################################
sub new{
	my $proto = shift;
	my $arg = shift;
	my $me = {};

	$me->{history} = $arg->{history};

	# join しているchannelの名称を記録するハッシュ。
	# - cid および name2cid ハッシュに格納されている値は整数。
	# - cid2nameハッシュに格納されているのは perl decoded な チャネル名
	$me->{cid2name} = {};
	$me->{name2cid} = {};

	# join しているtopicの名称を記録するハッシュ
	# charset: perl internal
	$me->{topic} = {};

	$me->{nicks} = {};

	$me->{tbuffer} = {};	# time, ref to array
	$me->{nbuffer} = {};	# nick, ref to array
	$me->{mbuffer} = {};	# message, perl internal, ref to array
	$me->{rbuffer} = {};	# read flag, ref to array

	# 各チャネルの最終発言時刻
	$me->{mtime} = {};

	# timestamp of last posted message
	$me->{timestamp} = 0;

        bless $me;
}

################################################################
sub add_nick{
	my($me, $cid, $nick, $chop, $realname) = @_;
	$me->{nicks}->{$cid}->{$nick}->{realname} = $realname;
	$me->{nicks}->{$cid}->{$nick}->{chop} = $chop;
}

################################################################
sub list_nick{
	my($me, $cid, $nick, $chop, $realname) = @_;
	keys %{$me->{nicks}->{$cid}};
}

################################################################
sub remove_nick{
	my($me, $cid, $nick) = @_;
	delete $me->{nicks}->{$cid}->{$nick};
}

################################################################
sub get_nick_realname{
	my($me, $cid, $nick) = @_;
	$me->{nicks}->{$cid}->{$nick}->{realname};
}

################################################################
sub op_nick{
	my($me, $cid, $nick) = @_;
	if(defined $me->{nicks}->{$cid}){
		if(defined $me->{nicks}->{$cid}->{$nick}){
			$me->{nicks}->{$cid}->{$nick}->{chop} = 1;
		}
	}
}

################################################################
sub deop_nick{
	my($me, $cid, $nick) = @_;
	if(defined $me->{nicks}->{$cid}){
		if(defined $me->{nicks}->{$cid}->{$nick}){
			$me->{nicks}->{$cid}->{$nick}->{chop} = 0;
		}
	}
}

################################################################
sub get_nick_op{
	my($me, $cid, $nick) = @_;
	if(defined $me->{nicks}->{$cid}){
		if(defined $me->{nicks}->{$cid}->{$nick}){
			return $me->{nicks}->{$cid}->{$nick}->{chop};
		}
	}
}

################################################################
sub channels{
	my $me = shift;
	map {
		$_
	}(sort
	  {
		  $me->mtime($b) <=> $me->mtime($a)
	  } keys %{$me->{cid2name}});
}

################################################################
sub format_time{
	my ($me, $t) = @_;
	my ($sec, $min, $hour) = localtime($t);
	sprintf('%02d:%02d', $hour, $min);
}

################################################################
sub format_date{
	my ($me, $t) = @_;
	my ($sec, $min, $hour, $day, $month, $year) = localtime($t);
	sprintf('%04d/%02d/%02d', $year+1900, $month+1, $day);
}

################################################################
sub name2cid{
	my($me, $name) = @_;
	my $raw_name = $name;
	$name =~ tr/A-Z[\\]^/a-z{|}~/;

	unless(defined $me->{name2cid}->{$name}){
		my $cid = (sort { $b - $a } (keys %{$me->{cid2name}}))[0];
		$cid++;
		$me->{cid2name}->{$cid} = $raw_name;
		$me->{name2cid}->{$name} = $cid;
	}

	$me->{name2cid}->{$name};
}

################################################################
sub cid2name{
	my($me, $cid) = @_;
	$me->{cid2name}->{$cid};
}

################################################################
sub part{
	my($me, $cid) = @_;
	delete $me->{cid2name}->{$cid};
	delete $me->{name2cid}->{$cid};
	delete $me->{topic}->{$cid};
	delete $me->{nicks}->{$cid};
	delete $me->{tbuffer}->{$cid};
	delete $me->{nbuffer}->{$cid};
	delete $me->{mbuffer}->{$cid};
	delete $me->{rbuffer}->{$cid};
}

################################################################
sub join{
	my ($me, $name) = @_;
	my $cid = $me->name2cid($name);
}

################################################################
sub mtime{
	my($me, $cid) = @_;
	$me->{mtime}->{$cid} || 0;
}

################################################################
sub unread_lines{
	my($me, $cid) = @_;
	scalar grep(/0/, @{$me->{rbuffer}->{$cid}});
}

################################################################
sub topic{
	my($me, $cid, $topic) = @_;
	if(defined $topic){
		$me->{topic}->{$cid} = $topic;
	}
	$me->{topic}->{$cid};
}

################################################################
sub buffer_ptr{
	my($me, $cid) = @_;
	($me->{tbuffer}->{$cid}, $me->{nbuffer}->{$cid},
	 $me->{mbuffer}->{$cid}, $me->{rbuffer}->{$cid});
}

################################################################
# 引数の $msg の charset は perl internal
sub add_message{
	my($me, $cid, $message, $who) = @_;

	unless(defined $me->{tbuffer}->{$cid}){
		$me->{tbuffer}->{$cid} = [];
	}
	unless(defined $me->{nbuffer}->{$cid}){
		$me->{nbuffer}->{$cid} = [];
		}
	unless(defined $me->{mbuffer}->{$cid}){
		$me->{mbuffer}->{$cid} = [];
	}
	unless(defined $me->{rbuffer}->{$cid}){
		$me->{rbuffer}->{$cid} = [];
	}

	push @{$me->{tbuffer}->{$cid}}, time;
	push @{$me->{nbuffer}->{$cid}}, $who;
	push @{$me->{mbuffer}->{$cid}}, $message;
	push @{$me->{rbuffer}->{$cid}}, 0;

	if(@{$me->{tbuffer}->{$cid}} > $me->{history}){
		shift @{$me->{tbuffer}->{$cid}};
	}
	if(@{$me->{nbuffer}->{$cid}} > $me->{history}){
		shift @{$me->{nbuffer}->{$cid}};
	}
	if(@{$me->{mbuffer}->{$cid}} > $me->{history}){
		shift @{$me->{mbuffer}->{$cid}};
	}
	if(@{$me->{rbuffer}->{$cid}} > $me->{history}){
		shift @{$me->{rbuffer}->{$cid}};
	}

	if($me->{cid2name}->{$cid} eq '*console*') {
		$me->{mtime}->{$cid} = -1;
	} else {
		$me->{mtime}->{$cid} = time;
	}
}

################################################################
# チャネル名称を短かくする
# 返り値は Perl internal code
sub compact_channel_name{
	my $me = shift;
	my $cid = shift;
	my $name = $me->cid2name($cid);

	return undef unless defined $name;

	# #name:*.jp を %name に
	if($name =~ s/:\*\.jp$//){
		$name =~ s/^#/%/;
	}

	# 末尾の単独の @ は取る (plumプラグインのmulticast.plm対策)
	# @ の後に空白が入ることもあるようだ。理由はわからない。
	$name =~ s/\@\s*$//;
	$name;
}

################################################################
sub simple_escape{
	my $me = shift;
        local($_) = shift;
	if(defined $_){
		s/&/&amp;/g;
		s/>/&gt;/g;
		s/</&lt;/g;
		s/{/&#123;/g;
		s/}/&#125;/g;
		s/\+/&#043;/g;
	}
        $_;
}

################################################################
sub colorize{
	my $me = shift;
        local($_) = shift;

	my %ct = (
		1 => 'Black',
		2 => '#000080', # Navy Blue
		3 => 'Green',
		4 => 'Red',
		5 => 'Maroon',
		6 => 'Purple',
		7 => 'Olive',
		8 => 'Yellow',
		9 => '#32cd32', # Lime Green
		10 => 'Teal',
		11 => 'Aqua',
		12 => '#4169e1', # Royal Blue
		13 => '#ff69b4', # Hot Pink
		14 => '#a9a9a9', # Dark Gray
		15 => '#d3d3d3', # Light Gray
		16 => 'White');
	my $colored = 0;

	return undef unless defined $_;

	do{
		if($colored){
			s|\x03(\d{1,2})|sprintf('</font><font color="%s">', $ct{0+$1})|e;
			if(s|\x03|</font>|){
				$colored = 0;
			}
		}else{
			if(s|\x03(\d{1,2})|sprintf('<font color="%s">', $ct{0+$1})|e){
				$colored = 1;
			}
		}
	}while(m|\x03\d{1,2}|);

	if($colored){
		$_ .= '</font>';
	}

	$_;
}

################################################################
# 同一秒間の連続発言を防ぐためのチェック。
#
# 前回 update_timestamp() が呼ばれた時刻と同じ時刻に
# 再度 update_timestamp() が呼ばれたら 0 を返す。
#
# 前回 update_timestamp() が呼ばれた時刻と異なる時刻に
# 再度 update_timestamp() が呼ばれたら 1 を返す。
#
sub update_timestamp{
	my $me = shift;
	my $time = shift;

	if($me->{timestamp} != $time){
		$me->{timestamp} = $time;
		return 1;
	}

	return 0;
}

1;
