package MexWWW;

use utf8;
use strict;
use warnings;

use Dancer ':syntax';
use File::Slurp qw/read_file write_file/;
use File::Copy;
use File::Path 'make_path';
use Cwd;
use Encode;
use Encode::Guess;
use Dancer::Plugin::FlashMessage;
#use Dancer::Plugin::Database; # TODO Use for SQLite manipulation
use YAML::Syck;
$YAML::Syck::ImplicitUnicode = 1;
use Exercise::Gen::Verify;

$ENV{PATH} .= ':/usr/local/bin';
my $local = '/usr/local/bin';

our $VERSION = '0.1';
my $TMPDIR = '/tmp/MexWWW';
my $BASEDIR = '/home/natura/mex/public'; ##FIXME será settings('public') ?
my $USRDIR = "$BASEDIR/users";

set_pass_basedir($BASEDIR);

my %users;
_readusers();

# Main routes
get '/'               => sub { redirect '/source'        ; };
get '/source'         => sub { __templ( 'source'        ); };
get '/validate'       => sub { __templ( 'validate'      ); };
get '/generated'      => sub { __templ( 'generated'     ); };
get '/html'           => sub { __templ( 'html'          ); };
get '/example'        => sub { __templ( 'example'       ); };
get '/problem_pedia'  => sub { __templ( 'problem_pedia' ); };

get '/submitsolution' => sub {
	if(session 'exid') {
		my $exid = session 'exid';
		if(!session 'solution') {
			session 'solution' => Exercise::Gen::Verify::mk_exercise_base_str({enun=>1}, $exid);
		}
	} else {
		session 'solution' => "Error: EXID wasn't defined!";
	}

	template 'submitsolution' => {current=>'submitsolution'};
};

post '/submitsolution' => sub {
	my $exid = session 'exid';
	my $solution = param 'solution';
	session 'solution' => $solution;
	
	mkdir $BASEDIR."/solutions" unless(-e $BASEDIR."/solutions");
	my $sfile = $BASEDIR.'/solutions/'.$exid.'.sol';
	session 'solfile' => $sfile;
	#print STDERR "Solution file path: ".$sfile."\n";

	write_file($sfile, {binmode => ':utf8'}, $solution);
	#print STDERR "Solution: ".$solution."\n";
	
	my $total = 0;
	my $score = `pass-check -passdir="$BASEDIR" "$sfile"`;
	#print STDERR "Score: ".$score."\n";
	my @lines = split("\n", $score);
	my %score = ();

	for my $line (@lines) {
		if($line =~ m/'total''(\d+(\.\d{2})?).*'/) {
			$total = $1;
		} else {
			my $grade = 0;
			if($line =~ m/'Q_(\d+)','-'/){
				$score{$1} = $grade;
			} else {
				$line =~ m/'Q_(\d+)',(\d+)/;
				if(!defined($1)){next;}
				$grade = $2 if $2;
				$score{$1} = $grade;
			}
		}
	}

	my @sorted = sort {$a<=>$b} keys(%score);

        template 'submitsolution' => {
		current=>'submitsolution',
		total=>$total,
		sortedsol=>\@sorted,
		solscore=>\%score,
	};
};

get '/myfiles' => sub {
   my $username = session 'username';
   my $file = session 'file';

   if ($username) {
	  session 'userfiles' => [__list_user_files($username)];
	  session 'metafiles' => __load_meta_user_files();
   }

	template 'myfiles' => {current=>'my files'};
};

get '/problem_pedia' => sub {
	template 'problem_pedia' => {current=>'problem_pedia'};
};

post '/uploadsolution' => sub {
	my $input = upload('input_solfile');
	if ($input) {
		my $solution = $input->content;
		$solution = __utf8($solution);
		session 'solution' => $solution;
	}

	flash msg => 'Solution successfully uploaded.';

	session 'solscore' => undef;

        redirect '/submitsolution';
};

post '/upload' => sub {
   my $input_file = upload('input_file');
   my $aux = param 'aux';
	my $username = session 'username';
	my $userdir = session 'userdir';

	# XXX handle auxiliary files
	if ($username and $aux and $input_file) {
	  my $source = $input_file->content;
	  $source = __utf8($source);
	  session source => $source;
	  my $filename = $input_file->basename;
		# HACK for IE in Vista
		if ($filename =~ m/\w:[\\\/]/i) {
		my @l = split /[\/\\]+/, $filename;
		$filename = $l[-1];
		} #/HACK

	  for($filename){
		y{áéíóúãõçâêôûüàñòèÉÁÍÓÚÃÇÂÂÊÔÕ}{aeiouaicaeouuanoeEAIOUACAAEOO};
		s/[^\w_.-]/_/g;
	  }
	  print STDERR "##### mex:  $filename\n";

	  session filename => $filename;

		write_file("$userdir/$filename", {binmode => ':utf8'}, $source);
		__build_meta_file({aux=>1});
	  session source => '';

		flash msg => 'File successfully uploaded.';
	return redirect '/';
	}

   if ($input_file) {
	  my $source = $input_file->content;
	  $source = __utf8($source);
##		utf8::decode($source) unless utf8::is_utf8($source);

	  my $filename = $input_file->basename;
		# HACK for IE in Vista
		if ($filename =~ m/\w:[\\\/]/i) {
		my @l = split /[\/\\]+/, $filename;
		$filename = $l[-1];
		} #/HACK
		unless ($username) {
		$filename .= "." . int(rand(99999));
		}

	  for($filename){
		y{áéíóúãõçâêôûüàñòèÉÁÍÓÚÃÇÂÂÊÔÕ}{aeiouaicaeouuanoeEAIOUACAAEOO};
		s/[^\w_.-]/_/g;
	  }

	  session source => $source;
	  session filename => $filename;
		if ($username) {
		session file => "$userdir/$filename";
		}
		else {
		session file => "$TMPDIR/$filename";
		}
		__write_file();
	  my $f = session "file";
	  my $wc = `wc '$f'` || "nada feito";
	  session wc =>  $wc;
   }

	__process_mex();

	if (session 'username') {
		__build_meta_file();
	}

	flash msg => 'File successfully uploaded.';

	session 'solution' => undef;
	session 'solscore' => undef;

   redirect '/';
};

post '/update' => sub {
   my $source = param 'source';
   my $username = session 'username';
   my $userdir = session 'userdir';

   session source => $source;
   my $filename = session 'filename';
	unless ($filename) {
		$filename = int(rand(99999));
	}
	if ($username) {
	my $dir = session 'userdir';
		session file => "$userdir/$filename";
	}
	else {
		session file => "$TMPDIR/$filename";
	}
	__write_file();
	my $f = session "file";
	session wc =>  `wc '$f'`;

	__process_mex();
	if ($username) {
		__build_meta_file();
	}

	flash msg => 'File successfully uploaded.';

        session 'solution' => undef;
        session 'solscore' => undef;

   redirect '/';
};

post '/updatesolution' => sub {
   my $solution = param 'solution';
   my $exid = session 'exid';
   my $username = session 'username';
   my $userdir = session 'userdir';
   my $filename = session 'filename'.'.solution';

   session solution => $solution;
   if ($username) {
      my $dir = session 'userdir';
      session file => "$userdir/$filename";
   } else {
      session file => "$TMPDIR/$filename";
   }

   #__write_file();

   redirect '/submitsolution';
};

get '/newsolution' => sub {
        session 'solution' => undef;
        redirect 'submitsolution';
};

get '/new' => sub {
	session 'source' => '';
	session 'filename' => '';
	redirect 'source';
};

post '/login' => sub {
	my $action = param 'action';

	if($action =~ m/Login/) {
		my $username = param 'username';
		my $password = param 'password';

		if (defined($users{lc($username)}) and $users{lc($username)} eq $password) {
			session username => $username;
			__handle_dir($username);
			flash msg => 'Login Successful!';
		} else {
			flash msg => 'Login failed!';
		}

		session 'filename' => '';
		session 'source' => '';

		redirect '/';
	} else {
		redirect '/register';
	}
};

get '/logout' => sub {
	session->destroy;
	flash msg => 'Logout Successful';
	redirect '/';
};

get '/register' => sub {
	template 'register' => {current=>'register'};	
};

post '/register' => sub {
	my $username = param 'rusername';
	my $password = param 'rpassword';
	my $result = _adduser($username, $password);
	print STDERR "DEBUG: Registering user ".$username.", result: ".$result."\n";
	if($result == 0) { flash msg => 'Registration complete!'; }
	elsif($result == 3) { flash msg => 'That login was already used!';  }
	else { flash msg => 'Unable to register user.';  }
	redirect '/';
};

get '/activate/:filename' => sub {
	my $filename = params->{filename};

	my $userdir = session 'userdir';
	my $target = "$userdir/$filename";
	if (-e $target) {
	my $source = read_file( $target, { binmode => ':utf8' }); # XXX
	session source => $source;
	session file => $target;
	session filename => $filename;

		__process_mex();

		flash msg => 'Exercise successfully activated.';
	}

	redirect '/source';
};

get '/delete/:filename' => sub {
   my $filename = params->{filename};

	my $username = session 'username';
	my $target = "$USRDIR/$username/$filename";
	if ($username and -e $target) {
		unlink $target, "$target.META";
		flash msg => 'File successfully deleted.';
	}

   redirect '/';
};

sub __templ{template $_[0] => {current => $_[0]}};

sub __write_file {
	my $target = session 'file';
	my $source = session 'source';

	mkdir $TMPDIR unless (-e $TMPDIR);
	write_file($target, {binmode => ':utf8'}, $source);
}

sub __write_solution {
	my $target = session 'solfile';
	my $solution = session 'solution';
	write_file($target, {binmode => ':utf8'}, $solution);
}

sub __process_mex {
	my $file = session 'file';              ## path/filename
	my $filename = session 'filename';
	my $username = session 'username';
	my $cwd = cwd();
	mkdir 'PDFs/' unless -e 'PDFs';

    my $base = $filename;
    $base =~ s/\.(mex|pex|txt)$//;
    session 'basename' => $base;

	# XXX do not process aux files ???
	my $meta = __load_meta_info("$file.META");
	return if $meta->{aux};

	if ($username) {
		chdir session 'userdir';
		$file = $filename;
	}
	else {
		chdir $TMPDIR;
	}

	# XXX defined $HOME because some command in the nexts lines uses it
	$ENV{'HOME'} = '/var/www';

	my $exid = $username.'_'.$file;

	print STDERR "Executing: $local/pass-exer -pdf2 -store -passdir='$BASEDIR' -exid='$exid' -d $file 2>&1\n";

	#./pass-exer -pdf2 -store -exid="vitor_inicial" ../example/Vitor/source/inicial.txt
	#my $stdout = `pass-exer -pdf2 -d $file 2>&1`;    ##FIXME JJ
	
	my $stdout = `pass-exer -pdf2 -store -passdir='$BASEDIR' -exid='$exid' -d $file 2>&1`;    ##FIXME JJ

	session 'exid' => $exid;

	#print STDERR "Executing: pass-exer -store -passdir='/home/natura/.passarola' $file\n";

	print STDERR "Executing: $local/teximporter OUT/$base.tex > OUT/_$base.tex\n";
	$stdout .= `$local/teximporter OUT/$base.tex > OUT/_$base.tex`;

	# remove images from last tth
	unlink glob("pic*.gif");
	print STDERR "Executing: tth -a -u -r -e1 < OUT/_$base.tex \n";
	my $mexhtml = `tth -a -u -r -e1 < OUT/_$base.tex`;

	my $dir = "users/$username";
	$mexhtml =~ s/((src|gref)=['"])/$1$dir\//g;
        $mexhtml =~ s/tex-cache/${dir}\/tex-cache/g;
    $mexhtml = __utf8($mexhtml);
##	utf8::decode($mexhtml) unless utf8::is_utf8($mexhtml);

	session t_debug   => $stdout;
	session mexhtml => $mexhtml;

	chdir $cwd;

	my $target_pdf = "$base.pdf";
	my $target_tex = "$base.tex";

	if ($username) {
		my $dir = session 'userdir';
		$target_pdf = "$dir/OUT/$target_pdf";
		$target_tex = "$dir/OUT/$target_tex";
		session have_pdf => 1 if -e $target_pdf;
		session have_tex => 1 if -e $target_tex;
	    # clean up files in user dir
	    unlink glob("$dir/_*"),
               glob("$dir/teximporter*log"),
	           glob("$dir/OUT/_*"),
               glob("$dir/OUT/teximporter*log"),
               "$dir/missfont.log",
               "$dir/OUT/missfont.log";
	}
	else {
		$target_pdf = 'PDFs/'.$target_pdf;
		$target_tex = 'PDFs/'.$target_tex;
		if (-e "$file.tex") {
			session have_tex => 1;
			copy("$file.tex", $target_tex) or die;
		}
		if (-e "$file.pdf") {
			session have_pdf => 1;
			copy("$file.pdf", $target_pdf) or die;
		}
	}
}

sub __utf8{ my $a=shift;
  #00 00 FE FF 	UTF-32, big-endian
  #FF FE 00 00 	UTF-32, little-endian
  #FE FF 	UTF-16, big-endian
  #FF FE 	UTF-16, little-endian
  #EF BB BF 	UTF-8
  ## remove BOM marks if present
  $a =~ s/^\xEF\xBB\xBF//;
  $a =~ s/^\xFE\xFF//;
  $a =~ s/^\xFF\xFE//;

  my $decoder = Encode::Guess->guess($a);
  if(ref($decoder)){ $decoder->decode($a);}
  else             { $a}
}

sub __handle_dir {
	my ($username) = @_;

#	my $target = setting('public') . "/$USRDIR/$username";
	my $target = "$USRDIR/$username";
	make_path($target) unless -e $target;

	session userdir => $target;
}

# Show the relevant files in a user folder (just Exercises)
sub __list_user_files {
	my ($username) = @_;

	my $dir = session 'userdir';
	return () unless -d $dir;
	opendir (my $fh, $dir);
	my @files = readdir $fh;
	close $fh;

	@files = grep { $_ !~ m/^\.{1,2}/} @files;
	@files = grep { $_ !~ m/\.test$/i} @files;
	@files = grep { $_ !~ m/\.(tex|pdf|META|gif|tab)$/i} @files;
	@files = grep { $_ !~ m/^__/i} @files;
	@files = grep { $_ !~ m/^(OUT|PDFs|tex\-cache)$/i} @files;

	return @files;
}

sub set_flash { session 'flash' => shift }
sub get_flash { my $msg = session 'flash'; session 'flash'=> ""; return $msg }

sub __build_meta_file {
	my $data = shift if @_;

	my $filename = session 'filename';
	my $dir = session 'userdir';
	my $metafile = "$dir/$filename.META";

	# XXX
	my $source = session 'source';
	$data->{title} = session 'filename';
	if ($source =~ m/#\s*title\s*:?\s*(.*?)\n/i) {
		$data->{title} = $1;
	}
	$data->{author} = 'no author set';
	if ($source =~ m/#\s*author\s*:?\s*(.*?)\n/i) {
		$data->{author} = $1;
	}
	$data->{uploaded} = `date`;
	$data->{filename} = $filename;

	# XXX preserve information in meta
	if (-e $metafile) {
		my $previous = __load_meta_info($metafile);
		$data->{aux} = $previous->{aux};
	}

	my $yaml = Dump($data);
	write_file($metafile, {binmode => ':utf8'}, $yaml);
	session 'metafile' => $metafile;
}

sub __load_meta_info {
	my $metafile = shift;
	return unless -e $metafile;

	my $yaml = read_file( $metafile, { binmode => ':utf8' });
	my $data = Load($yaml);

	return $data;
}

sub __load_meta_user_files {
	my $files = session 'userfiles';
	my $dir = session 'userdir';

	my $all = {};
	foreach (@$files) {
		next unless -e "$dir/$_.META";
	my $yaml = read_file( "$dir/$_.META", { binmode => ':utf8' });
		my $data = Load($yaml);

		$all->{$_} = $data;
	}

	return $all;
}

sub __list_pedia_files {
	opendir (my $fh, $USRDIR);
	my @users = readdir $fh;
	close $fh;
	@users = grep { $_ !~ m/^\.{1,2}/} @users;

	my $data = {};
	foreach my $u (@users) {
		opendir (my $fh, "$USRDIR/$u");
		my @files = readdir $fh;
		@files = grep { $_ !~ m/^\.{1,2}/} @files;
		@files = grep { $_ !~ m/\.(tex|pdf|meta)$/i} @files;
		close $fh;

		foreach (@files) {
			next unless -e "$USRDIR/$u/$_.META";
			my $yaml = read_file( "$USRDIR/$u/$_.META", { binmode => ':utf8' });
			$data->{$u}->{$_} = Load($yaml);
			$data->{$u}->{$_}->{'source'} = read_file( "$USRDIR/$u/$_", { binmode => ':utf8' });
		}

	}

	return $data;
}

# _readusers() : Fills the %users variable.
sub _readusers{ # TODO Switch to a "SELECT * FROM users" with SQLite
  my $mexroot = $USRDIR;
  open(A,"<","$mexroot/passwd") or warn("cant find $mexroot/passwd\n");
    while(<A>){
      next if /^#/;
      if(/^(\S+?):([^:\s]+)/){ $users{$1}=$2 }
    }
  close A;
}

# _adduser(Login, Password) : Adds a user to the passwd file.
sub _adduser{ # TODO Switch to SQLite
	if(scalar(@_) < 2 || !defined($USRDIR)) { return 1; }
	my $username = shift(@_);
	my $password = shift(@_);

	open(A,"<",$USRDIR."/passwd") or die("Error: Can't find '".$USRDIR."/passwd'.\n", 2);
	while(<A>){
		my $user = $_;
		# Ignore comments
		next if($user =~ m/^#/);
		# Fail if username exists
		if($user =~ m/$username:.*/) { return 3; }
	}
	close(A);

	open(A, ">>",$USRDIR."/passwd") or die("Error: Can't write on '".$USRDIR."/passwd'.\n", 4);
	print A $username.":".$password."\n";
	close(A);

	_readusers();
	return 0;
}

true;
