/*
 *    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.util;

import org.geotools.data.vpf.ifc.DataTypesDefinition;
import org.geotools.data.vpf.io.VPFDate;


/**
 * Class DataUtils.java is responsible for a bunch of 
 * miscellaneous operations for reading and converting
 * data
 * 
 * <p>
 * Created: Wed Jan 29 10:06:37 2003
 * </p>
 *
 * @author <a href="mailto:kobit@users.sourceforge.net">Artur Hefczyc</a>
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/vpf/src/main/java/org/geotools/data/vpf/util/DataUtils.java $
 * @version $Id: DataUtils.java 22482 2006-10-31 02:58:00Z desruisseaux $
 */
public class DataUtils implements DataTypesDefinition {
    /**
     * Describe <code>toBigEndian</code> method here.
     *
     * @param source a <code>byte[]</code> value
     * @return a <code>byte[]</code> value
     */
    public static byte[] toBigEndian(byte[] source) {
        byte[] result = new byte[source.length];

        for (int i = 0; i < source.length; i++) {
            result[i] = source[source.length - (i + 1)];
        }

        return result;
    }

    /**
     * Describe <code>decodeData</code> method here.
     *
     * @param bytes a <code>byte[]</code> value
     * @param type a <code>char</code> value
     * @return an <code>Object</code> value
     */
    public static Object decodeData(byte[] bytes, char type) {
        Object result = null;

        switch (type) {
        case DATA_TEXT:
        case DATA_LEVEL1_TEXT:
        case DATA_LEVEL2_TEXT:
        case DATA_LEVEL3_TEXT:

            StringBuffer sb = new StringBuffer(bytes.length);

            for (int i = 0; i < bytes.length; i++) {
                sb.append((char) bytes[i]);
            }

            boolean isNull = false;

            for (int i = 0; i < STRING_NULL_VALUES.length; i++) {
                isNull |= sb.toString().trim()
                            .equalsIgnoreCase(STRING_NULL_VALUES[i]);
            }

            if (isNull) {
                result = null;
            } else {
                result = sb.toString().trim();
            }

            break;

        case DATA_SHORT_FLOAT:
            result = new Float(decodeFloat(bytes));

            break;

        case DATA_LONG_FLOAT:
            result = new Double(decodeDouble(bytes));

            break;

        case DATA_SHORT_INTEGER:
            result = new Short(decodeShort(bytes));

            break;

        case DATA_LONG_INTEGER:
            result = new Integer(decodeInt(bytes));

            break;

        case DATA_2_COORD_F:
//        {
//            // I doubt this is being used
//          float[][] coords = new float[bytes.length / DATA_2_COORD_F_LEN][2];
//          byte[] floatData = new byte[DATA_SHORT_FLOAT_LEN];
//
//          for (int i = 0; i < coords.length; i++) {
//              copyArrays(floatData, bytes, i * DATA_2_COORD_F_LEN);
//              coords[i][0] = decodeFloat(floatData);
//              copyArrays(floatData, bytes, i * (DATA_2_COORD_F_LEN + 1));
//              coords[i][1] = decodeFloat(floatData);
//          }
//          DirectPosition[] coords = new DirectPosition2D[bytes.length / DATA_2_COORD_F_LEN];
//          double xval, yval;
//          byte[] floatData = new byte[DATA_SHORT_FLOAT_LEN];
//
//          for (int i = 0; i < coords.length; i++) {
//              copyArrays(floatData, bytes, i * DATA_2_COORD_F_LEN);
//              xval = decodeFloat(floatData);
//              copyArrays(floatData, bytes, i * (DATA_2_COORD_F_LEN + 1));
//              yval = decodeFloat(floatData);
//              coords[i] = new DirectPosition2D(xval, yval);
//          }

//          result = coords;
//            result = new Coordinate2DFloat(coords);
//        }
//
//        break;

        case DATA_2_COORD_R:
//        {
            // I doubt this is being used
//            DirectPosition[] coords = new DirectPosition2D[bytes.length / DATA_2_COORD_R_LEN];
//            double xval, yval;
//            byte[] doubleData = new byte[DATA_LONG_FLOAT_LEN];
//
//            for (int i = 0; i < coords.length; i++) {
//                copyArrays(doubleData, bytes, i * DATA_2_COORD_R_LEN);
//                xval = decodeDouble(doubleData);
//                copyArrays(doubleData, bytes, i * (DATA_2_COORD_R_LEN + 1));
//                yval = decodeDouble(doubleData);
//                coords[i] = new DirectPosition2D(xval, yval);
//            }
//
//            result = coords;
//            double[][] coords = new double[bytes.length / DATA_2_COORD_R_LEN][2];
//            byte[] doubleData = new byte[DATA_LONG_FLOAT_LEN];
//
//            for (int i = 0; i < coords.length; i++) {
//                copyArrays(doubleData, bytes, i * DATA_2_COORD_R_LEN);
//                coords[i][0] = decodeDouble(doubleData);
//                copyArrays(doubleData, bytes, i * (DATA_2_COORD_R_LEN + 1));
//                coords[i][1] = decodeDouble(doubleData);
//            }
//
//            result = new Coordinate2DDouble(coords);
//        }
//
//        break;
//
        case DATA_3_COORD_F:
//        {
          // I doubt this is being used
//            DirectPosition[] coords = new DirectPosition2D[bytes.length / DATA_2_COORD_R_LEN];
//            double xval, yval, zval;
//            byte[] floatData = new byte[DATA_SHORT_FLOAT_LEN];
//
//            for (int i = 0; i < coords.length; i++) {
//                copyArrays(floatData, bytes, i * DATA_3_COORD_F_LEN);
//                xval = decodeFloat(floatData);
//                copyArrays(floatData, bytes, i * (DATA_3_COORD_F_LEN + 1));
//                yval = decodeFloat(floatData);
//                copyArrays(floatData, bytes, i * (DATA_3_COORD_F_LEN + 2));
//                zval = decodeFloat(floatData);
//                coords[i] = new GeneralDirectPosition(xval, yval, zval);
//            }
//
//            result = coords;
//            float[][] coords = new float[bytes.length / DATA_3_COORD_F_LEN][3];
//            byte[] floatData = new byte[DATA_SHORT_FLOAT_LEN];
//
//            for (int i = 0; i < coords.length; i++) {
//                copyArrays(floatData, bytes, i * DATA_3_COORD_F_LEN);
//                coords[i][0] = decodeFloat(floatData);
//                copyArrays(floatData, bytes, i * (DATA_3_COORD_F_LEN + 1));
//                coords[i][1] = decodeFloat(floatData);
//                copyArrays(floatData, bytes, i * (DATA_3_COORD_F_LEN + 2));
//                coords[i][2] = decodeFloat(floatData);
//            }
//
//            result = new Coordinate3DFloat(coords);
//        }
//
//        break;
//
        case DATA_3_COORD_R:
//        {
            // I doubt this is being used
//            DirectPosition[] coords = new DirectPosition2D[bytes.length / DATA_2_COORD_R_LEN];
//            double xval, yval, zval;
//            byte[] doubleData = new byte[DATA_LONG_FLOAT_LEN];
//
//            for (int i = 0; i < coords.length; i++) {
//                copyArrays(doubleData, bytes, i * DATA_3_COORD_R_LEN);
//                xval = decodeDouble(doubleData);
//                copyArrays(doubleData, bytes, i * (DATA_3_COORD_R_LEN + 1));
//                yval = decodeDouble(doubleData);
//                copyArrays(doubleData, bytes, i * (DATA_3_COORD_R_LEN + 2));
//                zval = decodeDouble(doubleData);
//                coords[i] = new GeneralDirectPosition(xval, yval, zval);
//            }
//
//            result = coords;
//            double[][] coords = new double[bytes.length / DATA_3_COORD_R_LEN][3];
//            byte[] doubleData = new byte[DATA_LONG_FLOAT_LEN];
//
//            for (int i = 0; i < coords.length; i++) {
//                copyArrays(doubleData, bytes, i * DATA_3_COORD_R_LEN);
//                coords[i][0] = decodeDouble(doubleData);
//                copyArrays(doubleData, bytes, i * (DATA_3_COORD_R_LEN + 1));
//                coords[i][1] = decodeDouble(doubleData);
//                copyArrays(doubleData, bytes, i * (DATA_3_COORD_R_LEN + 2));
//                coords[i][2] = decodeDouble(doubleData);
//            }
//
//            result = new Coordinate3DDouble(coords);
//        }
        {    
            throw new RuntimeException("If this code is actually being used, replace it with equivalent code from VPFFile.");
        }
        case DATA_DATE_TIME:
            result = new VPFDate(bytes);

            break;

        case DATA_NULL_FIELD:
            break;

        case DATA_TRIPLET_ID:
        default:
            break;
        }

        return result;
    }

    /**
     * Describe <code>copyArrays</code> method here.
     *
     * @param dest a <code>byte[]</code> value
     * @param source a <code>byte[]</code> value
     * @param fromIdx an <code>int</code> value
     */
    public static void copyArrays(byte[] dest, byte[] source, int fromIdx) {
        for (int i = 0; i < dest.length; i++) {
            dest[i] = source[i + fromIdx];
        }
    }

    /**
     * Describe <code>decodeShort</code> method here.
     *
     * @param bytes a <code>byte[]</code> value
     * @return a <code>short</code> value
     */
    public static short decodeShort(byte[] bytes) {
        short res = 0;
        int shift = 8;

        for (int i = 0; (i < bytes.length) && (shift >= 0); i++) {
            res |= ((short) (bytes[i] & 0xff) << shift);
            shift -= 8;
        }

        return res;
    }

    //   public static int littleEndianToInt(byte[] fourBytes)
    //   {
    //     int res = 0;
    //     int limit = Math.min(fourBytes.length, 4);
    //     for (int i = 0; i < limit; i++)
    //     {
    //       res |= (fourBytes[i] & 0xFF) << (i*8);
    //     } // end of for (int i = 0; i < limit-1; i++)
    //     return res;
    //   }

    /**
     * Describe <code>decodeInt</code> method here.
     *
     * @param bytes a <code>byte[]</code> value
     * @return an <code>int</code> value
     */
    public static int decodeInt(byte[] bytes) {
        int res = 0;
        int shift = 24;

        for (int i = 0; (i < bytes.length) && (shift >= 0); i++) {
            res |= ((bytes[i] & 0xff) << shift);
            shift -= 8;
        }

        return res;
    }

    /**
     * Describe <code>decodeFloat</code> method here.
     *
     * @param bytes a <code>byte[]</code> value
     * @return a <code>float</code> value
     */
    public static float decodeFloat(byte[] bytes) {
        int res = 0;
        int shift = 24;

        for (int i = 0; (i < bytes.length) && (shift >= 0); i++) {
            res |= ((bytes[i] & 0xff) << shift);
            shift -= 8;
        }

        return Float.intBitsToFloat(res);
    }

    /**
     * Describe <code>decodeDouble</code> method here.
     *
     * @param bytes a <code>byte[]</code> value
     * @return a <code>double</code> value
     */
    public static double decodeDouble(byte[] bytes) {
        long res = 0;
        int shift = 56;

        for (int i = 0; (i < bytes.length) && (shift >= 0); i++) {
            res |= ((long) (bytes[i] & 0xff) << shift);
            shift -= 8;
        }

        return Double.longBitsToDouble(res);
    }

    /**
     * Describe <code>unsigByteToInt</code> method here.
     *
     * @param b a <code>byte</code> value
     * @return an <code>int</code> value
     */
    public static int unsigByteToInt(byte b) {
        return (int) b & 0xFF;
    }

    /**
     * Describe <code>getDataTypeSize</code> method here.
     *
     * @param type a <code>char</code> value
     * @return an <code>int</code> value
     */
    public static int getDataTypeSize(char type) {
        int size = -1;

        switch (type) {
        case DATA_TEXT:
        case DATA_LEVEL1_TEXT:
        case DATA_LEVEL2_TEXT:
        case DATA_LEVEL3_TEXT:
            size = 1;

            break;

        case DATA_SHORT_FLOAT:
            size = DATA_SHORT_FLOAT_LEN;

            break;

        case DATA_LONG_FLOAT:
            size = DATA_LONG_FLOAT_LEN;

            break;

        case DATA_SHORT_INTEGER:
            size = DATA_SHORT_INTEGER_LEN;

            break;

        case DATA_LONG_INTEGER:
            size = DATA_LONG_INTEGER_LEN;

            break;

        case DATA_2_COORD_F:
            size = DATA_2_COORD_F_LEN;

            break;

        case DATA_2_COORD_R:
            size = DATA_2_COORD_R_LEN;

            break;

        case DATA_3_COORD_F:
            size = DATA_3_COORD_F_LEN;

            break;

        case DATA_3_COORD_R:
            size = DATA_3_COORD_R_LEN;

            break;

        case DATA_DATE_TIME:
            size = DATA_DATE_TIME_LEN;

            break;

        case DATA_NULL_FIELD:
            size = DATA_NULL_FIELD_LEN;

            break;

        case DATA_TRIPLET_ID:
            size = DATA_TRIPLET_ID_LEN;

        default:
            break;
        }

        return size;
    }

    /**
     * Describe <code>isNumeric</code> method here.
     *
     * @param type a <code>char</code> value
     * @return a <code>boolean</code> value
     */
    public static boolean isNumeric(char type) {
        switch (type) {
        case DATA_TEXT:
        case DATA_LEVEL1_TEXT:
        case DATA_LEVEL2_TEXT:
        case DATA_LEVEL3_TEXT:
        case DATA_DATE_TIME:
        case DATA_NULL_FIELD:
            return false;

        case DATA_SHORT_FLOAT:
        case DATA_LONG_FLOAT:
        case DATA_SHORT_INTEGER:
        case DATA_LONG_INTEGER:
            return true;

        case DATA_2_COORD_F:
        case DATA_2_COORD_R:
        case DATA_3_COORD_F:
        case DATA_3_COORD_R:
        case DATA_TRIPLET_ID:
            return true;

        default:
            return false;
        }
    }
}

// DataUtils
