package com.algobase.stracks;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;

import android.graphics.Bitmap;
import android.location.Location;
import com.algobase.gpx.TrackPoint;
import com.algobase.gpx.TrkWriter;

import com.algobase.share.network.LedaSocket;


public class GpsBuffer {

  sTracksRoot activity;
  File track_folder;

  File trk_file;
  Thread thread;
  
  LedaSocket sock;
  TrkWriter trk_writer;

  String client_version;

  String user;
  String password;
  String track;
  String status;


  boolean user_ok;
  boolean live_tracking;
  boolean write_track;

  String host;
  int port;
  
  Location current_loc = null;
  Location last_server_loc = null;

  long last_server_t = 0; 
  int current_hrate = 0;
  float current_speed = 0;

  long gmt_offset = 0;


  TrackPoint[]  buffer;

  TrackPoint p_last_sent;

  int size;
  int first;
  int stop;
  int connect_count;


public GpsBuffer(sTracksRoot act, File trk_folder, int sz)
{ 
  activity = act;
  track_folder = trk_folder;
  user  = new String();
  track = new String();
  host  = new String();
  status = new String();

  client_version = activity.format("%.3f", 0.001f*activity.version_code);
  
  live_tracking = true;
  write_track = true;

  user_ok = true;

  size = sz;
  buffer = new TrackPoint[sz];

  sock = null;
  port = 0;

  p_last_sent = null;
  first = 0;
  stop = 0;
  connect_count = 0;

  // get gmt offset
  GregorianCalendar cal = new GregorianCalendar();
  Date date = cal.getTime();
  gmt_offset = cal.getTimeZone().getOffset(date.getTime());

 thread = new Thread() {
     public void run()
     { for(;;)
       { long t = 100;
         if (first == stop) t = 2000;
         if (send_trackpoint() == false) t = 10000;
         try { sleep(t); } catch (Exception e){}
        }
      }
   };

  thread.start();

}


/*
public Location get_current_location() { return current_loc; }
*/
public void set_live_tracking(boolean b) { live_tracking = b; }

public void set_write_track(boolean b) { write_track = b; }

public void set_user(String user_name, String pwd) 
{ user = user_name; 
  password = pwd;
}

public void set_track(String track_name) { track = track_name; }
public void set_host(String host_name) { host = host_name; }
public void set_port(int port_num) { port = port_num; }

public int length() 
{ if (stop >= first) 
    return stop - first;
  else
    return size - first + stop;
}

public int first_index() { return first; }
public int stop_index() { return stop; }
public int next_index(int i) { 
  if (++i >= size) i=0;
  return i;
 }

public double get_altitude(int i) { return buffer[i].get_alt(); }
public double get_longitude(int i) { return buffer[i].get_lon(); }
public double get_latitude(int i) { return buffer[i].get_lat(); }


public int num_connects() { return connect_count; }
public String get_status() { return status; }


public void begin_track(String track_name)
{
  activity.log("gps_buffer: begin track " + track_name);

  track = track_name;
  p_last_sent = null;
  first = 0;
  stop = 0;
  connect_count = 0;

  if (write_track)
  { trk_file = new File(track_folder,track + ".trk");
    trk_writer =  new TrkWriter(trk_file);
    //trk_writer.begin_track(track.substring(0,10),activity.device_name);
    trk_writer.begin_track(track,activity.device_name);
  }
  connect_count = 0;
  connect();
}

public void clear_buffer()
{ activity.log("gps_buffer: clear buffer");
  first = stop;
  p_last_sent = null;
  connect();
}

public void resume_track()
{ activity.log("gps_buffer: resume track");
  p_last_sent = null;
  first = 0;
  stop = 0;
  connect_count = 0;
  trk_file = new File(track_folder,track + ".trk");
  trk_writer =  new TrkWriter(trk_file,true); // append
  connect();
}



public File get_trk_file() { return trk_file; } 

public void delete_trk_file() 
{ activity.log("GpsBuffer: delete trk-file " + trk_file.getName());
  if (!trk_file.exists()) 
  { activity.log("File does not exist.");
    return;
   }
  trk_file.delete(); 
}


public boolean connect()
{ 
  String msg = "";

  if (connected()) return true;

  connect_count++;

  sock = new LedaSocket();

  sock.setTimeout(4000);
  if (!sock.connect(host,port)) 
  { status = "connect failed";
    return false;
   }

  sock.sendString(user + "&" + client_version);
  msg = sock.receiveString();

  if (msg.equals("ok"))
    status = "ok";
  else
  { status = user + ": " + msg;
    user_ok = false;
    sock.disconnect();
    return false;
   }

  sock.sendString("trk " + track);
  return sock.wait("ok");
}


public void finish_track() 
{
  track = "";

  if (sock == null) return;

  if (live_tracking) sock.sendString("end"); 

  sock.disconnect();

  if (write_track) trk_writer.finish_track();
}

public boolean connected() { return sock != null && sock.connected(); }

public void setPoint(Location loc, double speed, int hrate) 
{ current_loc = loc; 
  current_speed = (float)speed;
  current_hrate = hrate;
}

public void addPoint(int id, long t, Location loc, double alt, 
                                                   double dist,
                                                   double speed,
                                                   double ascent,
                                                   int hrate,
                                                   int power)
{ 
  //activity.log("addPoint: baro_alt = " + barometer_alt);

  double lon = loc.getLongitude();
  double lat = loc.getLatitude();
  double gps_alt = loc.getAltitude();
  double acc = loc.getAccuracy();

  TrackPoint p = 
       new TrackPoint(id,t,lon,lat,alt,acc,speed,dist,ascent,hrate,power); 
  //TrackPoint q = (stop > 0) ? buffer[stop-1] : null;

  buffer[stop] = p;
  if (++stop == buffer.length) stop = 0;

  // if overflow: skip first
  if (first == stop && ++first == buffer.length) first = 0;

  t -= gmt_offset;

  if (write_track) trk_writer.add_point(p);
}


public void addLap() { 
  if (write_track) trk_writer.add_lap();
}


public void addBreak(int id, long msec) { 
/*
  if (write_track) trk_writer.add_break(msec);
*/
}
  
  

public boolean send_trackpoint()
{
live_tracking = false;

  if (!live_tracking) return false;

  if (!activity.running && first == stop && current_loc != null) { 
    float dist = 0;
    float ascent = 0;
    return send_point(current_loc,dist,current_speed,ascent,current_hrate);
  }

  if (!user_ok) return true;
  if (first == stop) return true;

  if (!connect())
  { activity.log("send_trackpoint: (re)connect failed");
    return false;
   }

  TrackPoint p = buffer[first];

  //String s = p.trk_line(p_last_sent);

  int bat = (int)(0.5 + 100*activity.battery_level);

  String s; 
  if (p_last_sent == null)
     s = activity.format("P%d %d %d %d %d %d %d %d %d %d %d", 
                   p.id,p.time,p.lon,p.lat,p.alt,p.acc,p.speed,
                                           p.dist,p.ascent,p.hrate,bat);
  else
   { int t_diff   = p.time - p_last_sent.time;
     int lon_diff = p.lon  - p_last_sent.lon;
     int lat_diff = p.lat  - p_last_sent.lat;
     int alt_diff = p.alt  - p_last_sent.alt;
     int acc_diff = p.acc  - p_last_sent.acc;
     int spd_diff = p.speed - p_last_sent.speed;
     int dst_diff = p.dist - p_last_sent.dist;
     int asc_diff = p.ascent - p_last_sent.ascent;
     int hrt_diff = p.hrate - p_last_sent.hrate;

     s = activity.format("D%d %d %d %d %d %d %d %d %d %d %d", 
                     p.id, t_diff, lon_diff,lat_diff,alt_diff,
                     acc_diff,spd_diff,dst_diff,asc_diff,hrt_diff,bat);
    }


  if (!sock.sendString(s))
  { sock.disconnect();
    return false;
   }

  s = sock.receiveString();

  if (!s.equals("ok"))
  { sock.disconnect();
    return false;
   }

  if (++first == buffer.length) first = 0;
  p_last_sent = p;
  return true;
}


public double getServerAltitude(Location loc, int hrate)
{
  double lon = loc.getLongitude();
  double lat = loc.getLatitude();
  int acc = (int)(0.5 + loc.getAccuracy());
  long t = loc.getTime()/1000;

  LedaSocket sock = new LedaSocket();

  sock.setTimeout(4000);
  if (!sock.connect(host,port))
  { activity.log("getServerAltitude: connect failed");
    return -1;
   }

  String msg = "";

  sock.sendString(user + "&" + client_version);
  msg = sock.receiveString();
  
  if (!msg.equals("ok"))
  { sock.disconnect();
    return -1;
   }

  sock.sendString(activity.format("alt %d %.7f %.7f %d %d",t,lon,lat,acc,hrate));
  msg = sock.receiveString();
  sock.disconnect();

  if (msg.equals("") || msg.equals("error")) return -1;

  int i = msg.indexOf(";");

  double alt = 0;
  double dist = 0;

  if (i > 0)
  { alt =  Float.parseFloat(msg.substring(0,i));
    dist =  Float.parseFloat(msg.substring(i+1));
   }
  else
    alt =  Float.parseFloat(msg);

  return alt;
}



public boolean send_point(Location loc, float dist, float speed, float ascent, 
                                                                 int hrate)
{
  long t = loc.getTime()/1000;

  if (last_server_loc != null && loc.distanceTo(last_server_loc) < 20
      && (t - last_server_t) < 30)  return true;

  double lon = loc.getLongitude();
  double lat = loc.getLatitude();
  double alt = loc.getAltitude();
  float  acc = loc.getAccuracy();
  float  spd = 3.6f * speed;

  LedaSocket sock = new LedaSocket();
  sock.setTimeout(4000);

  if (!sock.connect(host,port))
  { activity.log("send_point: connect failed.");
    return false;
   }

  String msg = "";

  sock.sendString(user + "&" + client_version);
  msg = sock.receiveString();
  
  if (!msg.equals("ok"))
  { sock.disconnect();
    activity.log("send_point: connect refused.");
    return false;
   }

  int bat = (int)(0.5 + 100*activity.battery_level);

  msg = activity.format("point %d %.6f %.6f %.1f %.1f %.1f %.1f %.1f %d %d",
                                t,lon,lat,alt,acc,spd,dist,ascent,hrate, bat);
  sock.sendString(msg);
  msg = sock.receiveString(); // ok
  sock.disconnect();

  last_server_t = t;
  last_server_loc = loc;

  return true;
}


  

public boolean send_bitmap(Bitmap bmp, File jpg_file, 
                                       MyProgressDialog progressDialog)
{
    activity.log("send_waypoint: name = " + jpg_file.getName());

    GregorianCalendar cal = new GregorianCalendar();
    String time_str = 
       new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(cal.getTime()); 
    
    FileOutputStream out = null;

    try { out = new FileOutputStream(jpg_file);
          //bmp.compress(Bitmap.CompressFormat.JPEG, 100, out);
          bmp.compress(Bitmap.CompressFormat.JPEG, 95, out);
          activity.show_toast("Foto: " + jpg_file.getPath());
          out.close();
    } catch (Exception e){}
    
  
  activity.log("send: " + jpg_file.getPath());
  activity.log("size =  " + (int)jpg_file.length());
  
  final MyProgressDialog progress_diag = progressDialog;
  
  LedaSocket sock = new LedaSocket() {
      int last_kb = 0;
	  
	  @Override
	  public void progress_init(int sz)  
	  { final int kb_max = sz/1024;
            activity.log("total bytes: " + sz);
            activity.handler.post(new Runnable() {
              public void run() { 
                 progress_diag.setMax(kb_max);
                 
              }
          });
          
	  }
	  
	  @Override
	  public void progress(int x, int sz)
	  { final int kb = x/1024;
	    final int kb_max = sz/1024;	
	    
	    if (kb == last_kb) return;	
	    
	    activity.handler.post(new Runnable() {
             public void run() { 
            	activity.log(activity.format("%3d / %d kb",kb,kb_max));                     
                progress_diag.setProgress(kb);  
             }
         });	
        		   
        last_kb = kb; 
       }
  };  
          
  sock.setTimeout(5000);

  if (!sock.connect(host,port))
  { activity.log("send_waypoint: connect failed");
    return false;
   }

  
  sock.sendString(user + "&" + client_version);
  String msg = sock.receiveString();
  if (!msg.equals("ok"))
  { activity.log("send_waypoint: " + msg);
    sock.disconnect();
    return false;
   }
  
  sock.sendString(activity.format("bmp %s",time_str));
  msg = sock.receiveString();
  if (!msg.equals("ok")) 
  { activity.log("send_waypoint: " + msg);
    sock.disconnect();
    return false;
   }

  sock.sendFile(jpg_file);
  activity.log("send_waypoint finished");
  sock.disconnect();
  return true;
}


public void send_bitmap_old(String name, Bitmap bmp)
{ 
  int w = bmp.getWidth();
  int h = bmp.getHeight();
  int len = w*h;

  activity.log("send_bitmap:  len = " + len);

  int pixels[] = new int[len];
  bmp.getPixels(pixels,0,w,0,0,w,h);

  ByteBuffer byte_buf = ByteBuffer.allocate(4*len);
  for(int i=0; i<len; i++) byte_buf.putInt(pixels[i]);

  LedaSocket sock = new LedaSocket();
  sock.setTimeout(4000);

  if (!sock.connect(host,port))
  { activity.log("connect2 failed");
    return;
  }

  String cmd = activity.format("%s bitmap: %s",user,name);
  sock.sendString(cmd);
  String msg = sock.receiveString();

  if (msg != null && msg.equals("ok"))
    sock.sendBytes(byte_buf.array());

  sock.disconnect();
}

}
