// GpsSimulator.java
//
// Copyright (C) 1998 Henrik Bjorkman. 
// May be distributed under GNU General public licence.
//
//
// Credits
//
// SUNs jdk 1.1.6 was used when developing this program.
//
//
//
// History
//
// 1998-10-27 Created by Henrik Bjorkman
// 1998-10-30 Speed and course can be changed with key inputs. Henrik


//package se.beod.henrik.nav;

import java.io.*;
import java.util.Date;
import java.util.Calendar;
import java.awt.*;
import java.text.*;
import se.beod.henrik.nav.*;




public class GpsSimulator extends Thread
{
  static final String version="Henrik Bjorkman's GPS Simulator";
  ComPortAPI port;
  Pos pos=new Pos();
  KeyInput keyInput;

  static void debug(String s)
  {
    System.out.println("GpsSimulator: " + s);
  }

  static void error(String s)
  {
    System.err.println("GpsSimulator: " + s);
    System.exit(0);
  }

  public static void println(String s)
  {
    System.out.println(s);
  }

  // Adds zeroes first so that a string with a minimum length of n
  // is returned.
  public static String addZero(String str,int n)
  {
    while (str.length()<n)
    { 
      str=new String("0"+str);
    }
    return(str);
  }

  public static String timeString(Pos pos)
  {
    return(addZero((""+(pos.time/3600000L)%24),2)+""+
           addZero((""+(pos.time/60000L)%60),2)+""+
           addZero((""+(pos.time/1000L)%60),2)+"."+
           addZero((""+(pos.time/10L)%100),2));
  }

  static public String toStringLatLng(double minutes,int digits)
  {
    int l=(new Double(minutes*1000)).intValue();
    if (l<0) {l=-l;}
    int d=l/60000;
    int m=(l/1000)%60;
    int m1000=l%1000;
    return(""+addZero(""+d,digits)+addZero(""+m,2)+"."+addZero(""+m1000,3));
  }

  void writeLine(String line)
  {
    try 
    {
      port.out.write(line+"\r\n");
      port.out.flush();
    }
    catch( IOException e ) 
    {
      error("writeLine " + e );
    }  
  }

  // Calculate where we will be in t milliseconds with current
  // speed and direction (track).
  void movePos(Pos pos, long t)
  {
    double r=t*pos.speed/3600000;
    double lng=pos.lng+r*Math.sin(Math.PI*pos.track/180)/Math.cos(Math.PI*pos.lat/(180*60));
    double lat=pos.lat+r*Math.cos(Math.PI*pos.track/180);    
    pos.lat=lat;
    pos.lng=lng;
    pos.time+=t;
  }

  String GPGLL(Pos pos)
  {
    String line;
    /* example: $GPGLL,6200.000,N,01652.500,E,112401,A*2C */
    /* probably we should have some checksum also */
    line="$GPGLL,"+toStringLatLng(pos.lat,2)+(pos.lat>=0?",N,":",S,")+
     toStringLatLng(pos.lng,3)+(pos.lat>=0?",E,":",W,")+
     timeString(pos)+",A";
    return(line);
  }

  String GPRMC(Pos pos)
  {
    String line;
    /* GPRMC,112402,A,6200.000,N,01652.500,E,000.0,360.0,281098,002.8,E*76 */
    /* probably we should have some checksum also */
    line="$GPRMC,"+timeString(pos)+",A,"+
     toStringLatLng(pos.lat,2)+(pos.lat>=0?",N,":",S,")+
     toStringLatLng(pos.lng,3)+(pos.lat>=0?",E,":",W,")+
     pos.speed+","+pos.track;
    return(line);
  }

  public void run() 
  {
    int state=0;
    //BufferedReader in=new BufferedReader(new InputStreamReader(System.in));

    debug("start");

    pos.time=System.currentTimeMillis();

    while(true) 
    {
      String line=null;
      synchronized (this)
      {
        try 
        {
          this.wait(100);
        }
        catch (InterruptedException e) {;}
      }


/*    try 
      {
        if (in.ready())
        {
          int ch=in.read();
          switch(ch)
          {
            case 'l':pos.track-=10;if (pos.track<0) {pos.track+=360;} break;
            case 'r':pos.track+=10;if (pos.track>=360) {pos.track-=360;} break;
            case 'f':pos.speed+=10;break;
            case 's':pos.speed-=10;if (pos.speed<0) {pos.speed=0;} break;
            default: break;
          }
        }
      }
      catch (IOException e) {debug(""+e);}*/


      
      switch(keyInput.getKey())
      {
        case 'l':pos.track-=10;if (pos.track<0) {pos.track+=360;} break;
        case 'r':pos.track+=10;if (pos.track>=360) {pos.track-=360;} break;
        case 'f':pos.speed+=10;break;
        case 's':pos.speed-=10;if (pos.speed<0) {pos.speed=0;} break;
        default: break;
      }
      

      movePos(pos,System.currentTimeMillis()-pos.time);

      switch(state++)
      {
        case 0: line=GPGLL(pos);break;
        case 10: line=GPRMC(pos);break;
        case 19: state=0;break;
        default: break;
      }

      if (line!=null) 
      {
        println(line);
        writeLine(line);
      }
    } 
  }

  public static int numLen(String str)
  {
    int i=0;
    while (i<str.length() && Character.isDigit(str.charAt(i))) {i++;}
    return(i);
  }

  static public double parseLatLng(String str, char pos, char neg)
  {
    int l=numLen(str);
    double m=Double.valueOf(str.substring(0,l)).doubleValue()*60d +
     (Double.valueOf(str.substring(l+1)).doubleValue());
    if (str.charAt(l)==neg)
    {
      m=-m;
    }
    else if (str.charAt(l)!=pos)
    {
      error("wrong format lat/lng: "+str);
    }
    return(m);
  }

  void help()
  {
    println("Usage: Java GpsSimulator [switches] latitude longitude speed course");
    println("switches:");
    println("-h      This text ");
    println("-p<n>   Use serial port n");
    System.exit(0);
  }

  public GpsSimulator(String[] args) 
  {
    String port_name=null;
    String file_name=null;
    int argc=0;

    while (argc<args.length)
    {
      if (args[argc].charAt(0)!='-') break;
      if (args[argc].length()<2) {help();break;}
      switch(args[argc].charAt(1))
      {
        case 'p' : port_name=args[argc].substring(2);break;
        default  : help();break;      
      }
      argc++;
    }

    if (args.length>argc) {pos.lat=parseLatLng(args[argc++],'N','S');}
    if (args.length>argc) {pos.lng=parseLatLng(args[argc++],'E','W');}
    if (args.length>argc) {pos.speed=Double.valueOf(args[argc++]).doubleValue();}
    if (args.length>argc) {pos.track=Double.valueOf(args[argc++]).doubleValue();}
    if (args.length>argc) {file_name=args[argc++];}

    println(version);
    println("Copyright 1998 Henrik Bjorkman www.stacken.kth.se/~bjorkman");
    println("Start with switch -? for help\n");

    port=new ComPortAPI(port_name);
    keyInput=new KeyInput(this);
    this.start();
  }

  static public void main(String[] args)
  {
    new GpsSimulator(args);
  }


}

