/*
 * Copyright (c) 2014. The Trustees of Indiana University.
 *
 * This version of the code is licensed under the MPL 2.0 Open Source license with additional
 * healthcare disclaimer. If the user is an entity intending to commercialize any application
 * that uses this code in a for-profit venture, please contact the copyright holder.
 */

package com.muzima.service;

import android.util.Log;
import com.muzima.api.model.Concept;
import com.muzima.api.model.Encounter;
import com.muzima.api.model.Observation;
import com.muzima.api.model.Patient;
import com.muzima.controller.ConceptController;
import com.muzima.controller.EncounterController;
import com.muzima.controller.ObservationController;
import com.muzima.controller.PatientController;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import static com.muzima.utils.DateUtils.parse;
import static java.util.Arrays.asList;

public class HTMLFormObservationCreator {

    private PatientController patientController;
    private ConceptController conceptController;
    private EncounterController encounterController;
    private ObservationController observationController;
    private ObservationParserUtility observationParserUtility;

    private Patient patient;
    private Encounter encounter;
    private List<Observation> observations;
    private String TAG = "HTMLFormObservationCreator";

    public HTMLFormObservationCreator(PatientController patientController, ConceptController conceptController,
                                      EncounterController encounterController, ObservationController observationController) {
        this.patientController = patientController;
        this.conceptController = conceptController;
        this.encounterController = encounterController;
        this.observationController = observationController;
        this.observationParserUtility = new ObservationParserUtility(conceptController);
    }

    public void createAndPersistObservations(String jsonResponse,String formDataUuid) {
        parseJSONResponse(jsonResponse,formDataUuid);
        try {
            saveObservationsAndRelatedEntities();
        } catch (ConceptController.ConceptSaveException e) {
            Log.e(TAG, "Error while saving concept", e);
        } catch (EncounterController.SaveEncounterException e) {
            Log.e(TAG, "Error while saving Encounter", e);
        } catch (ObservationController.SaveObservationException e) {
            Log.e(TAG, "Error while saving Observation", e);
        } catch (Exception e) {
            Log.e(TAG, "Unexpected Exception occurred", e);
        }
    }

    public List<Observation> getObservations() {
        return observations;
    }

    private void parseJSONResponse(String jsonResponse, String formDataUuid) {
        try {
            JSONObject responseJSON = new JSONObject(jsonResponse);
            patient = getPatient(responseJSON.getJSONObject("patient"));
            encounter = createEncounter(responseJSON.getJSONObject("encounter"),formDataUuid);
            observations = extractObservationFromJSONObject(responseJSON.getJSONObject("observation"));
        } catch (PatientController.PatientLoadException e) {
            Log.e(TAG, "Error while fetching Patient", e);
        } catch (ConceptController.ConceptFetchException e) {
            Log.e(TAG, "Error while fetching Concept", e);
        } catch (JSONException e) {
            Log.e(TAG, "Error while parsing response JSON", e);
        } catch (ParseException e) {
            Log.e(TAG, "Error while parsing response JSON", e);
        } catch (ConceptController.ConceptSaveException e) {
            Log.e(TAG, "Error while saving newly created concept", e);
        }
    }

    private void saveObservationsAndRelatedEntities() throws EncounterController.SaveEncounterException,
            ObservationController.SaveObservationException, ConceptController.ConceptSaveException {
        try {
            encounterController.saveEncounters(asList(encounter));
            conceptController.saveConcepts(observationParserUtility.getNewConceptList());
            observationController.saveObservations(observations);
        } catch (Exception e) {
            Log.e(TAG, "Error while parsing and storing Observations.", e);
        }
    }

    private List<Observation> extractObservationFromJSONObject(JSONObject jsonObject) throws JSONException, ConceptController.ConceptFetchException, ConceptController.ConceptSaveException {
        List<Observation> observations = new ArrayList<Observation>();
        Iterator keys = jsonObject.keys();
        while (keys.hasNext()) {
            String key = (String) keys.next();
            observations.addAll(extractBasedOnType(jsonObject, key));
        }
        observations.removeAll(Collections.singleton(null));
        return observations;
    }

    private List<Observation> extractBasedOnType(JSONObject jsonObject, String key) throws JSONException, ConceptController.ConceptFetchException, ConceptController.ConceptSaveException {
        if (jsonObject.get(key) instanceof JSONArray) {
            return createMultipleObservation(key, jsonObject.getJSONArray(key));
        } else if (jsonObject.get(key) instanceof JSONObject) {
            return extractObservationFromJSONObject(jsonObject.getJSONObject(key));
        }
        ArrayList<Observation> observations = new ArrayList<Observation>();
        observations.add(createObservation(key, jsonObject.getString(key)));
        return observations;
    }

    private List<Observation> createMultipleObservation(String conceptName, JSONArray jsonArray) throws JSONException,
            ConceptController.ConceptFetchException, ConceptController.ConceptSaveException {
        List<Observation> observations = new ArrayList<Observation>();
        for (int i = 0; i < jsonArray.length(); i++) {
            if (jsonArray.get(i) instanceof JSONObject) {
                observations.addAll(extractObservationFromJSONObject(jsonArray.getJSONObject(i)));
            } else {
                observations.add(createObservation(conceptName, jsonArray.getString(i)));
            }
        }
        return observations;
    }

    private Observation createObservation(String conceptName, String value) throws JSONException, ConceptController.ConceptFetchException, ConceptController.ConceptSaveException {
        Concept concept = observationParserUtility.getConceptEntity(conceptName);
        if (concept == null) {
            return null;
        }
        Observation observation = observationParserUtility.getObservationEntity(concept, value);
        observation.setEncounter(encounter);
        observation.setPerson(patient);
        observation.setObservationDatetime(encounter.getEncounterDatetime());
        return observation;
    }

    private Encounter createEncounter(JSONObject encounterJSON, String formDataUuid) throws JSONException, ParseException {
        return observationParserUtility.getEncounterEntity(parse(encounterJSON.getString("encounter.encounter_datetime")), patient,formDataUuid);
    }

    public Date getEncounterDateFromFormDate(String jsonResponse){
        try {
            JSONObject jsonObject = new JSONObject(jsonResponse);
            JSONObject jsonObjectInner = jsonObject.getJSONObject("encounter");
            return parse(jsonObjectInner.getString("encounter.encounter_datetime"));
        } catch (JSONException e) {
            Log.e(TAG, "Error while parsing response JSON", e);
        } catch (ParseException e) {
            Log.e(TAG, "Error while parsing response JSON", e);
        }
        return null;
    }

    private Patient getPatient(JSONObject patient) throws JSONException, PatientController.PatientLoadException {
        String uuid = patient.getString("patient.uuid");
        return patientController.getPatientByUuid(uuid);
    }
}

@@ -3206,32 +3206,87 @@
 ,formDataUuid);%0A
+            if (responseJSON.has(%22observation%22)) %7B%0A    
             obse
@@ -3369,24 +3369,38 @@
 rvation%22));%0A
+            %7D%0A
         %7D ca
@@ -4351,24 +4351,93 @@
 eptList());%0A
+            if(observations != null && !observations.isEmpty())%7B%0A    
             
@@ -4490,16 +4490,30 @@
 tions);%0A
+            %7D%0A
         

Fix optional parsing of observations on forms to accommodate registration form