// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.bot.builder;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;

import java.util.HashMap;
import java.util.Map;

/**
 * Contains recognition results generated by an {@link Recognizer}.
 */
public class RecognizerResult implements RecognizerConvert {
    @JsonProperty(value = "entities")
    private JsonNode entities;

    @JsonProperty(value = "text")
    private String text;

    @JsonProperty(value = "alteredText")
    private String alteredText;

    @JsonProperty(value = "intents")
    private Map<String, IntentScore> intents;

    /**
     * Additional properties.
     */
    private HashMap<String, JsonNode> properties = new HashMap<>();

    /**
     * Holds intent score info.
     */
    @SuppressWarnings({ "checkstyle:VisibilityModifier" })
    public static class NamedIntentScore {
        /// The intent name
        public String intent;

        /// The intent score
        public double score;
    }

    /**
     * Return the top scoring intent and its score.
     * 
     * @return The top scoring intent and score.
     * @throws IllegalArgumentException No intents available.
     */
    @JsonIgnore
    public NamedIntentScore getTopScoringIntent() throws IllegalArgumentException {
        if (getIntents() == null) {
            throw new IllegalArgumentException("RecognizerResult.Intents cannot be null");
        }

        NamedIntentScore topIntent = new NamedIntentScore();
        for (Map.Entry<String, IntentScore> intent : getIntents().entrySet()) {
            double score = intent.getValue().getScore();
            if (score > topIntent.score) {
                topIntent.intent = intent.getKey();
                topIntent.score = intent.getValue().getScore();
            }
        }

        return topIntent;
    }

    /**
     * Gets the input text to recognize.
     * 
     * @return The original text.
     */
    public String getText() {
        return text;
    }

    /**
     * Sets the input text to recognize.
     * 
     * @param withText The text to recognize.
     */
    public void setText(String withText) {
        text = withText;
    }

    /**
     * Gets the input text as modified by the recognizer, for example for spelling
     * correction.
     * 
     * @return Text modified by recognizer.
     */
    public String getAlteredText() {
        return alteredText;
    }

    /**
     * Sets the input text as modified by the recognizer, for example for spelling
     * correction.
     * 
     * @param withAlteredText Text modified by recognizer.
     */
    public void setAlteredText(String withAlteredText) {
        alteredText = withAlteredText;
    }

    /**
     * Gets the recognized intents, with the intent as key and the confidence as
     * value.
     * 
     * @return Mapping from intent to information about the intent.
     */
    public Map<String, IntentScore> getIntents() {
        return intents;
    }

    /**
     * Sets the recognized intents, with the intent as key and the confidence as
     * value.
     * 
     * @param withIntents Mapping from intent to information about the intent.
     */
    public void setIntents(Map<String, IntentScore> withIntents) {
        intents = withIntents;
    }

    /**
     * Gets the recognized top-level entities.
     * 
     * @return Object with each top-level recognized entity as a key.
     */
    public JsonNode getEntities() {
        return entities;
    }

    /**
     * Sets the recognized top-level entities.
     * 
     * @param withEntities Object with each top-level recognized entity as a key.
     */
    public void setEntities(JsonNode withEntities) {
        entities = withEntities;
    }

    /**
     * Gets properties that are not otherwise defined by the RecognizerResult type
     * but that might appear in the REST JSON object.
     * 
     * @return The extended properties for the object.
     */
    @JsonAnyGetter
    public Map<String, JsonNode> getProperties() {
        return this.properties;
    }

    /**
     * Sets properties that are not otherwise defined by the RecognizerResult type
     * but that might appear in the REST JSON object.
     *
     * <p>
     * With this, properties not represented in the defined type are not dropped
     * when the JSON object is deserialized, but are instead stored in this
     * property. Such properties will be written to a JSON object when the instance
     * is serialized.
     * </p>
     *
     * @param key   The property key.
     * @param value The property value.
     */
    @JsonAnySetter
    public void setProperties(String key, JsonNode value) {
        this.properties.put(key, value);
    }

    /**
     * Convert recognizer result.
     * 
     * @param result Result to convert.
     */
    @Override
    public void convert(Object result) {
        setText(((RecognizerResult) result).getText());
        setAlteredText(((RecognizerResult) result).getAlteredText());
        setIntents(((RecognizerResult) result).getIntents());
        setEntities(((RecognizerResult) result).getEntities());

        for (String key : ((RecognizerResult) result).getProperties().keySet()) {
            setProperties(key, ((RecognizerResult) result).getProperties().get(key));
        }
    }
}
