/**
 * @file VirtualModem.java
 * @brief
 *    Contain the Main class for the project VirtualModem
 * 
 * Usage examples:
 * ==========================
 * java -jar ./VirtualModem.jar -v -l Exxxx-$(date +%F-%T).log -e Exxxx 600
 * java -jar ./VirtualModem.jar -v -l Qxxx-Rxxxx-$(date +%F-%T).log -a Qxxxx Rxxxx 600
 * java -jar ./VirtualModem.jar -v -l Pxxxx_600_80_8p-$(date +%F-%T).log -p Pxxxx 600 80 8 P1124-$(date +%F-%T)
 * java -jar ./VirtualModem.jar -v -l Mxxxx-$(date +%F-%T).log -g Mxxxx Mxxxx-$(date +%F-%T) 2
 * java -jar ./VirtualModem.jar -v -l Gxxxx-$(date +%F-%T).log -g Gxxxx Gxxxx-$(date +%F-%T) 2
 * 
 * @author Christos Choutouridis AEM:8997
 * @email  cchoutou@ece.auth.gr
 */
package net.hoo2.auth.vmodem;

/** @name imports */
/** @{ */
import org.apache.commons.cli.*; /** command line utility */
/** @} */


/**
 * @class VirtualModem
 * 
 * @brief This is the main control class of the program. 
 * 
 * This class includes the main function which read the user input
 * and create the appropriate object for the requested sequence to
 * retrieve data.
 */
public class VirtualModem {

   /** @name Data */
   /** @{ */
   CommandLine       line;       /** Command line for the argument list */
   CommandLineParser parser;     /** Parser for the argument list */
   Options           options;    /** Option configuration container */
   HelpFormatter     formatter;  /** An extra helper for -h */
   Com               com;        /** Reference to basic configuration module */
   Log               log;        /** Reference to basic logging module */
   /** @} */

   /**
    * @brief The main constructor of the class
    * We allocate memory for the data needed for the command line
    */
   public VirtualModem () {
      parser      = new DefaultParser();
      options     = new Options();
      formatter   = new HelpFormatter();
      com         = new Com();

      // Create argument options and a basic help file
      // for future use.
      Option verb    = new Option ("v", "verbose", false, "Be more verbose to the console");
      Option help    = new Option ("h", "help", false, "Print this message");
      Option timeout = Option.builder("t")
                             .longOpt("timeout")
                             .hasArg()
                             .valueSeparator('=')
                             .desc("Select timeout in [sec]")
                             .build();
      Option speed   = Option.builder("s")
                             .longOpt("speed")
                             .hasArg()
                             .valueSeparator('=')
                             .desc("Select speed in [bps]")
                             .build();
      Option log     = Option.builder("l")
                             .longOpt("log")
                             .hasArg()
                             .desc("Log file name to use")
                             .build();
      Option  echo   = Option.builder("e")
                             .longOpt("echo")
                             .numberOfArgs(2)
                             .desc ("Request echo sequence where <arg> = <1> <2>\n"
                                  + "   <1>: The echo requested code from virtual lab\n"
                                  + "   <2>: The time duration of the session [sec]")
                             .build();
      Option  arq    = Option.builder("a")
                             .longOpt("arq")
                             .numberOfArgs(3)
                             .desc ("Request arq sequence where <arg> = <1> <2> <3>\n"
                                  + "   <1>: The ACK requested code for virtual lab\n"
                                  + "   <2>: The NACK requested code from virtual lab\n"
                                  + "   <2>: The time duration for the session [sec]")
                             .build();
      Option img     = Option.builder("g")
                             .longOpt("img")
                             .numberOfArgs(3)
                             .desc("Request an image sequence where <arg> = <1> <2> <3>\n"
                                 + "   <1>: The image requested code from virtual lab\n"
                                 + "   <2>: The filename to use for storing the image (without .jpg)\n"
                                 + "   <3>: The requested images to fetch.")
                             .build();
      Option gps     = Option.builder("p")
                             .longOpt("gps")
                             .numberOfArgs(5) //  G8164 10 800 8 gps_10_800
                             .desc("Request a GPS sequence where <arg> = <1> <2> <3> <4>\n"
                                 + "   <1>: The gps requested code from virtual lab\n"
                                 + "   <2>: The time from trace to use as starting point [sec]\n"
                                 + "   <3>: The time duration for the trace to fetch [sec]\n"
                                 + "   <4>: The number of points to fetch from the above trace\n"
                                 + "   <5>: The filename to use for storing the image (without .jpg)")
                             .build();
      options.addOption(verb);
      options.addOption(help);
      options.addOption(timeout);
      options.addOption(speed);
      options.addOption(log);
      options.addOption(echo);
      options.addOption(arq);
      options.addOption(img);
      options.addOption(gps);
   }

   /** @name private api */
   /**@{*/

   /**
    * parse the command line arguments
    * @param args the arguments to parse
    * @return the status of the operation
    */
   private boolean getCmdOptions (String[] args) {
      try {
         // parse the command line arguments
         line = parser.parse (options, args);
      }
      catch( ParseException exp ) {
         // oops, something went wrong
         System.err.println( "Parsing command line failed: " + exp.getMessage() );
         return false;
      }
      return true;
   }

   /**
    * Dispatch the correct functionality based on arguments
    * @return the status of the operation (currently true)
    */
   private boolean commandDispatcher () {
      boolean verbose = false;
      
      if (line.hasOption("help")) {
         formatter.printHelp( "virtualModem", options );
         return true;
      }

      // Get log and verbose options first
      if (line.hasOption("verbose")) 
         verbose = true;
      if (line.hasOption("log"))
         log = new Log (line.getOptionValue("log"), verbose);
      else
         log = new Log (null, verbose);
      if (log.open() != true)
         return false;
      
      // get other options
      if (line.hasOption("timeout")) {
         com.timeout(Integer.parseInt(line.getOptionValue("timeout")));
      }
      if (line.hasOption("speed")) {
         com.speed(Integer.parseInt(line.getOptionValue("speed")));
      }

      // Execution dispatcher
      if (line.hasOption("echo")) {
         byte[] code = line.getOptionValues("echo")[0].getBytes();
         Echo e = new Echo(com, log, code,
                           Integer.valueOf(line.getOptionValues("echo")[1]));
         if (com.open()) {
            e.caption(code);
            e.run();
            com.close();
            
         }
      }
      else if (line.hasOption("arq")) {
         byte[]  ack = line.getOptionValues("arq")[0].getBytes();
         byte[] nack = line.getOptionValues("arq")[1].getBytes();
         ARQ a = new ARQ(com, log, ack, nack,
                         Integer.valueOf(line.getOptionValues("arq")[2]));
         if (com.open()) {
            a.caption(ack, nack);
            a.run();
            com.close();

         }
      }
      else if (line.hasOption("img")) {
         byte[] code = line.getOptionValues("img")[0].getBytes();
         Image im = new Image (com, log, code,
                               line.getOptionValues("img")[1],
                               Integer.valueOf(line.getOptionValues("img")[2]));
         if (com.open()) {
            im.caption(code);
            im.run();
            com.close();
         }
      }
      else if (line.hasOption("gps")) {
         byte[] code = line.getOptionValues("gps")[0].getBytes();
         GPS g = new GPS (
                     com, log, code,
                     Integer.valueOf(line.getOptionValues("gps")[1]),
                     Integer.valueOf(line.getOptionValues("gps")[2]),
                     Integer.valueOf(line.getOptionValues("gps")[3])
                 );
         Image im = new Image (
                        com, log, null,
                        line.getOptionValues("gps")[4], 1
                    );
         if (com.open()) {
            g.caption();
            im.code(g.run().getBytes());
            im.run();
            com.close();
         }
      }

      log.close();
      return true;
   }
   /**@}*/
   

   /**
    * @brief Main function
    */
   public static void main(String[] args) {
      // allocate the main object
      VirtualModem vmodem = new VirtualModem();

      // prepare command line input
      if (vmodem.getCmdOptions (args) != true)
         return;
      // Call the requested functionality
      if (vmodem.commandDispatcher() != true)
         return;
   }
}
