package org.apache.vxquery.runtime.functions.cast;

import java.io.DataOutput;
import java.io.IOException;

import org.apache.vxquery.datamodel.accessors.atomic.XSDecimalPointable;
import org.apache.vxquery.datamodel.values.ValueTag;
import org.apache.vxquery.exceptions.ErrorCode;
import org.apache.vxquery.exceptions.SystemException;
import org.apache.vxquery.runtime.functions.strings.ICharacterIterator;
import org.apache.vxquery.runtime.functions.strings.UTF8StringCharacterIterator;

import edu.uci.ics.hyracks.data.std.primitive.BooleanPointable;
import edu.uci.ics.hyracks.data.std.primitive.DoublePointable;
import edu.uci.ics.hyracks.data.std.primitive.FloatPointable;
import edu.uci.ics.hyracks.data.std.primitive.LongPointable;
import edu.uci.ics.hyracks.data.std.primitive.UTF8StringPointable;

public class CastToDecimalOperation extends AbstractCastToOperation {

    @Override
    public void convertBoolean(BooleanPointable boolp, DataOutput dOut) throws SystemException, IOException {
        long value = (boolp.getBoolean() ? 1 : 0);
        dOut.write(ValueTag.XS_DECIMAL_TAG);
        dOut.writeByte(0);
        dOut.writeLong(value);
    }

    @Override
    public void convertDecimal(XSDecimalPointable decp, DataOutput dOut) throws SystemException, IOException {
        dOut.write(ValueTag.XS_DECIMAL_TAG);
        dOut.write(decp.getByteArray(), decp.getStartOffset(), decp.getLength());
    }

    @Override
    public void convertDouble(DoublePointable doublep, DataOutput dOut) throws SystemException, IOException {
        double doubleValue = doublep.getDouble();
        byte decimalPlace = 0;
        // Move the decimal
        while (doubleValue % 1 != 0) {
            if (decimalPlace + 1 > XSDecimalPointable.PRECISION) {
                throw new SystemException(ErrorCode.FOCA0001);
            }
            decimalPlace++;
            doubleValue *= 10;
        }
        // Remove extra zeros
        while (doubleValue != 0 && doubleValue % 10 == 0) {
            doubleValue /= 10;
            --decimalPlace;
        }
        dOut.write(ValueTag.XS_DECIMAL_TAG);
        dOut.write(decimalPlace);
        dOut.writeLong((long) doubleValue);
    }

    @Override
    public void convertFloat(FloatPointable floatp, DataOutput dOut) throws SystemException, IOException {
        float floatValue = floatp.getFloat();
        byte decimalPlace = 0;
        // Move the decimal
        while (floatValue % 1 != 0) {
            if (decimalPlace + 1 > XSDecimalPointable.PRECISION) {
                throw new SystemException(ErrorCode.FOCA0001);
            }
            decimalPlace++;
            floatValue *= 10;
        }
        // Remove extra zeros
        while (floatValue != 0 && floatValue % 10 == 0) {
            floatValue /= 10;
            --decimalPlace;
        }
        dOut.write(ValueTag.XS_DECIMAL_TAG);
        dOut.write(decimalPlace);
        dOut.writeLong((long) floatValue);
    }

    @Override
    public void convertInteger(LongPointable longp, DataOutput dOut) throws SystemException, IOException {
        dOut.write(ValueTag.XS_DECIMAL_TAG);
        dOut.write((byte) 0);
        dOut.writeLong(longp.getLong());
    }

    @Override
    public void convertString(UTF8StringPointable stringp, DataOutput dOut) throws SystemException, IOException {
        ICharacterIterator charIterator = new UTF8StringCharacterIterator(stringp);
        charIterator.reset();
        byte decimalPlace = 0;
        long value = 0;
        boolean pastDecimal = false, negativeValue = false;
        int count = 0;
        int c = 0;

        while ((c = charIterator.next()) != ICharacterIterator.EOS_CHAR) {
            if (count + 1 > XSDecimalPointable.PRECISION) {
                throw new SystemException(ErrorCode.FOCA0001);
            } else if (Character.isDigit(c)) {
                value = value * 10 + Character.getNumericValue(c);
                if (pastDecimal) {
                    decimalPlace++;
                }
                count++;
            } else if (c == Character.valueOf('-')) {
                negativeValue = true;
            } else if (c == Character.valueOf('.')) {
                pastDecimal = true;
            } else {
                throw new SystemException(ErrorCode.FORG0001);
            }
        }
        if (negativeValue) {
            value *= -1;
        }

        // Normalize the value and take off trailing zeros.
        while (value != 0 && value % 10 == 0) {
            value -= 10;
            --decimalPlace;
        }
        dOut.write(ValueTag.XS_DECIMAL_TAG);
        dOut.write(decimalPlace);
        dOut.writeLong(value);
    }

    @Override
    public void convertUntypedAtomic(UTF8StringPointable stringp, DataOutput dOut) throws SystemException, IOException {
        convertString(stringp, dOut);
    }

}
@@ -1676,36 +1676,79 @@
 leValue %25 1 != 0
+ && (doubleValue != 0 %7C%7C doubleValue != -0)
 ) %7B%0A
-
             if (
@@ -2001,24 +2001,30 @@
 leValue 
-!
+%25 10 =
 = 0 && 
+(
 doubleVa
@@ -2023,33 +2023,50 @@
 doubleValue 
-%25 10 == 0
+!= 0 %7C%7C doubleValue != -0)
 ) %7B%0A        
@@ -2446,32 +2446,41 @@
 cimalPlace = 0;%0A
+        %0A
         // Move 
@@ -2525,16 +2525,57 @@
 %25 1 != 0
+ && (floatValue != 0 %7C%7C floatValue != -0)
 ) %7B%0A    
@@ -2838,24 +2838,30 @@
 atValue 
-!
+%25 10 =
 = 0 && 
+(
 floatVal
@@ -2859,33 +2859,49 @@
 (floatValue 
-%25 10 == 0
+!= 0 %7C%7C floatValue != -0)
 ) %7B%0A        
@@ -3919,33 +3919,33 @@
 rrorCode.FOCA000
-1
+6
 );%0A            %7D

Update the error reported for string value being to large.