/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2003-2006, Geotools Project Managment Committee (PMC)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation; either
 *    version 2.1 of the License, or (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.data.vpf.io;

import org.geotools.data.vpf.exc.VPFDataFormatException;


/**
 * Class TripletId.java is responsible for
 *
 * @author <a href="mailto:kobit@users.sourceforge.net">Artur Hefczyc</a>
 * @author <a href="mailto:knuterik@onemap.org">Knut-Erik Johnsen</a>, Project
 *         OneMap
 * @version 1.0.0
 * @author <a href="mailto:jeff@ionicenterprise.com">Jeff Yutzler</a>
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/vpf/src/main/java/org/geotools/data/vpf/io/TripletId.java $
 */
public class TripletId extends Number {
    /**
     * The raw data that can be decomposed into as many as three separate
     * numbers
     */
    private byte[] rawData = null;

    /**
     * Creates a new <code>TripletId</code> instance.
     *
     * @param data a <code>byte[]</code> value
     */
    public TripletId(byte[] data) {
        rawData = data;
    }

    /*
     *  (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString() {
        String result = new String();

        try {
            if (getIdLength() > 0) {
                result = new Integer(getId()).toString();
            }

            if (getTileIdLength() > 0) {
                result = result.concat("%")
                               .concat(new Integer(getTileId()).toString())
                               .trim();
            }

            if (getNextIdLength() > 0) {
                result = result.concat("%")
                               .concat(new Integer(getNextId()).toString())
                               .trim();
            }
        } catch (RuntimeException exp) {
            throw new VPFDataFormatException("This triplet is invalid.", exp);
        }

        return result;
    }

    /**
     * Returns the length in bytes of the ID
     *
     * @return an <code>int</code> value
     */
    private int getIdLength() {
        return (rawData[0] >> 6) & 3;
    }

    /**
     * Returns the length in bytes of the Tile ID
     *
     * @return an <code>int</code> value
     */
    private int getTileIdLength() {
        return (rawData[0] >> 4) & 3;
    }

    /**
     * Returns the length in bytes of the Next ID
     *
     * @return an <code>int</code> value
     */
    private int getNextIdLength() {
        return (rawData[0] >> 2) & 3;
    }

    /**
     * Returns the ID value
     *
     * @return Returns the ID, the first number of the triplet
     */
    public int getId() {
        int result = 0;
        int length = getIdLength();
        int piece;

        if (length > 0) {
            try {
                for (int inx = 0; inx < length; inx++) {
                    piece = rawData[inx + 1];

                    // Convert bytes from signed to unsigned
                    if (piece < 0) {
                        piece += (-2 * (Byte.MIN_VALUE));
                    }

                    result += (piece << (8 * inx));
                }
            } catch (RuntimeException exp) {
                exp.printStackTrace();
                result = 0;
            }
        }

        return result;
    }

    /**
     * Returns the Tile ID
     * @return Returns the Tile ID, the second number of the triplet
     */
    public int getTileId() {
        int result = 0;
        int length = getTileIdLength();
        int piece;

        if (length > 0) {
            int rowIdLength = getIdLength();

            try {
                for (int inx = 0; inx < length; inx++) {
                    piece = rawData[inx + rowIdLength + 1];

                    if (piece < 0) {
                        piece += (2 * Byte.MAX_VALUE);
                    }

                    result += (piece << (8 * inx));
                }
            } catch (RuntimeException exp) {
                exp.printStackTrace();
                result = 0;
            }
        }

        return result;
    }

    /**
     * Returns the Next ID
     * @return Returns the Next ID, the third number of the triplet
     */
    public int getNextId() {
        int result = 0;
        int length = getTileIdLength();
        int piece;

        if (length > 0) {
            int prevLength = getIdLength() + getTileIdLength();

            try {
                for (int inx = 0; inx < length; inx++) {
                    piece = rawData[inx + prevLength + 1];

                    if (piece < 0) {
                        piece += (2 * Byte.MAX_VALUE);
                    }

                    result += (piece << (8 * inx));
                }
            } catch (RuntimeException exp) {
                exp.printStackTrace();
                result = 0;
            }
        }

        return result;
    }

    /**
     * Describe <code>calculateDataSize</code> method here.
     *
     * @param definition a <code>byte</code> value indicating the details of the bytes
     *
     * @return an <code>int</code> value
     */
    public static int calculateDataSize(byte definition) {
        int[] pieces = new int[3];
        pieces[0] = (definition >> 2) & 3;
        pieces[1] = (definition >> 4) & 3;
        pieces[2] = (definition >> 6) & 3;

        int size = 0;

        for (int i = 0; i < pieces.length; i++) {
            switch (pieces[i]) {
            case 0:
                break;

            case 1:
                size++;

                break;

            case 2:
                size += 2;

                break;

            case 3:
                size += 4;

                break;

            default:
                System.out.println("Tripled id size decoding error");
                System.out.println("tripled definition: " + definition);
                System.out.println("piece 0: " + pieces[0]);
                System.out.println("piece 1: " + pieces[1]);
                System.out.println("piece 2: " + pieces[2]);

                break;
            }
        }

        return size;
    }

    /* (non-Javadoc)
     * @see java.lang.Number#doubleValue()
     */
    public double doubleValue() {
        return new Integer(getId()).doubleValue();
    }

    /* (non-Javadoc)
     * @see java.lang.Number#floatValue()
     */
    public float floatValue() {
        return new Integer(getId()).floatValue();
    }

    /* (non-Javadoc)
     * @see java.lang.Number#intValue()
     */
    public int intValue() {
        return getId();
    }

    /* (non-Javadoc)
     * @see java.lang.Number#longValue()
     */
    public long longValue() {
        return new Integer(getId()).longValue();
    }

    /* (non-Javadoc)
     * @see java.lang.Number#byteValue()
     */
    public byte byteValue() {
        return new Integer(getId()).byteValue();
    }

    /* (non-Javadoc)
     * @see java.lang.Number#shortValue()
     */
    public short shortValue() {
        return new Integer(getId()).shortValue();
    }
}
