/**
 *  SusiCognition
 *  Copyright 24.07.2016 by Michael Peter Christen, @0rb1t3r
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *  
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *  
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program in the file lgpl21.txt
 *  If not, see <http://www.gnu.org/licenses/>.
 */

package ai.susi.mind;

import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

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

import ai.susi.server.ClientIdentity;
import ai.susi.tools.DateParser;
import ai.susi.tools.UTF8;

/**
 * An cognition is the combination of a query of a user with the response of susi.
 */
public class SusiCognition {

    JSONObject json;

    public SusiCognition(final SusiMind mind, final String query, int timezoneOffset, double latitude, double longitude, int maxcount, ClientIdentity identity) {
        this.json = new JSONObject(true);
        
        // get a response from susis mind
        String client = identity.getClient();
        this.setQuery(query);
        this.json.put("count", maxcount);
        SusiThought observation = new SusiThought();
        observation.addObservation("timezoneOffset", Integer.toString(timezoneOffset));
        
        if (!Double.isNaN(latitude) && !Double.isNaN(longitude)) {
            observation.addObservation("latitude", Double.toString(latitude));
            observation.addObservation("longitude", Double.toString(longitude));
        }
        this.json.put("client_id", Base64.getEncoder().encodeToString(UTF8.getBytes(client)));
        long query_date = System.currentTimeMillis();
        this.json.put("query_date", DateParser.utcFormatter.print(query_date));
        
        // compute the mind reaction
        List<SusiArgument> dispute = mind.react(query, maxcount, client, observation);
        long answer_date = System.currentTimeMillis();
        
        // store answer and actions into json
        this.json.put("answers", new JSONArray(dispute.stream().map(argument -> argument.finding(client, mind)).collect(Collectors.toList())));
        this.json.put("answer_date", DateParser.utcFormatter.print(answer_date));
        this.json.put("answer_time", answer_date - query_date);
        this.json.put("language", "en");
    }
    
    public SusiCognition(JSONObject json) {
        this.json = json;
    }

    public SusiCognition() {
        
    }
    
    /**
     * set ISO 639-1 code
     * @param language
     * @return
     */
    public SusiCognition setLanguage(String language) {
        if (language.length() > 2) language = language.substring(0, 2);
        this.json.put("language", language.toLowerCase()); // ISO 639-1 is a lowercase code
        return this;
    }
    
    public SusiCognition setQuery(final String query) {
        this.json.put("query", query);
        return this;
    }
    
    public String getQuery() {
        if (!this.json.has("query")) return "";
        String q = this.json.getString("query");
        return q == null ? "" : q;
    }
    
    public Date getQueryDate() {
        String d = this.json.getString("query_date");
        return DateParser.utcFormatter.parseDateTime(d).toDate();
    }

    /**
     * The answer of an cognition contains a list of the mind-melted arguments as one thought
     * @return a list of answer thoughts
     */
    public List<SusiThought> getAnswers() {
        List<SusiThought> answers = new ArrayList<>();
        if (this.json.has("answers")) {
            JSONArray a = this.json.getJSONArray("answers");
            for (int i = 0; i < a.length(); i++) answers.add(new SusiThought(a.getJSONObject(i)));
        }
        return answers;
    }
    
    /**
     * the expression of a cognition is the actual string that comes out as response to
     * actions of the user
     * @return a response string
     */
    public String getExpression() {
        List<SusiThought> answers = getAnswers();
        if (answers == null || answers.size() == 0) return "";
        SusiThought t = answers.get(0);
        List<SusiAction> actions = t.getActions();
        if (actions == null || actions.size() == 0) return "";
        SusiAction a = actions.get(0);
        List<String> phrases = a.getPhrases();
        if (phrases == null || phrases.size() == 0) return "";
        return phrases.get(0);
    }
    
    /**
     * The cognition is the result of a though extraction. We can reconstruct
     * the dispute as list of last mindstates using the cognition data.
     * @return a backtrackable thought reconstructed from the cognition data
     */    
    public SusiThought recallDispute() {
        SusiThought dispute = new SusiThought();
        if (this.json.has("answers")) {
            JSONArray answers = this.json.getJSONArray("answers"); // in most cases there is only one answer
            for (int i = answers.length() - 1; i >= 0; i--) {
                SusiThought clonedThought = new SusiThought(answers.getJSONObject(i));
                dispute.addObservation("query", this.json.getString("query"));  // we can unify "query" in queries
                SusiAction expressionAction = null;
                for (SusiAction a: clonedThought.getActions()) {
                    ArrayList<String> phrases = a.getPhrases();
                    // not all actions have phrases!
                    if (phrases != null && phrases.size() > 0) {expressionAction = a; break;}
                }
                if (expressionAction != null) dispute.addObservation("answer", expressionAction.getPhrases().get(0)); // we can unify with "answer" in queries

                // add all data from the old dispute
                JSONArray clonedData = clonedThought.getData();
                if (clonedData.length() > 0) {
                    JSONObject row = clonedData.getJSONObject(0);
                    row.keySet().forEach(key -> {if (key.startsWith("_")) dispute.addObservation(key, row.getString(key));});
                    //data.put(clonedData.get(0));
                }
            }
        }
        return dispute;
    }
    
    public JSONObject getJSON() {
        return this.json;
    }
    
    public String toString() {
        return this.json.toString();
    }
}

@@ -1002,16 +1002,67 @@
 JSON
-Object;%0A
+Exception;%0Aimport org.json.JSONObject;%0A%0Aimport ai.susi.DAO;
 %0Aimp
@@ -6485,81 +6485,494 @@
 -%3E %7B
-if (key.startsWith(%22_%22)) dispute.addObservation(key, row.getString(key));
+%0A                        if (key.startsWith(%22_%22)) %7B%0A                            try %7B%0A                                String observation = row.getString(key);%0A                                dispute.addObservation(key, observation);%0A                            %7D catch (JSONException e) %7B%0A                                // sometimes that is not string%0A                                DAO.log(e.getMessage());%0A                            %7D%0A                        %7D%0A                    
 %7D);%0A

fix for non-string values in recall