################################################
## irssi quiz (iQuiz) script by wilk/xorandor ##
################################################

# Quiz accepts:
# - standard Dizzy files (also without "pyt"/"odp" prefixes):
#
# pyt Question_1
# odp Answer_1
# pyt Question_2
# odp Answer_2
# ...
#
# - standard Mieszacz files (also without line numbers):
#
# 1 alpha
# 2 beta
# 3 gamma
# 4 delta
# ...
#
# - standard Familiada files (can have any number of answers per question):
#
# Question_1
# Answer_1*Answer_2*Answer_3
# Question_2
# Answer_1*Answer_2*Answer_3
# ...

# >>> To check quiz commands and user settings type: /quiz

use strict;
use warnings;
use vars qw($VERSION %IRSSI);
use Irssi;
use Time::HiRes qw(time);
use constant { QT_DIZZY => 1, QT_MIESZACZ => 2, QT_FAMILIADA => 3, QT_MILIONERZY => 4, QT_FORTUNA => 5};

$VERSION = '131021';
%IRSSI = (
	authors 		=> 'wilk',
	name 			=> 'iQuiz',
	description 	=> 'irssi quiz script',
	license 		=> 'GNU GPL v3 or any later version',
	changed 		=> $VERSION,
	url 			=> 'http://www.quizpl.net/pliki/quiz.pl'
);

##### Internal stuff #####
my %quiz = (
	chanrec => undef,
	type => 0,
	ison => 0, inq => 0,
	stime => 0, qtime => 0,
	qcnt => 0, qnum => 0, hnum => 0,
	tnext => undef, tround => undef, thint => undef, tremind => undef,
	hprot => 0, rprot => 0,
	data => [],
	players => {},
	dots => [], hwords => [], words => []
);

##### Theme (only channel messages are localized by default, feel free to customize, except authorship) #####
# quiz_inf_* & quiz_err_* messages are irssi only - use irssi color codes
# quiz_msg_* messages are sent on channel - use mIRC color codes:
# \002 - bold  \003$fg(,$bg)? - color  \017 - plain  \026 - reverse  \037 - underline
Irssi::theme_register([
	'quiz_inf_start', 	'%_iQuiz:%_ For help type: /quiz',
	'quiz_inf_delay', 	'%_iQuiz:%_ %gChanged delay to: $0%n',
	'quiz_inf_time', 	'%_iQuiz:%_ %gChanged round length to: $0%n',
	'quiz_inf_type', 	'%_iQuiz:%_ %gChanged quiz type to: $0%n',

	'quiz_err_ison', 		'%_iQuiz:%_ %RQuiz is already on%n',
	'quiz_err_isoff', 		'%_iQuiz:%_ %RQuiz is already off%n',
	'quiz_err_server', 		'%_iQuiz:%_ %RNot connected to server%n',
	'quiz_err_channel', 	'%_iQuiz:%_ %RInvalid channel%n',
	'quiz_err_nochannel', 	'%_iQuiz:%_ %RChannel $0 is not open%n',
	'quiz_err_filename', 	'%_iQuiz:%_ %RInvalid filename%n',
	'quiz_err_nofile', 		'%_iQuiz:%_ %RFile \'$0\' not found%n',
	'quiz_err_file', 		'%_iQuiz:%_ %RFile \'$0\' seems to be broken%n',
	'quiz_err_argument', 	'%_iQuiz:%_ %RInvalid argument%n',
	'quiz_err_noquestion', 	'%_iQuiz:%_ %RNo question asked%n',
	'quiz_err_type', 		'%_iQuiz:%_ %RInvalid quiz type%n',
	'quiz_err_na', 			'%_iQuiz:%_ %RFeature not available%n',

	'quiz_msg', 				"%s", # le trick (custom text)
	'quiz_msg_start1', 			"\00303>>> \00310iQuiz by wilk wystartowal \00303<<<",
	'quiz_msg_start2', 			"\00303Polecenia: !podp, !przyp, !ile, !ile nick",
	'quiz_msg_start2_2', 		"\00303Polecenia: !przyp, !ile, !ile nick",
	'quiz_msg_stop1', 			"\00303>>> \00310iQuiz zakonczony \00303<<<",
	'quiz_msg_stop2', 			"\00303Ilosc rund: \00304%u \00303Czas gry: \00304%s",
	'quiz_msg_question', 		"\00303\037Pytanie %u/%u:\037 \00304%s",
	'quiz_msg_question_2', 		"\00303\037Haslo %u/%u:\037 \00304%s",
	'quiz_msg_hint', 			"\00303Podpowiedz: \00304%s",
	'quiz_msg_dot', 			"\00310.\00304",
	'quiz_msg_remind', 			"\00303Przypomnienie pytania: \00304%s",
	'quiz_msg_remind_2', 		"\00303Przypomnienie hasla: \00304%s",
	'quiz_msg_delay', 			"\00303Opoznienie miedzy pytaniami: \00304%s",
	'quiz_msg_time', 			"\00303Czas trwania rundy: \00304%s",
	'quiz_msg_score', 			"\00304%s\00303\002\002, zdobyles(as) jak dotad \00304%u\00303 %s!",
	'quiz_msg_noscore', 		"\00304%s\00303\002\002, nie zdobyles(as) jeszcze zadnego punktu!",
	'quiz_msg_scorex', 			"\00304%s\00303 zdobyl(a) jak dotad \00304%u\00303 %s!",
	'quiz_msg_noscorex', 		"\00304%s\00303 nie zdobyl(a) jeszcze zadnego punktu!",
	'quiz_msg_noscores', 		"\00303Tablica wynikow jest jeszcze pusta.",
	'quiz_msg_scores', 			"\00303Wyniki quizu po \00304%s\00303:",
	'quiz_msg_scores_rank', 	"\00303%u. miejsce: \00304%s\00303 - \00304%u\00303 %s",
	'quiz_msg_congrats', 		"\00303Brawo, \00304%s\00303! Dostajesz %s za odpowiedz \00304%s\00303 podana po czasie %s Suma punktow: \00304%u\00303.",
	'quiz_msg_all_answers', 	"\00307Wszystkie odpowiedzi zostaly odgadniete.",
	'quiz_msg_timeout', 		"\00307Czas uplynal.",
	'quiz_msg_got_points', 		"\00304%u\00303 %s",
	'quiz_msg_got_point', 		"\00303%s",
	'quiz_msg_next', 			"\00303Nastepne pytanie za %s..",
	'quiz_msg_next_2', 			"\00303Nastepne haslo za %s..",
	'quiz_msg_last', 			"\00307Koniec pytan!",
	'quiz_msg_skipped', 		"\00303Pytanie zostalo pominiete.",
	'quiz_msg_point', 			"punkt",	# 1 point 			/ 1 punkt
	'quiz_msg_points_234', 		"punkty",	# 2-4, ?2-?4 points	/ 2-4, ?2-?4 punkty
	'quiz_msg_points_234_1x', 	"punktow",	# 11-14 points		/ 11-14 punktow
	'quiz_msg_points', 			"punktow",	# x points			/ x punktow
	'quiz_msg_hours', 			"%u godz.",
	'quiz_msg_minutes', 		"%u min.",
	'quiz_msg_seconds', 		"%u sek.",
	'quiz_msg_seconds_ms', 		"%.3f sek.",
]);

##### Support routines #####
sub load_quiz {
	undef @{$quiz{data}};
	$quiz{qcnt} = 0;
	local *F;
	my $lines = 0;
	if (open(F, shift)) {
		while (<F>) {
			s/[\n\r]//g;
			next if (/^$/);
			if ($quiz{type} == QT_DIZZY) {
				if ($lines % 2) {
					s/^odp //i;
					$quiz{qcnt}++; # ++ only on complete question
					$quiz{data}[$quiz{qcnt}]{answer} = $_;
				} else {
					s/^pyt //i;
					$quiz{data}[$quiz{qcnt} + 1]{question} = $_;
				};
			} elsif ($quiz{type} == QT_MIESZACZ) {
				s/^\d+ //;
				$quiz{qcnt}++;
				$quiz{data}[$quiz{qcnt}]{answer} = $_;
			} elsif ($quiz{type} == QT_FAMILIADA) {
				if ($lines % 2) {
					$quiz{qcnt}++; # ++ only on complete question
					my $pos = 1;
					%{$quiz{data}[$quiz{qcnt}]{answers}} = map { $_ => $pos++ } split /\*/;
				} else {
					$quiz{data}[$quiz{qcnt} + 1]{question} = $_;
				};
			}
			$lines++;
		}
		close F;
	}
	return $lines;
}

sub get_format {
	my ($tag, $args) = @_;
	$tag = $1 if ($tag =~ /^!(.+)/);
	my @args = (ref $args) ? @{$args} : ();
	return ($tag eq '') ? $args : sprintf(Irssi::current_theme()->get_format('Irssi::Script::quiz', $tag), @args);
}

sub send_msg {
	my ($tag) = @_;
	my $msg = get_format(@_);
	if ($quiz{chanrec}{server}{connected}) {
		if ($tag =~ /^!/) { # '!' prefix is for instant messages, otherwise queued
			$quiz{chanrec}{server}->send_raw_now("PRIVMSG $quiz{chanrec}{name} :$msg");
		} else {
			$quiz{chanrec}{server}->send_raw("PRIVMSG $quiz{chanrec}{name} :$msg");
		}
		Irssi::timeout_add_once(100, 'evt_delayed_show_msg', $msg); # le trick (fix for chantext after owntext)
	} else {
		print CLIENTCRAP $msg;
		Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_server');
	}
}

sub shuffle {
	my ($old, $new, $trap) = (shift, '', 10); # $trap is a watchdog
	do {
		my @letters = split(//, $old);
		$new = '';
		while (@letters) {
			$new .= splice(@letters, int(rand($#letters + 1)), 1);
		}
	} until (($old ne $new) || ($trap-- == 0));
	return $new;
}

sub confuse {
	(my $text = shift) =~ s/ //g;
	return shuffle($text);
}

sub make_hint {
	if (!@{$quiz{dots}}) { # make dots
		my $w = 0;
		foreach my $word (@{$quiz{words}}) {
			my @letters = split(//, $word);
			my $l = 0;
			my $hword = '';
			foreach my $letter (@letters) {
				if ($letter =~ /[a-z0-9]/i) {
					push(@{$quiz{dots}[$w]}, $l);
					$hword .= "\002"; # le trick (any non-ASCII char)
				} else {
					$hword .= $letter;
				}
				$l++;
			}
			push(@{$quiz{hwords}}, $hword);
			$w++;
		}
	}
	$quiz{hnum}++;
	my $first_dots = Irssi::settings_get_bool('quiz_first_hint_dots');
	if (!$first_dots || ($first_dots && $quiz{hnum} > 1)) { # reveal some dots
		my $w = 0;
		foreach my $rwdots (@{$quiz{dots}}) {
			if (ref $rwdots) {
				if (@{$rwdots} > 0) {
					my @letters = split(//, $quiz{words}[$w]);
					my @hletters = split(//, $quiz{hwords}[$w]);
					my $sel = Irssi::settings_get_bool('quiz_random_hints') ? int(rand(@{$rwdots})) : 0;
					$hletters[@{$rwdots}[$sel]] = $letters[@{$rwdots}[$sel]];
					$quiz{hwords}[$w] = join('', @hletters);
					splice(@{$rwdots}, $sel, 1);
				}
			}
			$w++;
		}
	}
	my $hint = join(' ', @{$quiz{hwords}});
	my $dot = get_format('quiz_msg_dot');
	$hint =~ s/\002/$dot/g; # le trick grande finale
	if (my ($scol, $ecol) = ($dot =~ /^(\003\d+).(\003\d+)$/)) {
		$hint =~ s/$ecol $scol/ /g; # reduce bloat
		$hint =~ s/$ecol$scol//g; # reduce bloat
	}
	return $hint;
}

sub time_str {
	my ($time, $mode) = @_; # $mode - 0: h/m/s, 1: force s, 2: hires s
	my $h = ($time && !$mode) ? int($time / 3600) : 0;
	my $m = ($time && !$mode) ? int($time / 60) - $h * 3600 : 0;
	my $s = $time - $m * 60;
	return ($h ? get_format('quiz_msg_hours', [$h]).' ' : '').($m ? get_format('quiz_msg_minutes', [$m]). ' ' : '').get_format('quiz_msg_seconds'.($mode == 2 ? '_ms' : ''), [$s]);
}

sub ord_score {
	my $value = abs(shift);
	if ($value != 1) {
		my @value = split(//, "$value");
		if (($value[$#value] > 1) && ($value[$#value] < 5)) {
			return get_format('quiz_msg_points_234'.($value[$#value - 1] == 1 ? '_1x' : ''));
		} else {
			return get_format('quiz_msg_points');
		}
	}
	return get_format('quiz_msg_point');
}

sub stop_quiz {
	my $fullstop = shift; # $fullstop - 0: stop question, 1: stop quiz
	@quiz{qw/inq hprot rprot/} = (0, 0, 0);
	Irssi::timeout_remove($quiz{tround}), $quiz{tround} = undef if ($quiz{tround});
	Irssi::timeout_remove($quiz{thint}), $quiz{thint} = undef if ($quiz{thint});
	Irssi::timeout_remove($quiz{tremind}), $quiz{tremind} = undef if ($quiz{tremind});
	if (defined $fullstop) {
		$quiz{ison} = 0;
		Irssi::timeout_remove($quiz{tnext}), $quiz{tnext} = undef if ($quiz{tnext});
		Irssi::signal_remove('message public', 'sig_pubmsg');
	}
}

sub init_next_question {
	my ($msg, $now) = @_;
	if ($quiz{qnum} >= $quiz{qcnt}) {
		send_msg(($now ? '!' : '').'quiz_msg', ["$msg ".get_format('quiz_msg_last')]);
	} else {
		my $delay = Irssi::settings_get_int('quiz_delay') * ($quiz{type} == QT_FAMILIADA ? 3 : 1);
		send_msg(($now ? '!' : '').'quiz_msg', ["$msg ".get_format('quiz_msg_next'.($quiz{type} == QT_MIESZACZ ? '_2' : ''), [time_str($delay, 1)])]);
		$quiz{tnext} = Irssi::timeout_add_once($delay * 1000, 'evt_next_question', undef);
	}
}

##### Commands' handlers #####
sub cmd_start {
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_ison'), return if ($quiz{ison});
	my ($args, $server, $window) = @_;
	my ($chan, $file) = split(/ /, $args);
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_server'), return if (!$server || !$server->{connected});
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_channel'), return if (!$chan || !$server->ischannel($chan));
	$quiz{chanrec} = $server->channel_find($chan);
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_nochannel', $chan), return if (!$quiz{chanrec});
	$file =~ s/^~\/// if ($file =~ /^~\//); # open does not support "~/"?
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_filename'), return if (!$file);
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_nofile', $file), return if (!-e $file);
	my $type = Irssi::settings_get_int('quiz_type');
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_type'), return if (($type < 1) || ($type > 3));
	$quiz{type} = $type;
	my $lines = load_quiz($file);
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_file', $file), return if (($quiz{qcnt} < 1) || ((($type == QT_DIZZY) || ($type == QT_FAMILIADA)) && ($quiz{qcnt} * 2 != $lines)));
	undef %{$quiz{players}};
	send_msg('!quiz_msg_start1');
	send_msg('!quiz_msg_start2'.($quiz{type} == QT_FAMILIADA ? '_2' : ''));
	@quiz{qw/stime qnum ison/} = (time(), 0, 1);
	Irssi::signal_add_last('message public', 'sig_pubmsg');
	$quiz{tnext} = Irssi::timeout_add_once(Irssi::settings_get_int('quiz_delay') * 1000, 'evt_next_question', undef);
}

sub cmd_stat {
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_isoff'), return if (!$quiz{ison});
	my ($num) = @_;
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_argument'), return if ($num && ($num !~ /^\d+$/));
	$num = 0 if (!$num);
	if (!keys %{$quiz{players}}) {
		send_msg('quiz_msg_noscores');
	} else {
		send_msg('quiz_msg_scores', [time_str(time() - $quiz{stime}, 0)]);
		my $rank = 1;
		foreach my $it (sort {
							return $quiz{players}{$b}{score} <=> $quiz{players}{$a}{score} if ($quiz{players}{$a}{score} != $quiz{players}{$b}{score});
							return $quiz{players}{$a}{ts} <=> $quiz{players}{$b}{ts};
						} keys %{$quiz{players}}) {
			send_msg('quiz_msg_scores_rank', [$rank, $quiz{players}{$it}{nick}, $quiz{players}{$it}{score}, ord_score($quiz{players}{$it}{score})]);
			last if ($rank == $num);
			$rank++;
		}
	}
}

sub cmd_delay {
	my ($delay) = @_;
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_argument'), return if (!$delay || ($delay !~ /^\d+$/) || $delay < 1);
	Irssi::settings_set_int('quiz_delay', $delay);
	send_msg('quiz_msg_delay', [time_str($delay * ($quiz{type} == QT_FAMILIADA ? 3 : 1), 1)]) if ($quiz{ison});
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_inf_delay', $delay);
}

sub cmd_time {
	my ($time) = @_;
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_argument'), return if (!$time || ($time !~ /^\d+$/) || $time < 1);
	Irssi::settings_set_int('quiz_round_time', $time);
	send_msg('quiz_msg_time', [time_str($time, 1)]) if ($quiz{ison});
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_inf_time', $time);
}

sub cmd_mode {
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_ison'), return if ($quiz{ison});
	my ($type) = @_;
	if ($type) {
		Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_argument'), return if (($type !~ /^\d+$/) || ($type < 1) || ($type > 3));
	} else {
		$type = (Irssi::settings_get_int('quiz_type') % 3) + 1;
	}
	Irssi::settings_set_int('quiz_type', $type);
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_inf_type', ('Dizzy', 'Mieszacz', 'Familiada')[$type - 1]);
}

sub cmd_skip {
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_isoff'), return if (!$quiz{ison});
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_noquestion'), return if (!$quiz{inq});
	stop_quiz();
	init_next_question(get_format('quiz_msg_skipped'), 0);
}

sub cmd_hint {
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_isoff'), return if (!$quiz{ison});
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_na'), return if ($quiz{type} == QT_FAMILIADA);
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_noquestion'), return if (!$quiz{inq});
	send_msg('quiz_msg_hint', [make_hint()]);
}

sub cmd_stop {
	Irssi::printformat(MSGLEVEL_CRAP, 'quiz_err_isoff'), return if (!$quiz{ison});
	stop_quiz(1);
	send_msg('quiz_msg_stop1');
	send_msg('quiz_msg_stop2', [$quiz{qnum}, time_str(time() - $quiz{stime}, 0)]);
}

sub cmd_help {
	print CLIENTCRAP "$IRSSI{name} v$VERSION by wilk (quiz is currently: ".($quiz{ison} ? 'on' : 'off').") [Perl: $]]";
	print CLIENTCRAP 'Available commands:';
	print CLIENTCRAP '   /qtype [1-3]            - change quiz type or select next one';
	print CLIENTCRAP '   /qon <channel> <path>   - start quiz from file';
	print CLIENTCRAP '   /qstat [num]            - show top x players or all of them';
	print CLIENTCRAP '   /qhint                  - enforce next hint';
	print CLIENTCRAP '   /qskip                  - skip current question';
	print CLIENTCRAP '   /qdelay <sec>           - change delay between questions';
	print CLIENTCRAP '   /qtime <sec>            - change round time (familiada)';
	print CLIENTCRAP '   /qoff                   - stop quiz';
	print CLIENTCRAP 'Available settings (/set):';
	print CLIENTCRAP '   quiz_type               : '.sprintf('%-5u', Irssi::settings_get_int('quiz_type')).' - quiz type (1 - dizzy, 2 - mieszacz, 3 - familiada)';
	print CLIENTCRAP '   quiz_delay              : '.sprintf('%-5u', Irssi::settings_get_int('quiz_delay')).' - delay between questions (sec; familiada: x3)';
	print CLIENTCRAP '   quiz_round_time         : '.sprintf('%-5u', Irssi::settings_get_int('quiz_round_time')).' - length of round (sec; familiada)';
	print CLIENTCRAP '   quiz_anticheat_delay    : '.sprintf('%-5u', Irssi::settings_get_int('quiz_anticheat_delay')).' - hint/remind protection delay (sec; 0 - off)';
	print CLIENTCRAP '   quiz_points_per_answer  : '.sprintf('%-5u', Irssi::settings_get_int('quiz_points_per_answer')).' - points for correct answer';
	print CLIENTCRAP '   quiz_progressive_points : '.sprintf('%-5s', Irssi::settings_get_bool('quiz_progressive_points') ? 'true' : 'false').' - use progressive scoring? (familiada)';
	print CLIENTCRAP '   quiz_random_hints       : '.sprintf('%-5s', Irssi::settings_get_bool('quiz_random_hints') ? 'true' : 'false').' - reveal random letters or from left to right? (dizzy alike)';
	print CLIENTCRAP '   quiz_show_first_hint    : '.sprintf('%-5s', Irssi::settings_get_bool('quiz_show_first_hint') ? 'true' : 'false').' - show hint just after question?';
	print CLIENTCRAP '   quiz_first_hint_dots    : '.sprintf('%-5s', Irssi::settings_get_bool('quiz_first_hint_dots') ? 'true' : 'false').' - show first hint as dots only?';
	print CLIENTCRAP '   quiz_mix_on_remind      : '.sprintf('%-5s', Irssi::settings_get_bool('quiz_mix_on_remind') ? 'true' : 'false').' - scramble letters every remind? (mieszacz)';

	print CLIENTCRAP '   quiz_cmd_hint           : '.sprintf('%-5s', Irssi::settings_get_bool('quiz_cmd_hint') ? 'true' : 'false').' - is !hint command enabled?';
	print CLIENTCRAP '   quiz_cmd_remind         : '.sprintf('%-5s', Irssi::settings_get_bool('quiz_cmd_remind') ? 'true' : 'false').' - is !remind command enabled?';
}

##### Timers' events #####
sub evt_delayed_show_msg {
	Irssi::signal_emit('message own_public', $quiz{chanrec}{server}, shift, $quiz{chanrec}{name});
}

sub evt_next_question {
	@quiz{qw/qtime hnum/} = (time(), 0);
	$quiz{qnum}++;
	$quiz{data}[$quiz{qnum}]{question} = confuse($quiz{data}[$quiz{qnum}]{answer}) if ($quiz{type} == QT_MIESZACZ);
	send_msg('!quiz_msg_question'.($quiz{type} == QT_MIESZACZ ? '_2' : ''), [$quiz{qnum}, $quiz{qcnt}, $quiz{data}[$quiz{qnum}]{question}]);
	if ($quiz{type} == QT_FAMILIADA) {
		$quiz{tround} = Irssi::timeout_add_once(Irssi::settings_get_int('quiz_round_time') * 1000, 'evt_round_timeout', undef);
	} else {
		undef @{$quiz{dots}};
		undef @{$quiz{hwords}};
		@{$quiz{words}} = split(/ /, $quiz{data}[$quiz{qnum}]{answer});
		send_msg('quiz_msg_hint', [make_hint()]) if (Irssi::settings_get_bool('quiz_show_first_hint'));
	}
	$quiz{inq} = 1;
}

sub evt_round_timeout {
	stop_quiz();
	init_next_question(get_format('quiz_msg_timeout'), 1);
}

##### Signals' handlers #####
sub sig_pubmsg {
	my ($server, $msg, $nick, $addr, $target) = @_;
	return if (!$quiz{ison} || $server->{tag} ne $quiz{chanrec}{server}{tag} || lc $target ne lc $quiz{chanrec}{name});
	if (lc $msg =~ /^!ile( ([^ ]+))?/) {
		my $who = $2;
		if ($who) {
			my $found = 0;
			foreach my $it (keys %{$quiz{players}}) {
				if (lc $quiz{players}{$it}{nick} eq lc $who) {
					send_msg('quiz_msg_scorex', [$quiz{players}{$it}{nick}, $quiz{players}{$it}{score}, ord_score($quiz{players}{$it}{score})]);
					$found = 1;
					last;
				}
			}
			send_msg('quiz_msg_noscorex', [$who]) if (!$found);
		} else {
			if ($quiz{players}{$addr}{score}) {
				send_msg('quiz_msg_score', [$nick, $quiz{players}{$addr}{score}, ord_score($quiz{players}{$addr}{score})]);
			} else {
				send_msg('quiz_msg_noscore', [$nick]);
			}
		}
	}
	return if (!$quiz{inq});
	if ((lc $msg eq '!podp') && Irssi::settings_get_bool('quiz_cmd_hint') && ($quiz{type} != QT_FAMILIADA)) {
		if (!$quiz{hprot}) {
			send_msg('quiz_msg_hint', [make_hint()]);
			my $delay = Irssi::settings_get_int('quiz_anticheat_delay');
			if ($delay) {
				$quiz{hprot} = 1;
				$quiz{thint} = Irssi::timeout_add_once($delay * 1000, sub { $quiz{hprot} = 0; }, undef);
			}
		}
	} elsif ((lc $msg eq '!przyp') && Irssi::settings_get_bool('quiz_cmd_remind')) {
		if ($quiz{type} == QT_MIESZACZ) {
			if (!$quiz{rprot}) {
				$quiz{data}[$quiz{qnum}]{question} = confuse($quiz{data}[$quiz{qnum}]{answer}) if (Irssi::settings_get_bool('quiz_mix_on_remind'));
				send_msg('quiz_msg_remind_2', [$quiz{data}[$quiz{qnum}]{question}]);
				my $delay = Irssi::settings_get_int('quiz_anticheat_delay');
				if ($delay && Irssi::settings_get_bool('quiz_mix_on_remind')) {
					$quiz{rprot} = 1;
					$quiz{tremind} = Irssi::timeout_add_once($delay * 1000, sub { $quiz{rprot} = 0; }, undef);
				}
			}
		} else {
			send_msg('quiz_msg_remind', [$quiz{data}[$quiz{qnum}]{question}]);
		}
	} elsif ($quiz{type} == QT_FAMILIADA) {
		my %lookup = map { lc $_ => $_ } keys %{$quiz{data}[$quiz{qnum}]{answers}};
		if (exists($lookup{lc $msg}) && ($quiz{data}[$quiz{qnum}]{answers}{$lookup{lc $msg}} > 0)) {
			my $ts = time();
			my $points = Irssi::settings_get_int('quiz_points_per_answer');
			@{$quiz{players}{$addr}}{qw/nick ts/} = ($nick, $ts);
			if (Irssi::settings_get_bool('quiz_progressive_points')) {
				$points *= (scalar keys %{$quiz{data}[$quiz{qnum}]{answers}}) - $quiz{data}[$quiz{qnum}]{answers}{$lookup{lc $msg}} + 1;
			}
			$quiz{players}{$addr}{score} += $points;
			send_msg('quiz_msg_congrats', [$nick, ($points > 1) ? get_format('quiz_msg_got_points', [$points, ord_score($points)]) : get_format('quiz_msg_got_point', [ord_score($points)]), $lookup{lc $msg}, time_str($ts - $quiz{qtime}, 2), $quiz{players}{$addr}{score}]);
			$quiz{data}[$quiz{qnum}]{answers}{$lookup{lc $msg}} = -$quiz{data}[$quiz{qnum}]{answers}{$lookup{lc $msg}};
			if (!grep { $_ > 0 } values %{$quiz{data}[$quiz{qnum}]{answers}}) {
				stop_quiz();
				init_next_question(get_format('quiz_msg_all_answers'), 1);
			}
		}
	} elsif (lc $msg eq lc $quiz{data}[$quiz{qnum}]{answer}) {
		stop_quiz();
		my $ts = time();
		my $points = Irssi::settings_get_int('quiz_points_per_answer');
		@{$quiz{players}{$addr}}{qw/nick ts/} = ($nick, $ts);
		$quiz{players}{$addr}{score} += $points;
		init_next_question(get_format('quiz_msg_congrats', [$nick, ($points > 1) ? get_format('quiz_msg_got_points', [$points, ord_score($points)]) : get_format('quiz_msg_got_point', [ord_score($points)]), $quiz{data}[$quiz{qnum}]{answer}, time_str($ts - $quiz{qtime}, 2), $quiz{players}{$addr}{score}]), 1);
	}
}

##### Bindings #####
Irssi::command_bind('quiz', 	'cmd_help');
Irssi::command_bind('qtype', 	'cmd_mode');
Irssi::command_bind('qon', 		'cmd_start');
Irssi::command_bind('qdelay', 	'cmd_delay');
Irssi::command_bind('qtime', 	'cmd_time');
Irssi::command_bind('qstat', 	'cmd_stat');
Irssi::command_bind('qhint', 	'cmd_hint');
Irssi::command_bind('qskip', 	'cmd_skip');
Irssi::command_bind('qoff', 	'cmd_stop');

##### User settings #####
Irssi::settings_add_int($IRSSI{name}, 	'quiz_type', 1);
Irssi::settings_add_int($IRSSI{name}, 	'quiz_delay', 7);
Irssi::settings_add_int($IRSSI{name}, 	'quiz_anticheat_delay', 3);
Irssi::settings_add_int($IRSSI{name}, 	'quiz_round_time', 120);
Irssi::settings_add_int($IRSSI{name}, 	'quiz_points_per_answer', 1);
Irssi::settings_add_bool($IRSSI{name}, 	'quiz_progressive_points', 1);
Irssi::settings_add_bool($IRSSI{name}, 	'quiz_random_hints', 1);
Irssi::settings_add_bool($IRSSI{name}, 	'quiz_show_first_hint', 0);
Irssi::settings_add_bool($IRSSI{name}, 	'quiz_first_hint_dots', 1);
Irssi::settings_add_bool($IRSSI{name}, 	'quiz_mix_on_remind', 1);
Irssi::settings_add_bool($IRSSI{name}, 	'quiz_cmd_hint', 1);
Irssi::settings_add_bool($IRSSI{name}, 	'quiz_cmd_remind', 1);

Irssi::printformat(MSGLEVEL_CRAP, 'quiz_inf_start');
