package com.openxc.remote;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;

import com.google.common.base.Objects;

import com.openxc.measurements.serializers.JsonSerializer;

import com.openxc.measurements.UnrecognizedMeasurementTypeException;

import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

/**
 * An untyped measurement used only for the AIDL VehicleService interface.
 *
 * This abstract base class is intented to be the parent of numerical, state and
 * boolean measurements. The architecture ended up using only numerical
 * measurements, with other types being coerced to doubles.
 *
 * A raw measurement can have a value, an event, both or neither. Most
 * measurements have only a value - measurements also with an event include
 * things like button events (where both the button direction and action need to
 * be identified). The value and event are both nullable, for cases where a
 * measurement needs to be returned but there is no valid value for it.
 *
 * This class implements the Parcelable interface, so it can be used directly as
 * a return value or function parameter in an AIDL interface.
 *
 * @see com.openxc.measurements.BaseMeasurement
 */
public class RawMeasurement implements Parcelable {
    private static final String TAG = "RawMeasurement";

    private String mCachedSerialization;
    private double mTimestamp;
    private String mName;
    private Object mValue;
    private Object mEvent;

    public RawMeasurement(String name, Object value) {
        this();
        mName = name;
        mValue = value;
    }

    public RawMeasurement(String name, Object value, Object event) {
        this(name, value);
        mEvent = event;
    }

    public RawMeasurement(String serialized) throws UnrecognizedMeasurementTypeException {
        this();
        deserialize(serialized, this);
    }

    private RawMeasurement(Parcel in) {
        this();
        readFromParcel(in);
    }

    private RawMeasurement() {
        timestamp();
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeString(serialize());
    }

    public void readFromParcel(Parcel in) {
        try {
            RawMeasurement measurement = new RawMeasurement(in.readString());
            copy(measurement);
        } catch(UnrecognizedMeasurementTypeException e) {
            return;
        }
    }

    public static final Parcelable.Creator<RawMeasurement> CREATOR =
            new Parcelable.Creator<RawMeasurement>() {
        public RawMeasurement createFromParcel(Parcel in) {
            return new RawMeasurement(in);
        }

        public RawMeasurement[] newArray(int size) {
            return new RawMeasurement[size];
        }
    };

    public String serialize() {
        return serialize(false);
    }

    public String serialize(boolean reserialize) {
        if(reserialize || mCachedSerialization == null) {
            Double timestamp = isTimestamped() ? getTimestamp() : null;
            mCachedSerialization = JsonSerializer.serialize(getName(), getValue(), getEvent(),
                    timestamp);
        }
        return mCachedSerialization;
    }

    private static void deserialize(String measurementString,
            RawMeasurement measurement) throws UnrecognizedMeasurementTypeException {
        JsonFactory jsonFactory = new JsonFactory();
        JsonParser parser;
        try {
            parser = jsonFactory.createParser(measurementString);
        } catch(IOException e) {
            String message = "Couldn't decode JSON from: " + measurementString;
            Log.w(TAG, message, e);
            throw new UnrecognizedMeasurementTypeException(message, e);
        }

        try {
            parser.nextToken();
            while(parser.nextToken() != JsonToken.END_OBJECT) {
                String field = parser.getCurrentName();
                parser.nextToken();
                if(JsonSerializer.NAME_FIELD.equals(field)) {
                    measurement.mName = parser.getText();
                } else if(JsonSerializer.VALUE_FIELD.equals(field)) {
                    measurement.mValue = parseUnknownType(parser);
                } else if(JsonSerializer.EVENT_FIELD.equals(field)) {
                    measurement.mEvent = parseUnknownType(parser);
                } else if(JsonSerializer.TIMESTAMP_FIELD.equals(field)) {
                    measurement.mTimestamp =
                        parser.getNumberValue().doubleValue();
                }
            }
        } catch(IOException e) {
            String message = "JSON message didn't have the expected format: "
                    + measurementString;
            Log.w(TAG, message, e);
            throw new UnrecognizedMeasurementTypeException(message, e);
        }
        measurement.mCachedSerialization = measurementString;
    }

    private static Object parseUnknownType(JsonParser parser) {
        Object value = null;
        try {
            value = parser.getNumberValue();
        } catch(JsonParseException e) {
            try {
                value = parser.getBooleanValue();
            } catch(JsonParseException e2) {
                try {
                    value = parser.getText();
                } catch(JsonParseException e3) {
                } catch(IOException e4) {
                }
            } catch(IOException e5) {
            }
        } catch(IOException e) {
        }
        return value;
    }

    public String getName() {
        return mName;
    }

    public Object getValue() {
        return mValue;
    }

    public boolean hasEvent() {
        return getEvent() != null;
    }

    public Object getEvent() {
        return mEvent;
    }

    public boolean isTimestamped() {
        return getTimestamp() != null && !Double.isNaN(getTimestamp());
    }
    public Double getTimestamp() {
        return mTimestamp;
    }

    public void timestamp() {
        mTimestamp = System.currentTimeMillis() / 1000.0;
    }

    public void untimestamp() {
    	mTimestamp = Double.NaN;
    }

    public int describeContents() {
        return 0;
    }

    private void copy(RawMeasurement measurement) {
        mTimestamp = measurement.getTimestamp();
        mName = measurement.getName();
        mValue = measurement.getValue();
        mEvent = measurement.getEvent();
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
            .add("value", getValue())
            .add("event", getEvent())
            .toString();
    }
}

@@ -1927,16 +1927,28 @@
 ialized)
+%0A           
  throws 
@@ -2077,35 +2077,37 @@
 ement(Parcel in)
- %7B%0A
+%0A    
         this();%0A
@@ -2104,39 +2104,80 @@
   th
-is();%0A        readFromParcel(in
+rows UnrecognizedMeasurementTypeException %7B%0A        this(in.readString()
 );%0A 
@@ -2356,218 +2356,177 @@
 lic 
-void readFromParcel(Parcel in) %7B%0A        try %7B%0A            RawMeasurement measurement = new RawMeasurement(in.readString());%0A            copy(measurement);%0A        %7D catch(UnrecognizedMeasurementTypeException e
+static final Parcelable.Creator%3CRawMeasurement%3E CREATOR =%0A            new Parcelable.Creator%3CRawMeasurement%3E() %7B%0A        public RawMeasurement createFromParcel(Parcel in
 ) %7B%0A
@@ -2541,15 +2541,13 @@
     
-return;
+try %7B
 %0A   
@@ -2555,60 +2555,27 @@
     
-%7D%0A
     
-%7D%0A%0A
     
-public static final Parcelable.Creator%3C
+return new 
 RawM
@@ -2580,35 +2580,29 @@
 wMeasurement
-%3E CREATOR =
+(in);
 %0A           
@@ -2606,111 +2606,62 @@
     
-new Parcelable.Creator%3CRawMeasurement%3E() %7B%0A        public RawMeasurement createFromParcel(Parcel in) %7B%0A
+%7D catch(UnrecognizedMeasurementTypeException e) %7B%0A    
     
@@ -2686,36 +2686,48 @@
  RawMeasurement(
-in
 );
+%0A            %7D
 %0A        %7D%0A%0A    
@@ -3157,24 +3157,44 @@
 e(getName(),
+%0A                   
  getValue(),
@@ -3205,36 +3205,16 @@
 Event(),
-%0A                   
  timesta
@@ -3373,16 +3373,28 @@
 urement)
+%0A           
  throws 

Refactor new RawMeasurement constructors.