/**

Copyright (c) 2010-2015, Daniel S. Standage and CONTRIBUTORS

The AEGeAn Toolkit is distributed under the ISC License. See
the 'LICENSE' file in the AEGeAn source code distribution or
online at https://github.com/standage/AEGeAn/blob/master/LICENSE.

**/
#include <getopt.h>
#include "genometools.h"
#include "AgnMrnaRepVisitor.h"
#include "AgnPseudogeneFixVisitor.h"

typedef struct
{
  bool infer_introns;
  FILE *outstream;
  bool fix_pseudogenes;
  bool locus_parent;
  FILE *mapstream;
} PmrnaOptions;

static void print_usage(FILE *outstream)
{
  fprintf(outstream,
"\npmrna: filter out all but the primary isoform from each gene of the input\n"
"Usage: pmrna [options] < annot.gff3 > new.gff3\n"
"  Options:\n"
"    -h|--help           print this help message and exit\n"
"    -i|--introns        flag indicating that introns are declared explicitly\n"
"                        and do not need to be inferred from exon features;\n"
"                        default is to infer introns\n"
"    -l|--locus          report a single representative mRNA for each locus\n"
"                        instead of each gene\n"
"    -m|--map: FILE      write each gene/mRNA mapping to the specified file\n"
"    -p|--pseudogenes    disable pseudogene detection and correction\n\n");
}

static void parse_options(int argc, char **argv, PmrnaOptions *options)
{
  int opt = 0;
  int optindex = 0;
  const char *optstr = "hilm:p";
  const struct option pmrna_options[] =
  {
    { "help",        no_argument,       NULL, 'h' },
    { "introns",     no_argument,       NULL, 'i' },
    { "locus",       no_argument,       NULL, 'l' },
    { "map",         required_argument, NULL, 'm' },
    { "pseudogenes", no_argument,       NULL, 'o' },
    { NULL,      no_argument,       NULL,  0  },
  };
  for(opt  = getopt_long(argc, argv + 0, optstr, pmrna_options, &optindex);
      opt != -1;
      opt  = getopt_long(argc, argv + 0, optstr, pmrna_options, &optindex))
  {
    if(opt == 'h')
    {
      print_usage(stdout);
      exit(0);
    }
    else if(opt == 'i')
      options->infer_introns = false;
    else if(opt == 'l')
      options->locus_parent = true;
    else if(opt == 'm')
    {
      options->mapstream = fopen(optarg, "w");
      if(options->mapstream == NULL)
      {
        fprintf(stderr, "error: could not create mapfile '%s'", optarg);
        exit(1);
      }
    }
    else if(opt == 'p')
      options->fix_pseudogenes = false;
  }
}

int main(int argc, char **argv)
{
  GtError *error;
  GtNodeStream *stream, *last_stream;
  GtQueue *streams;
  PmrnaOptions options = { true, NULL, true, false, NULL };
  parse_options(argc, argv, &options);

  //----------
  // Set up the processing stream
  //----------
  gt_lib_init();
  streams = gt_queue_new();

  stream = gt_gff3_in_stream_new_unsorted(0, NULL);
  gt_gff3_in_stream_check_id_attributes((GtGFF3InStream *)stream);
  gt_gff3_in_stream_enable_tidy_mode((GtGFF3InStream *)stream);
  gt_queue_add(streams, stream);
  last_stream = stream;

  if(options.fix_pseudogenes)
  {
    stream = agn_pseudogene_fix_stream_new(last_stream);
    gt_queue_add(streams, stream);
    last_stream = stream;
  }

  if(options.infer_introns)
  {
    stream = gt_add_introns_stream_new(last_stream);
    gt_queue_add(streams, stream);
    last_stream = stream;
  }

  GtNodeVisitor *nv = agn_mrna_rep_visitor_new(options.mapstream);
  if(options.locus_parent)
    agn_mrna_rep_visitor_set_parent_type((AgnMrnaRepVisitor *)nv, "locus");
  stream = gt_visitor_stream_new(last_stream, nv);
  gt_queue_add(streams, stream);
  last_stream = stream;

  stream = gt_gff3_out_stream_new(last_stream, NULL);
  gt_gff3_out_stream_retain_id_attributes((GtGFF3OutStream *)stream);
  gt_queue_add(streams, stream);
  last_stream = stream;

  //----------
  // Execute the processing stream
  //----------
  error = gt_error_new();
  int had_err = gt_node_stream_pull(last_stream, error);
  if(had_err)
    fprintf(stderr, "Error processing node stream: %s\n", gt_error_get(error));
  gt_error_delete(error);

  //----------
  // Free memory
  //----------
  while(gt_queue_size(streams) > 0)
  {
    stream = gt_queue_get(streams);
    gt_node_stream_delete(stream);
  }
  gt_queue_delete(streams);
  if(options.mapstream != NULL)
    fclose(options.mapstream);
  gt_lib_clean();
  return had_err;
}
