#!/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_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_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);
	}

	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 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}",
			"student" => $student,
		}	
	}
#	print Dumper($XMLClass);
#	print Dumper(\%Machines);
}


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_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
}