package net.heartshield.data;

import com.google.gson.annotations.SerializedName;

import net.heartshield.signal.DVec;
import net.heartshield.signal.IVec;
import net.heartshield.signal.Signal;
import net.heartshield.util.JsonUtil;

/**
 * Result data on a measurement, as provided by the server.
 *
 * @author David Madl (git@abanbytes.eu)
 * @date 2016-04-07
 */
public class MeasurementResult {
    /** confidence that coronary artery disease is present (probability score in [0,1]) */
    // TODO: this should be Double, to enable null value for unclassified
    public double pred;
    /** filtered samples of the PPG signal */
    public double[] filtered;
    /** sample indices in `filtered` detected as heartbeats */
    public int[] idx;
    /** inter-beat intervals in MILLISECONDS */
    public double[] ibis;
    /** whether server succeeded in classifying the measurement */
    public Status status;

    /** unique token returned by server that identifies this Measurement (aka "Measurement ID" on UI) */
    public String token;

    /** error message or other reason for the status */
    public String reason;

    public String serialize() {
        return JsonUtil.getGson().toJson(this);
    }

    public static MeasurementResult deserialize(String json) {
        return JsonUtil.getGson().fromJson(json, MeasurementResult.class);
    }

    public enum Status {
        @SerializedName("ok")
        OK,
        @SerializedName("unclassified")
        UNCLASSIFIED,
        @SerializedName("recorded")
        RECORDED
        // TODO: 'error'
    }

    public static class HrvTimeDomain {
        public double rmssd;
        public double sdnn;
        public double pnn50;
        public double hrv_tri;
    }
    public static class HrvFrequencyDomain {
        public double total_power;
        public double lf;
        public double hf;
    }

    @SerializedName("hrv_time_domain")
    public HrvTimeDomain hrvTimeDomain;

    @SerializedName("hrv_frequency_domain")
    public HrvFrequencyDomain hrvFrequencyDomain;

    public boolean isHealthy() {
        if(!status.equals(Status.OK))
            throw new IllegalStateException("no result score");
        return this.pred <= 0.5;
    }

    public int getRiskLevel() {
        int level;

        // TODO: get 50th percentile for age group from server and use it for low/medium threshold (unless 50th percentile HS Score is >= 0.5)
        /*
        if(status.equals(Status.UNCLASSIFIED))
            level = 0;
        else if(pred < 0.4)
            level = 1;
        else if(isHealthy()) // <= 0.5
            level = 2;
        else
            level = 3; // > 0.5
        */

        if(status.equals(Status.UNCLASSIFIED) || status.equals(Status.RECORDED))
            level = 0;
        else if(isHealthy()) // <= 0.5
            level = 1;
        else
            level = 2; // > 0.5

        return level;
    }

    public double getHeartRate() {
        if(this.ibis == null || this.ibis.length == 0)
            return 0.0;

        // ibis = ibis / 1e3
        double[] ibis = DVec.mul(this.ibis, 1.0 / 1e3);

        // hr = 60.0 / np.mean(ibis[np.where((ibis>0.4)&(ibis<1.4))[0]])
        double[] goodIbis = Signal.goodIbis(ibis);
        if(goodIbis.length == 0)
            return 0.0;
        double meanIbi = DVec.mean(goodIbis);
        return 60.0 / meanIbi;
    }
}
