package com.algobase.gpx;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.SimpleTimeZone;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import android.location.Location;
import android.location.LocationManager;

import com.algobase.stracks.sTracksRoot;


public class GpxReader extends DefaultHandler {

  private class TerminationException extends SAXException {
     TerminationException() {};
     @Override
     public String toString() { return "GPX Termination Exception"; }
  };

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



  private static final String TAG_TRACK = "trk";
  private static final String TAG_TRACK_POINT = "trkpt";
  private static final Object TAG_TRACK_SEGMENT = "trkseg";
  private static final String TAG_NAME = "name";
  private static final String TAG_DESCRIPTION = "desc";
  private static final String TAG_ALTITUDE = "ele";
  private static final String TAG_TIME = "time";
  private static final String ATT_LAT = "lat";
  private static final String ATT_LON = "lon";

  private static final String TAG_SPEED = "speed";
  private static final String TAG_HRATE = "hrate";
  private static final String TAG_POWER = "power";


  private boolean first_point_stop = false;
  //private boolean description_stop = false;

  private String content;
  private String description;

  private String trk_name;
  private int    trk_num;
  private int    seg_num;
  private double total_dist;

  private int num_points;

  private Location first_loc;
  private Location last_loc;
  private Location current_loc;
  private int current_hrate;
  private int current_power;

  //private LinkedList<Location> point_list;

  private Locator locator;
  private SimpleDateFormat xml_date_format;
  private SimpleDateFormat xml_date_format2;

  File gpx_file;
  


  public void write_log(String msg) {}

  public boolean point_handler(int line, Location loc, int hrate, int power) 
  { return true; }

  public void track_handler() {}

  
  public GpxReader(File file) {
     
     gpx_file= file;
     content = "";
     description = "";
     xml_date_format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
     xml_date_format2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

     //xml_date_format.setTimeZone(new SimpleTimeZone(0, "UTC"));
     xml_date_format.setTimeZone(TimeZone.getDefault());

     current_loc = null;
     last_loc = null;
     num_points = 0;
     //point_list = new LinkedList<Location>();
  }

  public int number_of_points() 
  { //return point_list.size(); 
    return num_points;
  }

  public int number_of_lines()
  { int lines = 0;
    try { BufferedReader br = new BufferedReader(new FileReader(gpx_file));
          while (br.readLine() != null) lines++;
    } catch (IOException e) {}
    return lines;
  }

  public void read()
  { write_log("gpx_read: " + gpx_file.toString());
    first_point_stop = false;
    try {
      FileInputStream is = new FileInputStream(gpx_file);        
      SAXParserFactory factory = SAXParserFactory.newInstance();
      SAXParser parser = factory.newSAXParser();
      parser.parse(is,this);
    } catch (Exception e) { write_log("gpx_read: " + e.toString()); }

    if (num_points > 0 && trk_num == 0)  // missing track end
    { trk_num++;
      track_handler();
     }

    write_log("gpx_read: description = " + description);

    if (description.equals("")) compute_description();
  }


  public void compute_description()
  { 
    long start_time = 0;
    long end_time   = 0;

    if (num_points > 0)
    { start_time = first_loc.getTime();
      end_time   = last_loc.getTime();
     }

   long total_time = end_time - start_time;

   description = "";

   String start_str = xml_date_format.format(new Date(start_time));
   String end_str = xml_date_format.format(new Date(end_time));

   description += format("version=%d;\n",sTracksRoot.GPX_VERSION);
   description += format("start=%s;\n", start_str);
   description += format("end=%s;\n",  end_str);
   description += format("time=%d;\n", total_time/1000);
   description += format("dist=%d;\n", (int)total_dist);
   description += format("breaks=%d;\n", 0);
   description += format("ascent=%d;\n", 0);
   description += format("avgspeed=%.1f;\n", 0.0);
   description += format("maxspeed=%.1f;\n", 0.0);
   description += format("points=%d;\n",  num_points);
   description += format("laps=%d;\n",  seg_num);

   write_log("gpx_read: compute description = " + description);
  }


  public String get_description()
  { 
    write_log("gpx_get_description: " + gpx_file.toString());
  
    first_point_stop = true;
    //description_stop = true;
	
    try {
      FileInputStream is = new FileInputStream(gpx_file);        
      SAXParserFactory factory = SAXParserFactory.newInstance();
      SAXParser parser = factory.newSAXParser();
      parser.parse(is,this);
    } catch (Exception e) { write_log("Exception " + e.toString()); }

    return description;
  }


  public long get_start_time()
  { 
    write_log("gpx_start_time: " + gpx_file.toString());

    first_point_stop = true;
	
    try {
      FileInputStream is = new FileInputStream(gpx_file);        
      SAXParserFactory factory = SAXParserFactory.newInstance();
      SAXParser parser = factory.newSAXParser();
      parser.parse(is,this);
    } catch (Exception e) { write_log("gpx_start_time: " + e.toString()); }

    long start_time = 0;
    //if (!point_list.isEmpty()) start_time = point_list.getFirst().getTime();
    if (first_loc != null) start_time = first_loc.getTime();

    return start_time;
  }


  public String generic_track_name()
  { SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd-HH-mm");
     long t = get_start_time();
     return f.format(new Date(t));
   }

  public String generic_file_name()
  { SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss.'gpx'");
     long t = get_start_time();
     return f.format(new Date(t));
   }

  


  @Override
  public void characters(char[] ch, int start, int length) throws SAXException 
  { String s = new String(ch,start,length);
    content += s;
   }

  @Override
  public void setDocumentLocator(Locator locator) {
    this.locator = locator;
  }

  private String createErrorMessage(String details) {
    StringBuffer msg = new StringBuffer();
    msg.append("Parsing error at line: ");
    msg.append(locator.getLineNumber());
    msg.append(" column: ");
    msg.append(locator.getColumnNumber());
    msg.append(". ");
    msg.append(details);
    return msg.toString();
  }


  @Override
  public void startElement(String uri, String localName, String name,
                           Attributes attributes) throws SAXException 
  {
     //write_log("startElement: localName = " + localName);
     //write_log("gpx parser: line = " + locator.getLineNumber());

    if (localName.equals(TAG_TRACK)) 
    { //write_log("startTrack");
      total_dist = 0;
      seg_num = 0;
      //point_list.clear();
      num_points = 0;
      first_loc = null;
      last_loc = null;
      current_loc = null;
      return;
     }

    if (localName.equals(TAG_TRACK_POINT)) 
    { //write_log("startTrackPoint");
      String latitude = attributes.getValue(ATT_LAT);
      String longitude = attributes.getValue(ATT_LON);
      double lat = Double.parseDouble(latitude);
      double lon = Double.parseDouble(longitude);

      //current_loc = new Location(LocationManager.GPS_PROVIDER);
      current_loc = new Location("gpx");
      current_loc.setLongitude(lon);
      current_loc.setLatitude(lat);
      current_loc.setAltitude(sTracksRoot.DISTANCE_UNDEFINED);
      return;
    }

    if (localName.equals(TAG_TRACK_SEGMENT)) 
    { //write_log("startTrackSegment: ");
      seg_num++;
      return;
     }
  }



  @Override
  public void endElement(String uri, String localName, String name)
                          throws SAXException 
  {
    // process these elements only as sub-elements of track
    //if (!isInTrackElement) { content = ""; return; }

    //write_log("endElement: localName = " + localName);


    if (localName.equals(TAG_TRACK_POINT))
    { 
      //write_log("endTrackpoint: " + content.toString().trim());

      num_points++;

      if (first_loc == null) first_loc = current_loc;

      //if (!point_list.isEmpty())
      if (last_loc != null)
      { //Location last_loc = point_list.getLast();
        double d = current_loc.distanceTo(last_loc);
        total_dist += d;
      }

      int lnum = locator.getLineNumber();
      boolean go = point_handler(lnum,current_loc,current_hrate,current_power);

      //point_list.add(current_loc);
      last_loc = current_loc;

      if (first_point_stop || !go) throw new TerminationException();
     }
    else
    if (localName.equals(TAG_ALTITUDE))
    { //write_log("endAltitude: " + content.toString().trim());
      double alt = 0;
      try { alt = Double.parseDouble(content.trim());
      } catch (NumberFormatException e) {}
      current_loc.setAltitude(alt);
    }
    else
    if (localName.equals(TAG_SPEED))
    { //write_log("endSpeed: " + content.toString().trim());
      float speed = 0;
      try { speed = Float.parseFloat(content.trim());
      } catch (NumberFormatException e) {}
      current_loc.setSpeed(speed);
    }
    else
    if (localName.equals(TAG_HRATE))
    { //write_log("endHrate: " + content.toString().trim());
      current_hrate = 0;
      try { current_hrate = Integer.parseInt(content.trim());
      } catch (NumberFormatException e) {}
    }
    else
    if (localName.equals(TAG_POWER))
    { //write_log("endPower: " + content.toString().trim());
      current_power = 0;
      try { current_power = Integer.parseInt(content.trim());
      } catch (NumberFormatException e) {}
    }
    else
    if (localName.equals(TAG_TIME))
    { //write_log("endTime: " + content.toString().trim());
      if (current_loc != null)
      { ParsePosition position = new ParsePosition(0);
        String s = content.toString().trim();
        Date d = xml_date_format.parse(s,position);
        if (d == null) {
          // fractional seconds
          d = xml_date_format2.parse(s,position);
        }
        if (d == null)
          write_log("endTime parsing error:" + s);
        else
          current_loc.setTime(d.getTime());
      }
     }
    else
    if (localName.equals(TAG_NAME)) 
    { //write_log("endName: " + content.toString().trim());
      trk_name = content.toString().trim();
     } 
    else
    if (localName.equals(TAG_DESCRIPTION)) 
    { //write_log("endDescription: " + content.toString().trim());
      description = content;
      //if (description_stop) throw new TerminationException();
     } 
    else
    if (localName.equals(TAG_TRACK_SEGMENT))
    { //write_log("endTrackSegment " + seg_num);
     }
    else
    if (localName.equals(TAG_TRACK)) 
    { trk_num++;
      //write_log("endTrack " + trk_num);
      track_handler();
     }

    content = "";
  }

}

