#!@WHICHPERL@

use strict;
use warnings;

use lib qw(@PERLLIBDIR@);

use CGI;
use CGI::Carp qw(fatalsToBrowser);
use HTML::Template;

use MemeWebUtils qw(getnum get_safe_file_name loggable_date);
use OpalServices;
use OpalTypes;

# Setup logging
my $logger = undef;
eval {
  require Log::Log4perl;
  Log::Log4perl->import();
};
unless ($@) {
  Log::Log4perl::init('@APPCONFIGDIR@/logging.conf');
  $logger = Log::Log4perl->get_logger('meme.cgi.dreme');
}
$logger->trace("Starting dreme CGI") if $logger;

# constants
my $VERSION = '@S_VERSION@';
my $PROGRAM = 'DREME';
my $HELP_EMAIL = '@contact@';
my $BIN_DIR = '@MEME_DIR@/bin';
my $DREME_SERVICE_URL = '@OPAL@/DREME_@S_VERSION@';
my $DNA_ALPHABET = "ACGTURYKMSWBDHVN"; #includes IUPAC ambiguous characters

&process_request();
exit(0);

sub process_request {
  $logger->trace("call process_request") if $logger;
  my $q = CGI->new;
  my $utils = new MemeWebUtils($PROGRAM, $BIN_DIR);
  
  unless (defined $q->param('search')) { # display the form
    &display_form($utils, $q);
  } else { # try to submit to the webservice
    my $data = &check_parameters($utils, $q);
    if (!$utils->has_errors()) {
      &submit_to_opal($utils, $q, $data);
    } else {
      $utils->print_tailers();
    }
  }
}

sub display_form {
  $logger->trace("call display_form") if $logger;
  my ($utils, $q) = @_;

  #open the template
  my $template = HTML::Template->new(filename => 'dreme.tmpl');

  #fill in parameters
  $template->param(version => $VERSION);
  $template->param(help_email => $HELP_EMAIL);

  if (!$utils->has_errors()) { # output the html
    print $q->header, $template->output;
  } else { # finish off the errors
    $utils->print_tailers();
  }
}

sub require_field {
  $logger->trace("call require_field") if $logger;
  my ($q, $name) = @_;
  my $data = $q->param($name);
  unless (defined $data) {
    die("Required field '$name' was not passed to the CGI script.");
  }
  return $data;
}

sub bool_field {
  $logger->trace("call bool_field") if $logger;
  my ($q, $name) = @_;
  my $data = $q->param($name);
  return 0 if (not defined $data);
  return $data eq '1';
}

sub check_parameters {
  $logger->trace("call check_parameters") if $logger;
  my ($utils, $q) = @_;
  my %data = ();

  my ($file, $pasted, $use_pasted);
  my ($fasta, $alphabet, $nseqs, $min, $max, $ave, $total);
  
  $file = $q->param('pos_file');
  $pasted = $q->param('pos_pasted');
  $use_pasted = &bool_field($q, 'pos_use_pasted');

  ($fasta, $alphabet, $nseqs, $min, $max, $ave, $total) = 
      $utils->get_sequence_data($pasted, $file, 
        PASTE => $use_pasted, ALPHA => 'DNA');

  $data{POS_FASTA} = $fasta;
  $data{POS_NUM} = $nseqs;
  $data{POS_MIN} = $min;
  $data{POS_MAX} = $max;
  $data{POS_AVE} = $ave;
  $data{POS_TOTAL} = $total;

  $data{POS_NAME} = "sequences";
  if (!$use_pasted) {
    $data{POS_NAME} = &get_safe_file_name($file, $data{POS_NAME}, 2);
  }

  my $has_negs = require_field($q, 'has_negs');
  if ($has_negs eq '1') {
    $file = $q->param('neg_file');
    $pasted = $q->param('neg_pasted');
    $use_pasted = &bool_field($q, 'neg_use_pasted');

    ($fasta, $alphabet, $nseqs, $min, $max, $ave, $total) = 
        $utils->get_sequence_data($pasted, $file,
          NAME => 'comparative sequences', PASTE => $use_pasted, 
          ALPHA => 'DNA');

    $data{NEG_FASTA} = $fasta;
    $data{NEG_NUM} = $nseqs;
    $data{NEG_MIN} = $min;
    $data{NEG_MAX} = $max;
    $data{NEG_AVE} = $ave;
    $data{NEG_TOTAL} = $total;

    $data{NEG_NAME} = 'comparative_sequences';
    if (!$use_pasted) {
      $data{NEG_NAME} = &get_safe_file_name($file, $data{NEG_NAME}, 2);
    }
  }

  my $email = &require_field($q, 'email');
  my $email_confirm = &require_field($q, 'email_confirm');
  
  if ($email ne $email_confirm) {
    $utils->whine("Your email address does not match the confirmation email address.");
  } else {
    $utils->check_address($email, $HELP_EMAIL);
  }
  $data{EMAIL} = $email;

  $data{DESCRIPTION} = &require_field($q, 'description');

  my $limit_evalue = &getnum(&require_field($q, 'limit_evalue'));
  if (not defined($limit_evalue)) {
    $utils->whine("The evalue limit must be a number.");
  }
  $data{LIMIT_EVALUE} = $limit_evalue;

  if ($q->param('limit_count_enabled')) {
    my $limit_count_txt = require_field($q, 'limit_count');
    if ($limit_count_txt !~ m/^\d+$/) {
      $utils->whine("The motif count limit must be an integer.");
    }
    $data{LIMIT_COUNT} = &getnum($limit_count_txt);
  }

  return \%data;
}

sub make_arguments {
  $logger->trace("call make_arguments") if $logger;
  my ($data) = @_;
  my $args;
  $args = '-e ' . $data->{LIMIT_EVALUE};
  if (defined($data->{LIMIT_COUNT})) {
    $args .= ' -m '. $data->{LIMIT_COUNT};
  }
  if (defined($data->{NEG_NAME})) {
    $args .= ' -n ' . $data->{NEG_NAME};
  }
  $args .= ' ' . $data->{POS_NAME};
  return $args;
}

sub make_verification {
  $logger->trace("call make_verification") if $logger;
  my ($data) = @_;

  #open the template
  my $template = HTML::Template->new(filename => 'dreme_verify.tmpl');

  #fill in parameters
  $template->param(description => $data->{DESCRIPTION});
  $template->param(pos_file => $data->{POS_NAME});
  $template->param(pos_count => $data->{POS_NUM});
  $template->param(pos_min => $data->{POS_MIN});
  $template->param(pos_max => $data->{POS_MAX});
  $template->param(pos_avg => $data->{POS_AVE});
  $template->param(pos_total => $data->{POS_TOTAL});

  if ($data->{NEG_NAME}) {
    $template->param(neg_file => $data->{POS_NAME});
    $template->param(neg_count => $data->{POS_NUM});
    $template->param(neg_min => $data->{POS_MIN});
    $template->param(neg_max => $data->{POS_MAX});
    $template->param(neg_avg => $data->{POS_AVE});
    $template->param(neg_total => $data->{POS_TOTAL});
  }

  $template->param(evalue => $data->{LIMIT_EVALUE});
  if (defined($data->{LIMIT_COUNT})) {
    $template->param(count => $data->{LIMIT_COUNT});
  }

  return $template->output;
}

sub submit_to_opal {
  $logger->trace("call submit_to_opal") if $logger;
  my ($utils, $q, $data) = @_;

  my $service = OpalServices->new(service_url => $DREME_SERVICE_URL);

  my $args = &make_arguments($data);

  # start OPAL request
  my $req = JobInputType->new();
  $req->setArgs($args);

  # create list of OPAL file objects
  my @infilelist = ();
  # 1) positives
  my $inputfile = InputFileType->new($data->{POS_NAME}, $data->{POS_FASTA});
  push(@infilelist, $inputfile);
  # 2) optional negatives
  if ($data->{NEG_NAME}) {
    $inputfile = InputFileType->new($data->{NEG_NAME}, $data->{NEG_FASTA});
    push(@infilelist, $inputfile);
  }
  # 3) Description file
  if ($data->{DESCRIPTION} && $data->{DESCRIPTION} =~ m/\S/) {
    $inputfile = InputFileType->new("description", $data->{DESCRIPTION});
    push(@infilelist, $inputfile);
  }
  # 4) Email address file (for logging)
  $inputfile = InputFileType->new("address_file", $data->{EMAIL});
  push(@infilelist, $inputfile);
  # 5) Submit time file (for logging)
  $inputfile = InputFileType->new("submit_time_file", loggable_date());
  push(@infilelist, $inputfile);

  # Add file objects to request
  $req->setInputFile(@infilelist);

  # output HTTP header
  print "Content-type: text/html\n\n";

  # Submit the request to OPAL
  $logger->trace("about to call launchJob") if $logger;
  my $result = $service->launchJob($req);
  $logger->debug("launchJob result: $result") if $logger;

  # Give user the verification form and email message
  my $verify = &make_verification($data);

  $logger->trace("about to call verify_opal_job") if $logger;
  $utils->verify_opal_job($result, $data->{EMAIL}, $HELP_EMAIL, $verify);
}
