#! /usr/local/bin/perl -w

use strict;
use warnings;

use Digest::SHA qw(sha256);

use Getopt::Std;

my %opts;

getopts("c:N:n:", \%opts);

my @preamble = ();   # Preamble lines
my @postamble = ();  # Postamble lines
my @questions = ();  # Array of question hashrefs
# Each question hash has keys:
# {question}: arrayref with question lines
# {answers}:  arrayref of arrayrefs with answer lines, correct answer first
# {varid}:    idnex in qvars array
my @qvars = ();      # Array of question variants
# Each entry is an arrayref of indexes in the questions array


my $commonseed = $opts{N} // "";
my $seed = $opts{n} // "";

### READ INPUT FILE

if ( 1 ) { ## Keep following variables local

my $in_preamble = 1;
my $in_postamble = 0;
my $in_qvar = 0;
my $curqn;
my $listref = \@preamble;
my $varid;
LINELOOP:
while ($_ = <>) {
    if ( $_ =~ m/^\\def\\seedval\{.*\}$/ ) {
	$_ = "\\def\\seedval\{$seed\}\n";
    }
    if ( $_ =~ m/\\begin\{qcm\}/ ) {
	die "wrong placement" unless $in_preamble;
	die "bad format" unless $_ eq "\\begin\{qcm\}\n";
	$in_preamble = 0;
	$listref = undef;
	next LINELOOP;
    } elsif ( $_ =~ m/\\end\{qcm\}/ ) {
	die "wrong placement" if $in_preamble;
	die "bad format" unless $_ eq "\\end\{qcm\}\n";
	$in_postamble = 0;
	$listref = \@postamble;
	next LINELOOP;
    } elsif ( $_ =~ m/\\begin\{qvar\}/ ) {
	die "wrong placement" if $in_preamble || $in_postamble || $in_qvar || defined($curqn);
	die "bad format" unless $_ eq "\\begin\{qvar\}\n";
	$in_qvar = 1;
	push @qvars, [];
	next LINELOOP;
    } elsif ( $_ =~ m/\\end\{qvar\}/ ) {
	die "wrong placement" if $in_preamble || $in_postamble || (!$in_qvar) || defined($curqn);
	die "bad format" unless $_ eq "\\end\{qvar\}\n";
	$in_qvar = 0;
	next LINELOOP;
    } elsif ( $_ =~ m/\\begin\{question\}/ ) {
	die "wrong placement" if $in_preamble || $in_postamble || defined($curqn);
	die "bad format" unless $_ eq "\\begin\{question\}\n";
	my %qn = ();
	push @qvars, [] unless $in_qvar;
	$qn{varid} = $#qvars;
	$qn{question} = [];
	$qn{answers} = [];
	push @questions, \%qn;
	push @{$qvars[$#qvars]}, $#questions;
	$listref = $qn{question};
	$curqn = \%qn;
	next LINELOOP;
    } elsif ( $_ =~ m/\\end\{question\}/ ) {
	die "wrong placement" if $in_preamble || $in_postamble || !defined($curqn);
	die "bad format" unless $_ eq "\\end\{question\}\n";
	$listref = undef;
	$curqn = undef;
	next LINELOOP;
    } elsif ( $_ =~ m/\\(right)?answer/
	      && $_ !~ /\\newcommand/ && $_ !~ /\\let\\rightanswer/ ) {
	die "wrong placement" if $in_preamble || $in_postamble || !defined($curqn);
	die "bad format" unless $_ eq "\\answer\n" || $_ eq "\\rightanswer\n";
	die "this is impossible" unless ref($curqn) eq "HASH" && defined $curqn->{answers};
	die "right answer should come first" unless ($_ eq "\\rightanswer\n") == (scalar(@{$curqn->{answers}}) == 0);
	my @ans = ();
	push @{$curqn->{answers}}, \@ans;
	$listref = \@ans;
	## no next LINELOOP here: include \answer in answer itself!
    }
    die "this is impossible" if $in_preamble && $in_postamble;
    die "this is impossible" if $in_preamble && ($listref ne \@preamble);
    die "this is impossible" if $in_postamble && ($listref ne \@postamble);
    push @{$listref}, $_ if defined($listref);
}

}


### RANDOMIZE

my $nbqn = $opts{c} // int((scalar(@qvars)+1)/2);

my @questab = ();
my @quesanstab = ();

if ( 1 ) { ## Keep following variables local

my @hashlist;

for ( my $u=0 ; $u<scalar(@qvars) ; $u++ ) {
    push @hashlist, sha256("${commonseed}\n${seed}\nQV\n${u}\n");
}
my @qvartab = sort { $hashlist[$a] cmp $hashlist[$b] } (0..(scalar(@qvars)-1));

for ( my $k=0 ; $k<scalar(@qvartab) && $k<$nbqn ; $k++ ) {
    my $u = $qvartab[$k];
    @hashlist = ();
    for ( my $kv=0 ; $kv<scalar(@{$qvars[$u]}) ; $kv++ ) {
	my $i = $qvars[$u]->[$kv];
	push @hashlist, sha256("${commonseed}\n${seed}\nQ\n${i}\n");
    }
    my $kv = (sort { $hashlist[$a] cmp $hashlist[$b] } (0..(scalar(@{$qvars[$u]})-1)))[0];
    my $i = $qvars[$u]->[$kv];
    die "this is impossible" unless $questions[$i]->{varid} == $u;
    push @questab, $i;
}

for ( my $i=0 ; $i<scalar(@questions) ; $i++ ) {
    @hashlist = ();
    my $r = $questions[$i]->{answers};
    for ( my $j=0 ; $j<scalar(@$r) ; $j++ ) {
	push @hashlist, sha256("${commonseed}\n${seed}\nQ\n${i}\nA\n${j}\n");
    }
    my @anstab;
    @anstab = sort { $hashlist[$a] cmp $hashlist[$b] } (0..(scalar(@$r)-1));
    push @quesanstab, \@anstab;
}

}


### WRITE OUTPUT FILE

my @correct;

foreach my $l ( @preamble ) {
    print $l;
}

print "\\begin\{qcm\}\n\n";

for ( my $k=0 ; $k<scalar(@questab) ; $k++ ) {
    my $i = $questab[$k];
    my $qn = $questions[$i];
    print "\\begin\{question\}\n";
    foreach my $l ( @{$qn->{question}} ) {
	print $l;
    }
    for ( my $kj=0 ; $kj<scalar(@{$quesanstab[$i]}) ; $kj++ ) {
	my $j = $quesanstab[$i]->[$kj];
	my $a = $qn->{answers}->[$j];
	foreach my $l ( @{$a} ) {
	    print $l;
	}
	push @correct, sprintf("%d%s", $k+1, chr(ord("A")+$kj)) if $j==0;
    }
    print "\\end\{question\}\n\n";
}

print "\\end\{qcm\}\n\n";

printf "\%\% === %s ===\n", join(" ", @correct);
printf "\\ifcorrige\\bigskip\\noindent\\textbf{Corrigé.} %s\\fi\n", join(" ", @correct);

foreach my $l ( @postamble ) {
    print $l;
}