package com.algobase.gpx;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Locale;

import android.location.Location;

public class TrkReader {

  static int prec_loc = 1000000;
  static int prec_dist_low  = 10;
  static int prec_dist_high = 10;  // old value (may be overridden)

  static final int prec_time = 1000;


  private String format(String pattern, Object... args)
  { return String.format(Locale.US,pattern,args); }


  BufferedReader reader;

  String trk_name;

  File trk_file;

  int current_val[] = new int[11];

/*
  long current_break = 0;
*/

  String info_line;

  String description;

  double total_dist;
  double total_ascent;
  double total_descent;

  double max_speed;
  float  sum_hrate;
  int    max_hrate;

  float  sum_power;
  int    max_power;

  float  sum_cadence;
  int    max_cadence;

  int  min_temperature;
  int  max_temperature;

  long total_break_time;

  int num_points;
  int num_tracks;
  int num_laps;
  int num_breaks;

  Location first_loc;
  Location last_loc;
  Location current_loc;

  boolean go = true;

  int file_pos; // byte position in file


  public void file_error(String fname, String msg) {}

  public boolean handle_track_begin(int i, String name) { return true; }

  public void handle_track_end() {}
  public void handle_lap(int i) {}
  public void handle_break(int i, long t) {}

  public boolean handle_trackpoint(int i, Location loc, double dist, 
                                                        double speed, 
                                                        double ascent, 
                                                        int hrate,
                                                        int power,
                                                        int cad,
                                                        int temp)
  { return true; }

  public int get_file_pos() { return file_pos; }

  
  public TrkReader(File file) {
     
     trk_file = file;

     file_pos = 0;

     num_points = 0;
     num_tracks = 0;
     num_laps = 0;
     num_breaks = 0;

     sum_hrate = 0;
     max_hrate = 0;

     sum_power = 0;
     max_power = 0;

     max_speed = 0;

     current_loc = null;
     last_loc = null;
     reader = null;

  }

  void open_reader() {
      if (reader != null) close_reader();
      try {
       reader = new BufferedReader(new FileReader(trk_file)); 
      } catch (IOException e) { file_error(trk_file.getName(),e.toString()); }
  }

  void close_reader() {
     if (reader == null) return;
     try { reader.close();
     } catch (IOException e) { file_error(trk_file.getName(),e.toString()); }
     reader = null;
  }
  
  
/*
  public void reset() 
  { num_points = 0;
    num_tracks = 0;
    num_laps = 0;
    num_breaks = 0;
    current_loc = null;
    last_loc = null;
    info_line = null;
    description = null;
    file_pos = 0;

    try {
      if (reader != null) reader.close();
      reader = new BufferedReader(new FileReader(trk_file)); 
    } catch (IOException e) { file_error(trk_file.getName(), e.toString());
                              reader = null;
                             }
  }
*/



  public Location getCurrentLocation() { return current_loc; }
  public Location getFirstLocation()   { return first_loc; }
  public Location getLastLocation()    { return last_loc; }

  public String getTrackName() { return trk_name; }

  public int getNumberOfPoints() { return num_points; }
  public int getNumberOfLaps() { return num_laps; }
  public int getNumberOfTracks() { return num_tracks; }
  public int getNumberOfBreaks() { return num_breaks; }

  public double getMaxSpeed() { return max_speed; }

  public int    getMaxHrate() { return max_hrate; }
  public int    getAvgHrate() { return (int)(0.5 + sum_hrate/num_points); }

  public int    getMaxPower() { return max_power; }
  public int    getAvgPower() { return (int)(0.5 + sum_power/num_points); }

  public int    getMaxCadence() { return max_cadence; }
  public int    getAvgCadence() { return (int)(0.5 + sum_cadence/num_points); }

  public int    getMaxTemperature() { return max_temperature; }
  public int    getMinTemperature() { return min_temperature; }

  public int  getElapsedTime()     
  { if (first_loc == null || current_loc == null) return 0;
    return (int)((current_loc.getTime() - first_loc.getTime())/prec_time);
   }

  public int getTotalBreakTime() { return (int)(total_break_time/prec_time); }    
  public int getTotalTime() { return getElapsedTime() - getTotalBreakTime(); }


  public double getTotalDistance() { return total_dist; }
  public double getTotalAscent()   { return total_ascent; }
  public double getTotalDescent()  { return total_descent; }

  private void parse_line(String line)
  { 
    char buf[] = line.toCharArray();
    char x = buf[0];
    int count = 0;

/*
    // is_break: all entries (except of time) are zero
    boolean is_break = true;
    int  val0 = 0;
*/

    int i=0;

    while (i < buf.length && count < current_val.length)
    { int num = 0;
      int sign = 1;
      if (buf[i++] == '-') sign = -1;
      while (i < buf.length)
      { char c = buf[i];
        if (c == ' ')
        { if (buf[i+1] == '-') i++;
          break;
         }
        if (c == '-' || c == '+') break;
        num = 10*num + (c - '0');
        i++;
       }

      if (x == 'P')
         current_val[count] = sign*num;
      else
         current_val[count] += sign*num;

/*
      if (count == 0) val0 = num;
      if (count > 0 && num > 0) is_break = false;
*/

      count++;
    }

/*
    if (is_break) 
      current_break = prec_time*(current_val[0] + val0); 
    else
      current_break = 0; 
*/

  }

    
  private String read_line()
  { String line = null;
    if (reader != null) {
       try { line = reader.readLine();
       } catch (IOException e) {}
    }
    if (line != null) file_pos += (line.length() + 1);
    return line;
  }

  public String readComment()
  { String result = null;
    String line;
    while ((line = read_line()) != null)
    { if (line.length() == 0 || line.charAt(0) != '!')  continue;
      result = line.substring(1).trim();
      break;
     }
    return result;
  }

  public String readInfoLine()
  { String result = null;
    open_reader();
    String line;
    while ((line = read_line()) != null)
    { if (line.length() == 0 || line.charAt(0) != 'I')  continue;
      result = line.substring(1).trim();
      break;
     }
    close_reader();
    return result;
  }


  public String readInfoLine(String name)
  { String result = null;
    open_reader();
    String line;
    while ((line = read_line()) != null)
    { if (line.length() == 0 || line.charAt(0) != 'I')  continue;
      line = line.substring(1);
      if (line.startsWith(name))
      { result = line.trim();
        break;
       }
    }
    close_reader();
    return result;
  }


  public String readLabels()
  { String result = null;
    open_reader();
    String line;
    while ((line = read_line()) != null)
    { if (line.length() == 0 || line.charAt(0) != '@') continue;
      result = line.substring(1).trim();
      break;
    }
    close_reader();
    return result;
  }

  public String readDescription()
  { String result = null;
    open_reader();
    String line;
    while ((line = read_line()) != null)
    { if (line.length() == 0 || line.charAt(0) != '!') continue;
      result = line.substring(1).trim();
      break;
    }
    close_reader();
    return result;
  }



  public String createInfoLine()
  {
    read();

    if (first_loc == null) return null;

    int t_start = (int)(first_loc.getTime()/1000);
    int t_end   = (int)(last_loc.getTime()/1000);

    int time = getTotalTime();
    int breaks = getTotalBreakTime();

    double dist = getTotalDistance();
    double ascent = getTotalAscent();
    double max_speed = getMaxSpeed();
    int    max_hrate = getMaxHrate();
    int    avg_hrate = getAvgHrate();

    int    max_power = getMaxPower();
    int    avg_power = getAvgPower();

    int    max_cad = getMaxCadence();
    int    avg_cad = getAvgCadence();

    int    max_tmp = getMaxTemperature();
    int    min_tmp = getMinTemperature();

    return addInfoLines(null,null,
                       t_start,t_end,time,breaks,num_points,num_laps, dist,
                       ascent,max_speed,avg_hrate,max_hrate,avg_power,max_power,
                       avg_cad,max_cad,min_tmp,max_tmp,null);
   }





  public String addInfoLines(String labels,
                             String description,
                             int t_start, int t_end, int time, int breaks,
                             int points, int laps, double dist, 
                             double ascent, double max_speed,
                             int avg_hrate, int max_hrate,
                             int avg_power, int max_power,
                             int avg_cad, int max_cad,
                             int min_tmp, int max_tmp, String misc)
  {
     File tmp = new File(trk_file.getParent(),"t.trk");
     String info = "";
     info += format("start=%d;",t_start);
     info += format("end=%d;",t_end);
     info += format("laps=%d;",laps);
     info += format("points=%d;",points);
     info += format("time=%d;",time);
     info += format("breaks=%d;",breaks);
     info += format("dist=%.1f;",dist);
     info += format("ascent=%.1f;",ascent);
     info += format("maxspeed=%.1f;",max_speed);
     info += format("avghrate=%d;",avg_hrate);
     info += format("maxhrate=%d;",max_hrate);
     info += format("avgpower=%d;",avg_power);
     info += format("maxpower=%d;",max_power);
     info += format("avgcadence=%d;",avg_cad);
     info += format("maxcadence=%d;",max_cad);
     info += format("mintemp=%d;",min_tmp);
     info += format("maxtemp=%d;",max_tmp);

     if (misc != null) info += format("misc=%s;",misc);

     copyToFile(tmp,labels,description,info);
     trk_file.delete();
     tmp.renameTo(trk_file);

     return info;
  }


  public void addInfoLines(String labels,String descr,String info)
  { File tmp = new File(trk_file.getParent(),"t.trk");
    copyToFile(tmp,labels,descr,info);
    trk_file.delete();
    tmp.renameTo(trk_file);
  }



  public void addLabels(String labels)
  { File tmp = new File(trk_file.getParent(),"t.trk");
    copyToFile(tmp,labels,null,null);
    trk_file.delete();
    tmp.renameTo(trk_file);
  }

  public void addDescription(String descr)
  { File tmp = new File(trk_file.getParent(),"t.trk");
    copyToFile(tmp,null,descr,null);
    trk_file.delete();
    tmp.renameTo(trk_file);
  }




  public int number_of_points()
  { 
    int count = 0;
    open_reader();

    String line;
    while ((line = read_line()) != null) { 
            char c = line.charAt(0);
            if (c == 'P' || c == 'D') count++;
     }

    close_reader();
    return count;
  }



  public void read()
  { 
    open_reader();

    String line;
    while (go && (line = read_line()) != null)
    { 
      if (line.length() == 0) continue;

      switch (line.charAt(0)) {

      case '#': // comment
                break;

      case '!': { // description
                  description = line.substring(1).trim();
                  break;
                 }

      case 'I': { // info line
                  info_line = line.substring(1).trim();
                  break;
                 }

      case 'X': { // precisions
                  String prec_line = line.substring(1).trim();
                  String[] values = prec_line.split(" ");
                  if (values.length > 0)
                     prec_loc = Integer.parseInt(values[0]);
                  if (values.length > 1)
                     prec_dist_high = Integer.parseInt(values[1]);
                  if (values.length > 2)
                     prec_dist_low = Integer.parseInt(values[2]);
                  break;
                }


      case 'T': { // track
                  int p = line.indexOf("#");
                  if (p == -1) p = line.length();
                  trk_name = line.substring(1,p).trim();
                  for(int i=0; i<current_val.length; i++) current_val[i]= 0;
                  total_dist = 0;
                  total_ascent = 0;
                  total_descent = 0;
                  total_break_time = 0;
                  max_speed = 0;
                  first_loc = null;
                  last_loc = null;
                  current_loc = null;
                  go = handle_track_begin(num_tracks, trk_name);
                  num_tracks++;
                  break;
                }

      case 'P': 
      case 'D': { // point or diff
                  parse_line(line);
                  long t       = prec_time * (long)current_val[0];
                  double lon   = (double)current_val[1]/prec_loc;
                  double lat   = (double)current_val[2]/prec_loc;
                  double alt   = (double)current_val[3]/prec_dist_low;
                  double ascent= (double)current_val[6]/prec_dist_low;

                  double speed = (double)current_val[4]/prec_dist_high;
                  double dist  = (double)current_val[5]/prec_dist_high;

                  int hrate = current_val[7];
                  int power = current_val[8];

                  int cad = current_val[9];
                  int temp = current_val[10];

                  if (current_loc != null &&
                      lon == current_loc.getLongitude() && 
                      lat == current_loc.getLatitude() && 
                      alt == current_loc.getAltitude())
                  { // break
                    long brk_t= t - current_loc.getTime();
                    if (brk_t > 10000)  // 10 sec
                    { num_breaks++;
                      total_break_time += brk_t;
                     }
                   }

                  total_dist = dist;
                  total_ascent = ascent;

                  current_loc = new Location("trk");
                  current_loc.setTime(t);
                  current_loc.setLatitude(lat);
                  current_loc.setLongitude(lon);
                  current_loc.setAltitude(alt);
                  current_loc.setAccuracy(5.0f);
                  current_loc.setSpeed((float)speed);

                  if (first_loc == null) first_loc = current_loc;

                  if (speed > max_speed) max_speed = speed;

                  if (hrate > max_hrate) max_hrate = hrate;
                  sum_hrate += hrate;

                  if (power > max_power) max_power = power;
                  sum_power += power;

                  if (cad > max_cadence) max_cadence = cad;
                  sum_cadence += cad;

                  if (temp > max_temperature) max_temperature = temp;
                  if (temp < min_temperature || min_temperature == -100) 
                    min_temperature = temp;

                  last_loc = current_loc;
                  go = handle_trackpoint(num_points, current_loc, dist, 
                                         speed, ascent,hrate,power,cad,temp) ;
                  num_points++;

/*
                  if (current_break > 0) {
                    handle_break(num_breaks,current_break);
                    num_breaks++;
                  }
*/

                  break;
                }

      case 'L': { // lap
                  handle_lap(num_laps++);
                  break;
                 }

      case 'B': { // break
                  parse_line(line); 
                  long t = prec_time * (long)current_val[0];
                  handle_break(num_breaks,t);
                  num_breaks++;
                  break;
                 }

     }

    }

    handle_track_end();

    close_reader();
  }

  public boolean copyToFile(File file, String labels, String description,
                                                      String info)
  { 
    open_reader();

    BufferedWriter writer = null;
    try { writer = new BufferedWriter(new FileWriter(file));
    } catch (IOException e) { e.printStackTrace(); }

    if (writer == null) return false;

    String line = read_line();
    
    if (line == null) return false;

    try { writer.write(line);
          writer.newLine();
          if (labels != null)
          { writer.write("@" + labels);
            writer.newLine();
           }
          if (description != null)
          { writer.write("!" + description);
            writer.newLine();
           }
          if (info != null)
          { writer.write("I" + info);
            writer.newLine();
           }
    } catch(IOException e) {}


    while ((line = read_line()) != null)
    { 
      if (info != null)
      { // skip existing info lines
        if (line.length() > 0 && line.charAt(0) == 'I') continue;
       }

      if (labels != null)
      { // skip existing label lines
        if (line.length() > 0 && line.charAt(0) == '@') continue;
       }

      if (description != null)
      { // skip existing comment lines
        if (line.length() > 0 && line.charAt(0) == '!') continue;
       }

      try { writer.write(line);
            writer.newLine();
      } catch(IOException e) {}
     }

    try { writer.close(); } catch(IOException e) {} 
    close_reader();
    return true;
  }

}

