package ee.ioc.phon.android.speak.service;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

import ee.ioc.phon.android.speechutils.utils.TextUtils;


/**
 * Parses the JSON object delivered by the webservice. It can be in one of the following forms:
 * <pre>
 * {"status": 9, "message": "No decoder available, try again later"}
 *
 * {"status": 0, "result": {"hypotheses": [{"transcript": "elas metsas..."}], "final": false}}
 * {"status": 0, "result": {"hypotheses": [{"transcript": "elas metsas..."}], "final": true}}
 *
 * {"status": 0, "adaptation_state": {"type": "string+gzip+base64", "value": "eJxlvcu7"}}
 * </pre>
 */
public class WebSocketResponse {

    // Usually used when recognition results are sent.
    public static final int STATUS_SUCCESS = 0;

    // Audio contains a large portion of silence or non-speech.
    public static final int STATUS_NO_SPEECH = 1;

    // Recognition was aborted for some reason.
    public static final int STATUS_ABORTED = 2;

    // No valid frames found before end of stream.
    public static final int STATUS_NO_VALID_FRAMES = 5;

    // Used when all recognizer processes are currently in use and recognition cannot be performed.
    public static final int STATUS_NOT_AVAILABLE = 9;

    private final JSONObject mJson;
    private final int mStatus;

    public WebSocketResponse(String data) throws WebSocketResponseException {
        try {
            mJson = new JSONObject(data);
            mStatus = mJson.getInt("status");
        } catch (JSONException e) {
            throw new WebSocketResponseException(e);
        }
    }

    public int getStatus() {
        return mStatus;
    }

    public boolean isResult() {
        return mJson.has("result");
    }


    public Result parseResult() throws WebSocketResponseException {
        try {
            return new Result(mJson.getJSONObject("result"));
        } catch (JSONException e) {
            throw new WebSocketResponseException(e);
        }
    }


    public Message parseMessage() throws WebSocketResponseException {
        try {
            return new Message(mJson.getString("message"));
        } catch (JSONException e) {
            throw new WebSocketResponseException(e);
        }
    }

    public AdaptationState parseAdaptationState() throws WebSocketResponseException {
        try {
            return new AdaptationState(mJson.getJSONObject("adaptation_state"));
        } catch (JSONException e) {
            throw new WebSocketResponseException(e);
        }
    }


    public static class Result {
        private final JSONObject mResult;

        public Result(JSONObject result) {
            mResult = result;
        }

        // TODO: yield transcript and do pretty-printing in the client
        public ArrayList<String> getHypotheses(int maxHypotheses, boolean prettyPrint) throws WebSocketResponseException {
            try {
                ArrayList<String> hypotheses = new ArrayList<>();
                JSONArray array = mResult.getJSONArray("hypotheses");
                for (int i = 0; i < array.length() && i < maxHypotheses; i++) {
                    String transcript = array.getJSONObject(i).getString("transcript");
                    if (prettyPrint) {
                        hypotheses.add(TextUtils.prettyPrint(transcript));
                    } else {
                        hypotheses.add(transcript);
                    }
                }
                return hypotheses;
            } catch (JSONException e) {
                throw new WebSocketResponseException(e);
            }
        }

        /**
         * The "final" field does not have to exist, but if it does then it must be a boolean.
         *
         * @return true iff this result is final
         */
        public boolean isFinal() {
            return mResult.optBoolean("final", false);
        }
    }


    public static class Message {
        private final String mMessage;

        public Message(String message) {
            mMessage = message;
        }

        public String getMessage() {
            return mMessage;
        }
    }


    public static class AdaptationState {
        public AdaptationState(JSONObject result) {
        }
    }


    public static class WebSocketResponseException extends Exception {
        public WebSocketResponseException(JSONException e) {
            super(e);
        }
    }
}