package com.inductiveautomation.opcua.stack.core.types.builtin;

import javax.annotation.Nullable;

import com.inductiveautomation.opcua.stack.core.types.enumerated.TimestampsToReturn;

public final class DataValue {

    private final Variant value;
    private final StatusCode status;
    private final DateTime sourceTime;
    private final DateTime serverTime;

    public DataValue(long statusCode) {
        this(new StatusCode(statusCode));
    }

    public DataValue(StatusCode statusCode) {
        this(Variant.NULL_VALUE, statusCode, DateTime.MIN_VALUE);
    }

    public DataValue(Variant value) {
        this(value, StatusCode.GOOD);
    }

    public DataValue(Variant value, StatusCode status) {
        this(value, status, DateTime.now());
    }

    public DataValue(Variant value, StatusCode status, @Nullable DateTime time) {
        this(value, status, time, time);
    }

    public DataValue(Variant value, StatusCode status, @Nullable DateTime sourceTime, @Nullable DateTime serverTime) {
        this.value = value;
        this.status = status;
        this.sourceTime = sourceTime;
        this.serverTime = serverTime;
    }

    public Variant getValue() {
        return value;
    }

    public StatusCode getStatusCode() {
        return status;
    }

    @Nullable
    public DateTime getSourceTime() {
        return sourceTime;
    }

    @Nullable
    public DateTime getServerTime() {
        return serverTime;
    }

    public DataValue withStatus(StatusCode status) {
        return new DataValue(value, status, sourceTime, serverTime);
    }

    public DataValue withSourceTime(@Nullable DateTime sourceTime) {
        return new DataValue(value, status, sourceTime, serverTime);
    }

    public DataValue withServerTime(@Nullable DateTime serverTime) {
        return new DataValue(value, status, sourceTime, serverTime);
    }

    /**
     * Derive a new {@link DataValue} from a given {@link DataValue}.
     *
     * @param from       the {@link DataValue} to derive from.
     * @param timestamps the timestamps to return in the derived value.
     * @return a derived {@link DataValue}.
     */
    public static DataValue derivedValue(DataValue from, TimestampsToReturn timestamps) {
        boolean includeSource = timestamps == TimestampsToReturn.Source || timestamps == TimestampsToReturn.Both;
        boolean includeServer = timestamps == TimestampsToReturn.Server || timestamps == TimestampsToReturn.Both;

        return new DataValue(
                from.value,
                from.status,
                includeSource ? from.sourceTime : null,
                includeServer ? from.serverTime : null
        );
    }

    /**
     * Derive a new {@link DataValue} from a given {@link DataValue}.
     * <p>
     * The value is assumed to be for a non-value Node attribute, and therefore the source timestamp is not returned.
     *
     * @param from       the {@link DataValue} to derive from.
     * @param timestamps the timestamps to return in the derived value.
     * @return a derived {@link DataValue}.
     */
    public static DataValue derivedNonValue(DataValue from, TimestampsToReturn timestamps) {
        boolean includeServer = timestamps == TimestampsToReturn.Server || timestamps == TimestampsToReturn.Both;

        return new DataValue(
                from.value,
                from.status,
                null,
                includeServer ? from.serverTime : null
        );
    }

}

@@ -93,16 +93,95 @@
 lable;%0A%0A
+import com.inductiveautomation.opcua.stack.core.types.builtin.unsigned.UShort;%0A
 import c
@@ -417,35 +417,123 @@
 e final 
-DateTime serverTime
+UShort sourcePicoseconds;%0A    private final DateTime serverTime;%0A    private final UShort serverPicoseconds
 ;%0A%0A    p
@@ -1192,128 +1192,601 @@
 this
-.value = value;%0A        this.status = status;%0A        this.sourceTime = sourceTime;%0A        this.serverTime = serverTime
+(value, status, sourceTime, null, serverTime, null);%0A    %7D%0A%0A    public DataValue(Variant value,%0A                     StatusCode status,%0A                     @Nullable DateTime sourceTime,%0A                     @Nullable UShort sourcePicoseconds,%0A                     @Nullable DateTime serverTime,%0A                     @Nullable UShort serverPicoseconds) %7B%0A%0A        this.value = value;%0A        this.status = status;%0A        this.sourceTime = sourceTime;%0A        this.sourcePicoseconds = sourcePicoseconds;%0A        this.serverTime = serverTime;%0A        this.serverPicoseconds = serverPicoseconds
 ;%0A  
@@ -2036,60 +2036,256 @@
 lic 
-DateTime getServerTime() %7B%0A        return serverTime
+UShort getSourcePicoseconds() %7B%0A        return sourcePicoseconds;%0A    %7D%0A%0A    @Nullable%0A    public DateTime getServerTime() %7B%0A        return serverTime;%0A    %7D%0A%0A    @Nullable%0A    public UShort getServerPicoseconds() %7B%0A        return serverPicoseconds
 ;%0A  

Add source and server picosecond fields