import java.lang.Exception;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import crosby.binary.BinaryParser;
import crosby.binary.Osmformat;
import crosby.binary.file.BlockInputStream;

class ReadPBF {

    public static void main(String args[]) {
        String filename = args[0];

        System.out.println("Reading: " + filename);

        File input = new File(filename);

        try {
            InputStream is = new FileInputStream(input);

            parse(is);
        } catch (FileNotFoundException e) {
            System.out.println("Error: File not found!");
            System.exit(1);
        }
    }

    public static void parse(InputStream is) {
        try {
            BinParser reader = new BinParser();
            BlockInputStream stream = new BlockInputStream(is, reader);
            stream.process();
        } catch (IOException e) {
            System.out.println("Error: Failed to parse PBF");
            System.exit(1);
        }
    }

    public static class BinParser extends BinaryParser {

        protected void parse(Osmformat.HeaderBlock header) {
            System.out.println("Parsing HeaderBlock");

            if (header.hasBbox()) {
                double multiplier = .000000001;
                double maxLon = header.getBbox().getRight() * multiplier;
                double minLon = header.getBbox().getLeft() * multiplier;
                double maxLat = header.getBbox().getTop() * multiplier;
                double minLat = header.getBbox().getBottom() * multiplier;

                System.out.println(" BBOX: " + minLat + "," + minLon + "," + maxLat + "," + maxLon);
            }
            for (String s : header.getRequiredFeaturesList()) {
                System.out.println(" Feature: " + s);
            }

        }
        protected void parseNodes(List<Osmformat.Node> nodes) {
            System.out.println("Parsing nodes");

            for (Osmformat.Node n : nodes) {
                System.out.println(" node:" + n.getId());
                System.out.println("  Lat/Lon: " + n.getLat() + "," + n.getLon());

                System.out.println("  Tags: " + n.getKeysCount());
                for (int j = 0; j < n.getKeysCount(); j++) {
                    String key = getStringById(n.getKeys(j));
                    String val = getStringById(n.getVals(j));

                    System.out.println("   " + key + "=" + val);
                }
            }
        }

        protected final void parseDense(Osmformat.DenseNodes nodes) {
            System.out.println("Parsing dense nodes");

            long lastId = 0, lastLat = 0, lastLon = 0;

            int kvid = 0;

            for (int i = 0; i < nodes.getIdCount(); i++) {
                long lat = nodes.getLat(i) + lastLat;
                long lon = nodes.getLon(i) + lastLon;
                long id = nodes.getId(i) + lastId;
                lastLat = lat;
                lastLon = lon;
                lastId = id;

                System.out.println(" node:" + id);
                System.out.println("  Lat/Lon: " + lat + "," + lon);

                if (nodes.getKeysValsCount() > 0) {
                    while (nodes.getKeysVals(kvid) != 0) {
                        int keyid = nodes.getKeysVals(kvid++);
                        int valid = nodes.getKeysVals(kvid++);
                        String key = getStringById(keyid);
                        String val = getStringById(valid);

                        System.out.println("   " + key + "=" + val);
                    }
                    kvid++; // Skip over the '0' delimiter.
                }
            }
        }

        protected void parseWays(List<Osmformat.Way> ways) {
            System.out.println("Parsing ways");

            for (Osmformat.Way w : ways) {
                System.out.println(" way:" + w.getId());

                System.out.println("  Tags: " + w.getKeysCount());
                for (int j = 0; j < w.getKeysCount(); j++) {
                    String key = getStringById(w.getKeys(j));
                    String val = getStringById(w.getVals(j));

                    System.out.println("   " + key + "=" + val);
                }

                System.out.println("");
                System.out.println("  Nodes: " + w.getRefsCount());

                long nid = 0;
                for (long idDelta : w.getRefsList()) {
                    nid += idDelta;
                    System.out.println("   nd:" + nid);
                }
            }
        }

        protected void parseRelations(List<Osmformat.Relation> rels) {
            System.out.println("Parsing relations");

            for (Osmformat.Relation r : rels) {
                System.out.println(" relation:" + r.getId());

                System.out.println("  Tags: " + r.getKeysCount());
                for (int j = 0; j < r.getKeysCount(); j++) {
                    String key = getStringById(r.getKeys(j));
                    String val = getStringById(r.getVals(j));

                    System.out.println("   " + key + "=" + val);
                }

                System.out.println("");
                System.out.println("  Members: " + r.getMemidsCount());

                long lastMid = 0;

                for (int j = 0; j < r.getMemidsCount(); j++) {
                    long mid = lastMid + r.getMemids(j);
                    lastMid = mid;
                    String role = getStringById(r.getRolesSid(j));

                    System.out.println("   role:" + role);
                    System.out.println("   type:" + r.getTypes(j));
                    System.out.println("    ref:" + mid);
                    System.out.println("");
                }
            }
        }

	public void complete() {
            System.out.println("Parsing complete");
	}
    }
}

