/**
 * Simple program to convert Palm Fuel Log data into csv files.    
 * This program has been tested data from an IBM C3 (Palm Vx) 
 * with Fuel Log 1.0.
 *
 * License: Do whatever you want.
 *
 * Warranty: No warranty.    None.    Nil.
 * 
 * Build:
 *     javac FuelLog.java
 *     jar cvf fuellog.jar *.class
 * Usage:
 *     java -cp fuellog.jar FuelLog fuelLogDB.PDB 
 *
 * Alternate Build:
 *     javac FuelLog.java
 * Alternate Usage: 
 *     java -cp . FuelLog fuelLogDB.PDB 
 */

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;

public class FuelLog {
    public static void main(String []argv) throws Exception {
        if (argv.length < 1) {
            System.out.println("Usage: java -cp . FuelLog [filename]");
        }
        FileInputStream input = new FileInputStream(argv[0]);
        FileChannel channel = input.getChannel();
        int fileLength = (int)channel.size();
        MappedByteBuffer buffer =
            channel.map(FileChannel.MapMode.READ_ONLY, 0, fileLength);
        
        int n = buffer.getShort(76);
        ArrayList<Integer> offsets = new ArrayList<Integer>(n);
        ArrayList<Integer> cats = new ArrayList<Integer>(n);
        for(int i=0; i<n; ++i) {
            offsets.add((int) buffer.getShort(80 + i*8));
            cats.add(buffer.get(80 + i*8 + 2) & 15);
        }
        int where = 80 + n*8;
        int ncars = buffer.getShort(where);
        if (ncars > 16) ncars = 16;
        where += 2;
        ArrayList<String> carNames = new ArrayList<String>(16);
        Map<Integer, ArrayList<Entry>> entries =
            new HashMap<Integer, ArrayList<Entry>>(16);
        for(int i=0; i<16; ++i) {
            String s = getCString(buffer, where + 16*i).trim();
            carNames.add(s);
            entries.put(i, new ArrayList<Entry>());
        }
        where += 16*16 + 22;
        int dfuel, dprice, dodometer;
        dfuel = buffer.getShort(where);
        dprice = buffer.getShort(where + 2);
        dodometer = buffer.getShort(where + 4);
        where += 6;
        for(int i=0; i<n; ++i) {
            try {
                Entry entry =
                    new Entry(
                        buffer, offsets.get(i), dfuel, dprice, dodometer);
                entries.get(cats.get(i)).add(entry);
            } catch (Exception e) {
                continue;
            }
        }
        input.close();
        String format = "%d/%d/%d, " +
            "%." + dodometer + "f, " +
            "%." + dfuel + "f, " +
            "%." + dprice + "f, " +
            "%c\n";
        for(int i=0; i<16; ++i) {
            if ("".equals(carNames.get(i))) continue;
            ArrayList<Entry> list = entries.get(i);
            if (list.size() == 0) continue;
            String out = carNames.get(i) + ".csv";
            PrintStream printer = new PrintStream(new FileOutputStream(out));
            printer.print("date, odometer, volume, cost, full\n");
            for(int j=0; j<list.size(); ++j) {
                Entry e = list.get(j);
                printer.format(
                    format,
                    e.month, e.day, e.year,
                    e.odometer, e.fuel, e.price, 
                    (e.fulltank ? 'Y' : 'N'));
            }
            printer.flush();
            printer.close();
        }
    }

    static class Entry {
        double odometer;
        double fuel;
        int year, month, day;
        double price;
        boolean fulltank;

        public Entry(ByteBuffer buffer, int i,
                     int dfuel, int dprice, int dodometer) {
            odometer = buffer.getInt(i) * 1.0;
            fuel = buffer.getInt(i+4) * 1.0;
            int d = buffer.getShort(i+8);
            price = buffer.getInt(i+10) * 1.0;
            fulltank = buffer.getShort(i+14) != 0 ? true : false;
            for(int j=0; j<dodometer; ++j) odometer /= 10.0;
            for(int j=0; j<dfuel; ++j) fuel /= 10.0;
            for(int j=0; j<dprice; ++j) price /= 10.0;
            day = d & 31;
            d = d >> 5;
            month = d & 15;
            year = ((d >> 4) & 127 ) + 1904;
        }
    }

    static String getCString(ByteBuffer b, int i) {
        byte c = 127;
        String s = "";
        for(int j=0; j<16 && c != 0; ++j) {
            c = b.get(i + j);
            if (c == 0) break;
            s = s + (char)(c & 0xFF);
        } 
        return s;
    }
}
