/**
 * @file GPS.java
 * 
 * @author Christos Choutouridis AEM:8997
 * @email  cchoutou@ece.auth.gr
 */
package net.hoo2.auth.vmodem;

/** @name imports */
/** @{ */
import java.util.Arrays;
/** @} */

/**
 * @class GPS
 * 
 * Class to used for the GPS session
 */
class GPS {
   static final int    GPS_BUFFER_SIZE    = 256;   /** GPS trace buffer size */
   static final String GPS_BEGIN          = "START ITHAKI GPS TRACKING";   /** starting pattern */
   static final String GPS_END            = "STOP ITHAKI GPS TRACKING";    /** ending pattern */
   static final int    GPS_USE_TRACK      = 1;     /** Which track to use (given) */
   static final String GPS_TRACK_PREFIX   = "R=";  /** GPS command track request prefix */
   static final String GPS_IMAGE_PREFIX   = "T=";  /** GPS command image request prefix */
   static final int    GPS_MAX_POINTS     = 9;     /** Maximum points (given) */
   static final int    GPS_COORDINATES_SIZE = 6;   /** Coordinates size */

   static final int    GPS_LATITUDE_BEGIN = 17;    /** The latitude sequence string position */
   static final int    GPS_LATITUDE_END   = 28;    /** The end of latitude sequence string position */
   static final int    GPS_LONGITUDE_BEGIN= 29;    /** The longitude sequence string position */
   static final int    GPS_LONGITUDE_END  = 41;    /** The end of longitude sequence string position */

   private Com          com_;          /** Reference to communication module */
   private Log          log_;          /** Reference to logging module */
   private Transaction  transaction_;  /** A transaction object to used as a buffer for all requests */
   private byte[]       code_;         /** The desired code for the session */
   private int          start_;        /** Starting point [sec] */
   private int          duration_;     /** Duration of requested trace */
   private int          points_;       /** Number of points to fetch from the above trace */
   private byte[]       coordinates_;  /** Coordinates buffer */

   /**
    * Basic constructor
    */
   GPS (Com com, Log log, byte[] code, int start, int duration, int points) {
      com_        = com;
      log_        = log;
      transaction_= new Transaction(null, new byte[GPS_BUFFER_SIZE]);
      code_       = code;
      start_      = start;
      duration_   = duration;
      points_     = (points <= GPS_MAX_POINTS) ? points : GPS_MAX_POINTS;
      coordinates_= new byte[GPS_COORDINATES_SIZE];
   }

   /**
    * Functionality to drain the response buffer for the welcome message
    * from the server
    */
   void caption () {
      String line;

      line = "Running GPS with: " + new String(code_)
           + " time [" + start_ + " - " +(start_+duration_) + ") sec" + " [" + points_ + " points]";
      log_.write(line, true);

      transaction_ = com_.request (transaction_, null, null, null, false);
      line = new String(transaction_.getResponse());
      log_.out(line);
   }

   /**
    * Main transaction loop. It send requests to server, get the response
    * and log the procedure while doing it.
    * @return the image request code to pass to image module
    */
   String run () {
      String code, image_code;
      String line;

      log_.out("Get traces");
      image_code = new String(code_);
      for (int trace =start_ ; trace < start_+duration_ ; trace += duration_/points_) {
         code = new String(code_) + GPS_TRACK_PREFIX + GPS_USE_TRACK + String.format("%04d", trace) + "01";
         transaction_ = com_.request(transaction_, code.getBytes(), GPS_BEGIN.getBytes(), GPS_END.getBytes(), false);
         line = new String(transaction_.code) + ": "
              + new String(transaction_.getResponse())
              + " Tr= " + (transaction_.arrival - transaction_.departure) + " [msec]";
         log_.write(line);
         
         _get_coordinates(transaction_.getResponse());
         image_code += GPS_IMAGE_PREFIX + 
               String.format("%02d%02d%02d%02d%02d%02d", coordinates_[0], coordinates_[1],
                     coordinates_[2], coordinates_[3],
                     coordinates_[4], coordinates_[5]);
         
      }
      return image_code;
   }

   /** @name private helper API */
   /** @{ */

   /**
    * Extract coordinates from response
    * @param stream  The stream to search
    * @return        The coordinates buffer
    */
   private byte[] _get_coordinates (byte[] stream) {
      int start        = Com.detect(stream, "GPGGA".getBytes(), stream.length);
      double latitude  = _nmea2dec (Double.valueOf(
                         new String (Arrays.copyOfRange(stream, 
                               start+GPS_LATITUDE_BEGIN,
                               start+GPS_LATITUDE_END-2))));
      double longitude = _nmea2dec (Double.valueOf( 
                         new String(Arrays.copyOfRange(stream,
                               start+GPS_LONGITUDE_BEGIN,
                               start+GPS_LONGITUDE_END-2))));

      coordinates_[0] = (byte)(longitude);                              // longitude deg
      coordinates_[1] = (byte)((longitude - coordinates_[0]) * 60.0);   // longitude '
      coordinates_[2] = (byte)((longitude - coordinates_[0]
                                - coordinates_[1]/60.0)*3600);          // longitude "

      coordinates_[3] = (byte)(latitude);                               // latitude deg
      coordinates_[4] = (byte)((latitude - coordinates_[3]) * 60.0);    // latitude '
      coordinates_[5] = (byte)((latitude - coordinates_[3]
                                - coordinates_[4]/60.0)*3600);          // latitude "
      return coordinates_;
   }

   /**
    * A helper to convert the NMEA format to canonical
    * decimal coordinate format
    */
   double _nmea2dec (double c) {
      int d = (int)c/100;
      c -= d*100;
      return d + (c/60);
   }
   /** @} */
}
