package MexsWWW;

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";

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 '/submitsolution' => sub { __templ( 'submitsolution'); };
get '/texample'       => sub { __templ( 'texample'      ); };
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 't_file';
   __DEB("$file",$username);

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

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


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 t_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;
      }
      __DEB("mex_filename"=> $filename);

      session t_filename => $filename;
      session t_file => "$userdir/$filename";

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

      flash msg => 'File uploaded successful!';
   	  return redirect '/';
	}

   if ($input_file) {
      my $source = $input_file->content;
      $source = __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 t_source => $source;
      session t_filename => $filename;
      if ($username) {
      	session t_file => "$userdir/$filename";
	  }
	  else {
      	session t_file => "$TMPDIR/$filename";
	    mkdir $TMPDIR unless (-e $TMPDIR);
	  }
	  __write_file();
      my $f = session "t_file";
      my $wc = `wc '$f'` || "nada feito";
      session wc =>  $wc;
   }

	__process_mex_set();

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

	flash msg => 'File uploaded successful!';

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

   redirect '/';
};

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

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

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

	flash msg => 'File updated successful!';
        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 't_source' => '';
	session 't_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 't_file' => '';
                session 't_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 t_source => $source;
   	   session t_file => $target;
   	   session t_filename => $filename;

       __process_mex_set();

       flash msg => 'Exercise activated successful!';
	}

	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;
		set_flash('File deleted successful!');
	}

   redirect '/';
};


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

sub __write_file {
	my $target = session 't_file';
	my $source = session 't_source';
	write_file($target, {binmode => ':utf8'}, $source);
}

sub __process_mex_set {
	my $file     = session 't_file';
	my $filename = session 't_filename';
	my $username = session 'username';

	if ($username) {
        __DEB("User"=>$username, userdir => session 'userdir');
		chdir session 'userdir';
		$file = $filename;
	}
	else {
		chdir $TMPDIR;
	}
	my $cwd = cwd();
	mkdir 'PDFs/' unless -e 'PDFs';
    __DEB(cur_dir=>$cwd, filename=>$filename);

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

    my $base = $file;
    $base =~ s/\.test$//;

	session 't_basename' => $base;

    my $catal="$BASEDIR/mex_catalogue_dump"; #### FIXME catalogue ####

	my $err;
    $err = __sys2("$local/pass-test -catalog $catal $filename");                         ###FIXME jj
	$err .=__sys("$local/teximporter $base-sol.tex> _$base-sol.tex") if -f "$base-sol.tex";
	$err .=__sys("$local/ppdflatex -Q _$base-sol.tex") if -f "_$base-sol.tex";
    rename("_$base-sol.pdf",  "$base-sol.pdf" );

	$err .=__sys("$local/teximporter $base-enun.tex> _$base-enun.tex") if -f "$base-enun.tex";
	$err .=__sys("$local/ppdflatex -Q _$base-enun.tex") if -f "_$base-enun.tex";
    rename("_$base-enun.pdf",  "$base-enun.pdf" );

	unlink glob("pic*.gif");
	if(-f "_$base-sol.tex"){
	  my $mexhtml = __sys1("tth -a -u -r -e1 < _$base-sol.tex") ;
	  my $dir = "users/$username";
	  $mexhtml =~ s/(src=['"])/$1$dir\//g;
      $mexhtml = __utf8($mexhtml);
	  session t_mexhtml => $mexhtml;
    }
    else{
	  session t_mexhtml => "no _$base-sol.tex<br>$err" ;
    }
	session t_debug => $err;

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

	session t_have_pdf => 1 if -e $target_pdf;
	session t_have_tex => 1 if -e $target_tex;

	# clean up files in user dir
	my $userdir = session 'userdir';
	unlink glob("$userdir/_*"), glob("$userdir/teximporter*log"), "$userdir/missfont.log";
}

sub __sys{ my $comand=shift;
 my $cwd=cwd();
 print STDERR "DEBUG: $comand ($cwd)\n";
 system($comand) == 0 or print STDERR "...Erro:$!\n";
 return $! || "";
}

sub __sys1{ my $comand=shift;
 my $cwd=cwd();
 print STDERR "DEBUG: $comand ($cwd)\n";
 my $out = `$comand`;
 print STDERR "...Erro:$!\n" if $!;
 return $out
}

sub __sys2{ my $comand=shift;
 my $cwd=cwd();
 print STDERR "DEBUG: $comand ($cwd)\n";
 my $err = `$comand 2>&1`;
 print STDERR "...Erro(returned):$!\n" if $!;
 return $err
}

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;
    __DEB(1,"mkdir"=>$target);
}

sub __DEB{ print STDERR "DEBUG: '", join("','",@_), "'\n"; }

# Show the relevant files in a user folder (just Tests)
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 = map { if(s/\.test$//){($_)} else {()} }  @files;

	@files = grep { $_ !~ m/^\.{1,2}/} @files;
	@files = grep { $_ !~ m/\.(txt|mex|pex)$/} @files;
	@files = grep { $_ !~ m/\.(tex|pdf|META|gif|tab)$/i} @files;
	@files = grep { $_ !~ m/^__/i} @files;
	@files = grep { $_ !~ m/^tex\-cache$/i} @files;
	@files = grep { $_ !~ m/^OUT$/i} @files;
	@files = grep { $_ !~ m/^PDFs$/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 'basename';
	my $dir = session 'userdir';
	my $metafile = "$dir/$filename.META";

	# XXX
	my $source = session 't_source';
	$data->{title} = session 't_filename';
	if ($source =~ m/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;

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

sub __load_meta_info {
	my $metafile = shift;

	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;
