#!/usr/bin/perl


use strict;
use Data::Dumper;
use Switch;
use XML::Simple;
use Getopt::Long;
use utf8;

use lib "/usr/local/bin";
use l3config;

our $XMLClass;
our $XMLCourse;
our @Labs;

our %Machines;          # Machines list from class.xml
our @SelectedMachines;      # Machines list given as the command line argument

our $Config_File = "labmaker.conf";
our %Config_ = (
    "show_host"     => "no",

    # Вспомогательные программы
    #"l3-report"    => "./lm-report",
    "l3-report" => "./l3-report",

    # Каталоги
    "path_lilalo" => "/var/lilalo/",
    "path_classes"  => "/var/lilalo/classes/",
    "path_lablogs"  => "/var/lilalo/lablogs/",
    "courses_path"  => "/var/lilalo/courses/",
    "outpath"   => "/var/lilalo/out/",
    "path_web"  => "/var/www/l3",       # Путь к web-отчётам
    "path_share"    => "./share/",      # Путь к web-отчётам

    # Файлы
    "runfile"   => "lm.run", 
    "logfile"   => "lm.log", 

    "class"     => "class",                 # Имя файла класса
    "class_suffix"  => ".xml",              # Cуффикс файла класса
    "classfile" => "",

    "sshkey"    => "$ENV{HOME}/.ssh/id_dsa.pub",
    "lmssh"     => "./lm-ssh",
    "lminstall" => "./lm-install",
    "ssh_user"  => "root",
);

our %Run = (
    "lab" => ""
);

our %Scripts;

sub load_class;
sub load_config;
sub load_course;
sub load_scripts;

sub lm_get;
sub lm_next;
sub lm_prev;
sub lm_start;
sub lm_stop;
sub lm_set;
sub lm_do;
sub lm_report;
sub lm_show_hosts;
sub lm_show_email;
sub lm_show_labs;

sub load_run;
sub save_run;
sub print_log;
sub print_usage_info;
sub main();

main();

sub main()
{
    binmode STDOUT, ":utf8";

    if (! @ARGV) {
        print_usage_info();
        exit(0);
    }

    if ($ARGV[0] eq "get") {
         lm_get;
         exit(0);
    }

    init_config();
    #load_config;
    load_run;
    load_scripts;
    load_class;
    load_course;
    
    my $arg = join " ", @ARGV;

    # Getting @SelectedMachines if any
    if ($arg =~ s/@(.*?)\s//) {
        my $machines = $1;
        my @list = split /,/, $machines;
        for my $interval (@list) {
            my ($first, $last) = split /-/, $interval;

            push @SelectedMachines, $first;
            while ($first < $last) {
                push @SelectedMachines, ++$first;
            }   
        }
    }

    # Choose command to do
    switch ($arg) {
        case "next" { lm_next }
        case "prev" { lm_prev }
        case /set / { $arg =~ /set (.*)/; lm_set $1 }
        case "report"   { lm_report }
        case "start"    { lm_start }
        case "stop" { lm_stop }
        case "show hosts" { lm_show_hosts }
        case "show email" { lm_show_email }
        case "show labs" { lm_show_labs }
        case /do /  { $arg =~ /do (.*)/;  lm_do "$1" }
        else        { print_usage_info() }
    }
    save_run;
    exit(0);
}

sub load_scripts
{
    open (SCRIPTS, "$Config{l3scripts}")
        or die "Cant open l3scripts file: ".$Config{l3scripts}.": $!\n";
    binmode SCRIPTS, ":utf8";
    local $/;
    $_=<SCRIPTS>;
    close(SCRIPTS);

    %Scripts = ("empty-element", split (/###(.*)\n/));
    delete($Scripts{"empty-element"});

}

sub load_config
{
    my %file_config;
    my %argv_config;
    #read_config_file(\%file_config, $Config_File);
    GetOptions(\%argv_config, map "$_=s", keys %Config);
    %Config = (%Config, %file_config, %argv_config);
}

sub load_course
{
    $XMLCourse = XMLin($Config{"courses_path"}.$XMLClass->{"course"}.".xml", ForceArray => 1 )  
        or die "Can't open file of the course ",$XMLClass->{"course"}," [with .xml extension]\n";
#   print Dumper($XMLCourse);
    for my $lab (@{$XMLCourse->{"module"}}) {
        push @Labs, $lab->{"code"};
    }
}

sub load_class
{
    my $classfile =
    $Config{"classfile"} || 
    $Config{"path_classes"}."/".$Config{"class"}.$Config{"class_suffix"};
    $XMLClass = XMLin($classfile , ForceArray => [ 'student' ] )  
        or die "Can't open file of the class ",$classfile,"\n";

    for my $student (@{$XMLClass->{"student"}}) {
        $Machines{$student->{"host"}} = {
            "name"  => "$student->{firstname} $student->{surname}",
            "firstname" => "$student->{firstname}",
            "user"  => "$student->{user}",
            "email" => "$student->{email}",
            "student" => $student,
        }   
    }
#   print Dumper($XMLClass);
#   print Dumper(\%Machines);
}

sub lm_get
{
    print "Getting class description file...";
    if (system("cd $Config{path_classes}; rm -f class.xml ; wget xgu.ru/l3/classes/class.xml") ==0 )
    {
        print "Ok\n";
    } 
    else {
        die "Can't load class file\n"
    }
}


sub lm_next
{
    for(my $i=0; $i<=$#Labs; $i++){
        if ( $Labs[$i] eq $Run{"lab"} ) {
            if ($i < $#Labs) {
                lm_set($Labs[$i+1]);
                return ;
            } else {
                die "Lab ", $Run{"lab"}, " is the last. Switch to next lab is impossible"
            }
        }
        
    }
    die "Lab ", $Run{"lab"}, " not found. Don't know which is next"
}

sub lm_prev
# Switch to previous lab
{
    for(my $i=0; $i<=$#Labs; $i++){
        if ( $Labs[$i] eq $Run{"lab"} ) {
            if ($i > 0) {
                lm_set($Labs[$i-1]);
                return ;
            } else {
                die "Lab ", $Run{"lab"}, " is the first. Switch to previous lab is impossible"
            }
        }
        
    }
    die "Lab ", $Run{"lab"}, " not found. Don't know which is previous"
}

sub lm_set
# Switch to $_[0] lab
# FIXME
{
    my $lab = shift;
    print "Current lab is $lab\n";
    $Run{"lab"} = "$lab";
    lm_do "setlab", $lab;
}


sub lm_start
# Start new training day
{
    print_log(`date`." STARTED\n");
    if ($Run{"lab"}) {
        lm_next;
    }
    else
    {
        # First lab in the course
        lm_set($Labs[0]);
    }
}

sub lm_stop
# Stop this training day
{
    print_log(`date`." STOPPED\n");
}


sub lm_show_hosts
# Show hosts used to run a commands
{
    my $i=1;
    for my $m (sort keys %Machines) {
        if (!@SelectedMachines || grep /^$i$/, @SelectedMachines) {
            print "($i)","\t",$m,"\t",$Machines{$m}->{"name"},"\n";
        }   
        $i++;
    }
}

sub lm_show_email
# Show hosts used to run a commands
{
    my $i=1;
    for my $m (sort keys %Machines) {
        if (!@SelectedMachines || grep /^$i$/, @SelectedMachines) {
            print $Machines{$m}->{"email"},"\t",$Machines{$m}->{"name"},"\n";
        }   
        $i++;
    }
}

sub lm_show_labs
# Show hosts used to run a commands
{
    my $i=1;
    for my $lab (@Labs) {
        print $lab;
        print "*" if $lab eq $Run{"lab"};
        print "\n";
    }
}

sub lm_do
# Do the $_[0] command on all of the hosts 
{
    my $command = shift;
    my $arg = join " ", @_;
    my $i=1;

    my %myenv = ( %Config, 
                lab =>  $arg,           
                center  =>  $XMLClass->{"center"},
                course  =>  $XMLClass->{"course"},
                date    =>  $XMLClass->{"date"},
                stopdate    =>  $XMLClass->{"stop-date"},
                instructor  =>  $XMLClass->{"instructor"}->{"firstname"}." ".$XMLClass->{"instructor"}->{"surname"},
                manager     =>  $XMLClass->{"manager"}->{"firstname"}." ".$XMLClass->{"manager"}->{"surname"},
                coursepath =>   $XMLCourse->{"path"},
            );

    if (grep { $_ eq "PRE-$command"} keys %Scripts) {
        $_=$Scripts{"PRE-$command"};
        s/\$(\w+)/$myenv{$1}/ge;
        open(SHELL, "|/bin/sh -s");
        binmode SHELL, ":utf8";
        print SHELL $_;
        close (SHELL);
    }


    for my $m (sort keys %Machines) {
        if (!@SelectedMachines || grep $_ eq $i, @SelectedMachines) {
            print "$m:\n" if $Config{"show_host"} =~ /y/i;

            %myenv = ( %myenv,
                host    =>  $m,
                ipaddress   =>  $Machines{$m}->{"ipaddress"},
                dirs    =>  "/root /home/".$Machines{$m}->{"user"},
                lablogs =>  $Config{"path_lablogs"}."/".
                        $XMLClass->{"course"}."/".
                        $XMLClass->{"date"}."/".
                        "$m",
                email   =>  $Machines{$m}->{"student"}->{"email"},
                company =>  $Machines{$m}->{"student"}->{"company"},
                name    =>  $Machines{$m}->{"name"},
                firstname   =>  $Machines{$m}->{"firstname"},
            );
            if (grep { $_ eq $command} keys %Scripts) {
                $_=$Scripts{"$command"};
                s/\$(\w+)/$myenv{$1}/ge;
                open(SHELL, "|/bin/sh -s");
                binmode SHELL, ":utf8";
                print SHELL $_;
                close (SHELL);
            }
            else {
                my $res = `ssh $Config{"ssh_user"}\@$m $command`;
                if ($res) {
                    my $count = ($res =~ s/(^)/$m: /mg);
                    print $res;
                    print "\n" if ($count > 1);
                }
            }   
        }   
        $i++;
    }

    if (grep { $_ eq "POST-$command"} keys %Scripts) {
        $_=$Scripts{"POST-$command"};
        s/\$(\w+)/$myenv{$1}/ge;
        open(SHELL, "|/bin/sh -s");
        binmode SHELL, ":utf8";
        print SHELL $_;
        close (SHELL);
    }
}



=cut comment

lm report

Построить html представление для журналов текущего класса.
Для построения используется скрипт l3-report.

=cut

sub lm_report
{

    my $webdir = $Config{"path_web"};
    my $course=$XMLClass->{"course"};
    my $date=$XMLClass->{"date"};
    my $encoding=$XMLClass->{"charset"};

    my $center = $XMLClass->{"center"};
    my $instructor = $XMLClass->{"instructor"}->{"firstname"}." ".$XMLClass->{"instructor"}->{"surname"};
    my $course_name = $XMLCourse->{"fullname"}[0];


    # Собственно журналы

    for my $student (@{$XMLClass->{"student"}}) {
        my $user = $student->{"user"};
        my $hostname = $student->{"host"};
        my $encoding = $student->{"charset"};
        my $student_name = $student->{"firstname"}." ".$student->{"surname"};

        system("mkdir -p $webdir/$date/$hostname");
        system("cp ".$Config{"path_share"}."/*.{ico,css} $webdir/$date/$hostname");
        system($Config{"l3-report"}.
            " --input ".$Config{"path_lablogs"}."/$course/$date/$hostname/$user".
            " --diffs ".$Config{"path_lablogs"}."/$course/$date/$hostname/$user ".
                   $Config{"path_lablogs"}."/$course/$date/$hostname/root".
            " --output $webdir/$date/$hostname/$user.html".
            " --course-name '$course_name'".
            " --course-code '$course'".
            " --course-date '$date'".
            " --course-center '$center'".
            " --course-student '$student_name'".
            " --course-trainer '$instructor'".
            " --encoding $encoding"
        );
        system($Config{"l3-report"}.
            " --input ".$Config{"path_lablogs"}."/$course/$date/$hostname/root".
            " --diffs ".$Config{"path_lablogs"}."/$course/$date/$hostname/root ".
            " --output $webdir/$date/$hostname/root.html".
            " --course-name '$course_name'".
            " --course-code '$course'".
            " --course-date '$date'".
            " --course-center '$center'".
            " --course-student '$student_name'".
            " --course-trainer '$instructor'".
            " --encoding $encoding"
        );
    }

    # Индекс для данного класса

    my $head;

    $head="Журналы лабораторных работ";
    open(HTML, ">$webdir/$date/index.html")
        or die "Can't open $webdir/$date/index.html for writing";
    binmode HTML, ":utf8";
    print HTML <<HEAD;
    <html>
    <head>
    <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
    <title>$head</title>
    </head>
    <body>
    <h1>$head</h1>
    <p>
    Курс: $course_name ($course)<br/>
    Начало: $date<br/>
    Учебный центр: $center <br/>
    Инструктор: $instructor <br/>
    </p>
    <table>
HEAD
    for my $student (@{$XMLClass->{"student"}}) {
        my $user = $student->{"user"};
        my $hostname = $student->{"host"};
        print HTML "<tr>\n";
        print HTML "<td>",$student->{"firstname"}," ",$student->{"surname"},"</td>\n";
        print HTML "<td>",$hostname,"</td>\n";
        print HTML "<td><a href=\"$hostname/$user.html\">",$user,"</td>\n";
        print HTML "<td><a href=\"$hostname/root.html\">","root","</td>\n";
        print HTML "</tr>\n";
    }
    print HTML <<TAIL;
    </table>
    </html>
TAIL
    close (HTML);

    
    
}

sub load_run
{
    my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"};
    open (RUN, $runfile)
        or return;
    while (<RUN>) {
        chomp;
        my ($var, $val) = split /\s+/,$_,2;
        $Run{$var}=$val;
    }
    close (RUN);    
}

sub save_run
{
    my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"};
    open (RN, "$runfile")
        or die "Can't save running state to $runfile";
    for my $var (keys %Run) {
        print RN $var,"\t",$Run{$var},"\n";
    }
    close (RN); 
}

sub print_log
{
    my $logfile = $Config{"path_labmaker"}."/".$Config{"path_logfile"};
    open (LOG, ">>$logfile")
        or die "Can't open logfile $logfile for writing";
    print LOG  @_;
    close (LOG);    
}


sub print_usage_info
{
    print "Usage:\n\n\t$0 [host-list] command\n";
    print <<'USAGE';

Commands:

    next        -- next lab
    prev        -- prev lab
    set LAB     -- set current lab to LAB
    start       -- start this day training
    stop        -- stop this day training
    show hosts  -- show available hosts in the class
    show labs   -- show available labs in the course
    do COMMAND  -- do specified command on the hosts of hostlist
    report      -- generate XML/HTML reports

    
do commands:
    
    install [PROFILE] -- install profile 
    
Host list:  

    @N      -- machine N
    @N1-N2      -- all of the machines from N1 to N2
    @N1,N2,N3   -- machine N1, N2 and N3

    N* is numbers or domain names of the machines.
    
    If host list is not specified, 
    command is executed on all of the machines

USAGE
}