import java.io.File;
import java.io.RandomAccessFile;

/** Each line of file consists of space-separated ascii tokens. Token 0 is 
    epoch seconds, token 1 is latitude (deg), token 2 is longitude (deg).
    Encapsulates RandomAccessFile, remembers last record read.
*/
class LocationFile {

    String _prevRecord;
    long _prevEpochSec;
    String _filename;
    RandomAccessFile _file;

    LocationFile(String filename) throws Exception {
	_file = new RandomAccessFile(new File(filename), "r");
	_filename = new String(filename);
    }

    /** Interpolate file data to get lat/lon at specified time.  
	File MUST be in timetag-ascending order! */
    Location getLocation(long targetEpochSec)
	throws Exception {

	while (true) {
	    long startPos = _file.getFilePointer();
	    String record = _file.readLine();
	    if (record == null) {
		throw new Exception("Encountered " + _filename +  
				    " eof without finding record");
	    }

	    String[] recordTokens = record.split("\\s+");
	    if (recordTokens.length != 3) {
		System.err.println("zero-length record in " + _filename + "!");
		continue;
	    }

	    // First token is epoch time
	    long epochSec = Long.parseLong(recordTokens[0].trim());
	    if (epochSec == targetEpochSec) {
		_file.seek(startPos);   // Reposition to last-read location
		return new Location(epochSec,
				    Double.parseDouble(recordTokens[1].trim()),
				    Double.parseDouble(recordTokens[2].trim()));
		
	    }
	    if (epochSec > targetEpochSec) {
		if (startPos == 0) {
		    // First record timestamp exceeds target
		    _file.seek(startPos);   // Reposition to last-read location
		    throw new Exception("First " + _filename + 
					" record exceeds target time");
		}

		// Reposition to last-read location for next call
		_file.seek(startPos); 

		// Target time falls between records; interpolate position
		if (_prevRecord == null) {
		    throw new Exception("No location records for time " + targetEpochSec);
		}

		String[] prevTokens = _prevRecord.split("\\s+");

		// System.out.println("interpolate");
		return interpolateLocation(targetEpochSec,
					   _prevEpochSec,
					   Double.parseDouble(prevTokens[1].trim()),
					   Double.parseDouble(prevTokens[2].trim()),
					   epochSec, 
					   Double.parseDouble(recordTokens[1]),
					   Double.parseDouble(recordTokens[2].trim()));
		
	    }
	    _prevEpochSec = epochSec;
	    _prevRecord = new String(record);
	}
    }

    
    /** Compute linear interpolation of latitude and longitude */
    static Location interpolateLocation(long t, 
					long t1, double lat1, double lon1,
					long t2, double lat2, double lon2) {

	double lat = lat1 + (lat2 - lat1)*(t - t1)/(t2 - t1);
	double lon = lon1 + (lon2 - lon1)*(t - t1)/(t2 - t1);

	return new Location(t, lat, lon);
    }


    public static class Location {
	long _epochSec;
	double _latitude;
	double _longitude;

	Location(long epochSec, double lat, double lon) {
	    _epochSec = epochSec;
	    _latitude = lat;
	    _longitude = lon;
	}
    }

}
