V1_0.java [src/java/m/wqm/wqm07_scpestslp] Revision:   Date:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package m.wqm.wqm07_scpestslp;

import csip.ModelDataService;
import csip.api.server.ServiceException;
import csip.utils.JSONUtils;
import java.util.ArrayList;
import java.util.Map;
import java.util.logging.Level;
import javax.ws.rs.Path;
import csip.annotations.Description;
import csip.annotations.Name;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;

/**
 *
 * @author Srinivas
 * @author Rumpal Sidhu
 * @author Shaun Case
 */
@Name("WQM-07: Pesticide Soil Leaching Potential (PesticideSLP)")
@Description("This service computes pesticide soil leaching potential "
        + "for soil components in an area of analysis, and then computes "
        + "pesticide soil leaching potential representing the area of analysis. "
        + "The service primarily will consume data from the WQM-2 WQMSoilAttributes "
        + "service to compute soil pesticide leaching potential used later by "
        + "the WQM-13 service to compute threshold treatment level scores.")
@Path("m/pesticide_slp/1.0")

public class V1_0 extends ModelDataService {

    private static final int VERY_LOW = 1;
    private static final int LOW = 2;
    private static final int INTERMEDIATE = 3;
    private static final int HIGH = 4;
    private final String comp_pslp_string[] = {"", "VERY LOW", "LOW", "INTERMEDIATE", "HIGH"};

    private int comp_pslp_number;
    private String aoa_pslp;
    private ArrayList<AoA> aoAs;
    private String error_msg;

    // request payload
    // Store all of the input data by AoA
    private Input inputData;

    // response payload
    // Storeage the results
    private Result result;

    @Override
    // reading the inputs from the json file into input object and placing it in the arraylist
    protected void preProcess() throws Exception {
        comp_pslp_number = 0;
        aoa_pslp = "";
        error_msg = "";
        result = null;

        aoAs = new ArrayList<>();
        inputData = new Input(aoAs);

        if (inputData.getError()) {
            throw new ServiceException(inputData.getErrorMsg());
        }
    }

    @Override
    protected void doProcess() throws Exception {
        if (error_msg.isEmpty()) {
            for (AoA aoA : aoAs) {
                if (!aoA.process()) {
                    error_msg += aoA.getErrorMsg();
                    break;
                }
            }
            if (error_msg.isEmpty()) {
                result = new Result(aoAs);
            }
        }

    }

    @Override
    //writing the results back to JSON
    protected void postProcess() throws Exception {
        if (null != result) {
            result.putResults();
        }
    }

    class AoA {

        private ArrayList<SoilComponent> soilComponents;
        private final int AoAId;
        private String error_msg;
        private double cum_pslp_product;
        private double aoa_area;
        private String aoa_pslp;

        AoA(int AoAId, JSONArray components) throws ServiceException {
            this.cum_pslp_product = 0;
            this.aoa_area = 0;
            this.AoAId = AoAId;
            this.error_msg = "";
            this.aoa_pslp = "NONE";

            this.soilComponents = new ArrayList<>();

            if (AoAId == -1) {
                this.error_msg = " Invalid AoA Identifier. ";
            } else {
                try {
                    if ((null != components) && (components.length() > 0)) {
                        for (int j = 0; j < components.length(); j++) {
                            Map<String, JSONObject> cokey = JSONUtils.preprocess(components.getJSONArray(j));
                            SoilComponent tSC = new SoilComponent(AoAId, JSONUtils.getStringParam(cokey, "cokey", "err"),
                                    JSONUtils.getStringParam(cokey, "compname", " "), JSONUtils.getDoubleParam(cokey, "aoa_comp_area", 0.0),
                                    JSONUtils.getStringParam(cokey, "aoa_comp_hsg", "err"), JSONUtils.getDoubleParam(cokey, "aoa_comp_kfact", 0.0),
                                    JSONUtils.getDoubleParam(cokey, "aoa_comp_om", 0.0), JSONUtils.getIntParam(cokey, "aoa_comp_hzdepth", 0),
                                    JSONUtils.getBooleanParam(cokey, "aoa_comp_cracksgr24", false), JSONUtils.getBooleanParam(cokey, "aoa_comp_hwt_lt_24", false)
                            );
                            if (!tSC.getError()) {
                                if (!soilComponents.add(tSC)) {
                                    //Shouldn't ever happen, but just in case...
                                    error_msg += "Internal error, could not add this component to the list.";
                                    break;
                                }
                            } else {
                                //Most likely an input validation error here, caught by the subclass
                                error_msg += tSC.getErrorMsg();
                                break;
                            }
                        }// End for loop of components for this AoA
                    } else {
                        //This component was empty or the JSON failed to create an item.
                        error_msg += " This AoA's component is empty or missing, cannot proceed.";
                    }
                } catch (Exception ex) {
                    error_msg += " " + ex.getMessage();
                    LOG.log(Level.SEVERE, "Error in processing the request JSON for WQM-7!", ex);
                    throw new ServiceException("Error in processing the request JSON.", ex);
                }
            }
        }

        public Boolean process() {
            Boolean ret_val = true;

            for (SoilComponent component : soilComponents) {
                if (!component.process()) {
                    error_msg += " " + component.getErrorMsg();
                    break;
                }

                cum_pslp_product += (component.getCompPslpNumber() * component.getAoaCompArea());
                aoa_area += component.getAoaCompArea();
            }

            if (error_msg.isEmpty()) {
                double aoa_pslp_fract = (cum_pslp_product / aoa_area);

                if (aoa_pslp_fract <= 1.50) {
                    aoa_pslp = "VERY LOW";
                } else if (aoa_pslp_fract > 1.50 && aoa_pslp_fract <= 2.50) {
                    aoa_pslp = "LOW";
                } else if (aoa_pslp_fract > 2.50 && aoa_pslp_fract <= 3.50) {
                    aoa_pslp = "INTERMEDIATE";
                } else {
                    aoa_pslp = "HIGH";
                }
            }

            return ret_val;
        }

        public JSONArray putResults() throws ServiceException {
            JSONArray AoAData;
            JSONArray components;

            AoAData = new JSONArray();
            try {
                AoAData.put(JSONUtils.dataDesc("AoAId", AoAId, "Area of Analysis Identifier"));
                AoAData.put(JSONUtils.dataDesc("aoa_pslp", aoa_pslp, "Pesticide soil leaching potential representing the AoA"));

                components = new JSONArray();
                for (SoilComponent component : soilComponents) {
                    components.put(component.putResults());
                    if (component.getError()) {
                        error_msg += " " + component.getErrorMsg();
                        break;
                    }
                }

                AoAData.put(JSONUtils.dataDesc("componentlist", components, "List of soil components"));
            } catch (JSONException ex) {
                error_msg += " " + ex.getMessage();
                LOG.log(Level.SEVERE, "Error in processing the response JSON for WQM-7!", ex);
                throw new ServiceException("Error in processing the response JSON.", ex);
            }

            return AoAData;
        }

        public Boolean getError() {
            return (!error_msg.isEmpty());
        }

        public String getErrorMsg() {
            return error_msg;
        }

        public String getAoAPslp() {
            return aoa_pslp;
        }

        public int getAoAId() {
            return AoAId;
        }

        class SoilComponent {

            private final int AoAId;
            private final String cokey;
            private final String compname;
            private String comp_pslp;
            private int comp_pslp_number;
            private final double aoa_comp_area;
            private final String aoa_comp_hsg;
            private final double aoa_comp_kfact;
            private final double aoa_comp_om;
            private final int aoa_comp_hzdepth;
            private final boolean aoa_comp_cracksgr24;
            private final boolean aoa_comp_hwt_lt_24;
            private String error_msg;

            SoilComponent(int AoAId, String cokey, String compname, double aoa_comp_area, String aoa_comp_hsg, double aoa_comp_kfact, double aoa_comp_om, int aoa_comp_hzdepth, boolean aoa_comp_cracksgr24, boolean aoa_comp_hwt_lt_24) {
                this.AoAId = AoAId;
                this.cokey = cokey;
                this.compname = compname;
                this.aoa_comp_area = aoa_comp_area;
                this.aoa_comp_hsg = aoa_comp_hsg;
                this.aoa_comp_kfact = aoa_comp_kfact;
                this.aoa_comp_om = aoa_comp_om;
                this.aoa_comp_hzdepth = aoa_comp_hzdepth;
                this.aoa_comp_cracksgr24 = aoa_comp_cracksgr24;
                this.aoa_comp_hwt_lt_24 = aoa_comp_hwt_lt_24;

                this.error_msg = "";
                this.comp_pslp = "";
                this.comp_pslp_number = 0;

                if (cokey.equals("err") || aoa_comp_hsg.equals("err") || (AoAId == -1)) {
                    error_msg += " Invalid input data for this soil component.";
                }
            }

            public int getAoAId() {
                return AoAId;
            }

            public String getCokey() {
                return cokey;
            }

            public String getCompname() {
                return compname;
            }

            public String getCompPslp() {
                return comp_pslp;
            }

            ;
            public int getCompPslpNumber() {
                return comp_pslp_number;
            }

            public double getAoaCompArea() {
                return aoa_comp_area;
            }

            public Boolean getError() {
                return (!error_msg.isEmpty());
            }

            public String getErrorMsg() {
                return (error_msg);
            }

            public boolean process() {
                boolean ret_val = true;

                if (aoa_comp_hwt_lt_24) {
                    comp_pslp_number = HIGH;
                } else if (((aoa_comp_hsg.equals("A") || aoa_comp_hsg.equals("A/D")) && ((aoa_comp_hzdepth * aoa_comp_om) <= 30))
                        || ((aoa_comp_hsg.equals("B") || aoa_comp_hsg.equals("B/D")) && ((aoa_comp_hzdepth * aoa_comp_om) <= 9) && (aoa_comp_kfact <= 0.48))
                        || ((aoa_comp_hsg.equals("B") || aoa_comp_hsg.equals("B/D")) && ((aoa_comp_hzdepth * aoa_comp_om) <= 15) && (aoa_comp_kfact <= 0.26))) {
                    comp_pslp_number = HIGH;
                } else if (((aoa_comp_hsg.equals("B") || aoa_comp_hsg.equals("B/D")) && ((aoa_comp_hzdepth * aoa_comp_om) >= 35) && aoa_comp_kfact >= 0.40)
                        || ((aoa_comp_hsg.equals("B") || aoa_comp_hsg.equals("B/D")) && ((aoa_comp_hzdepth * aoa_comp_om) >= 45) && aoa_comp_kfact >= 0.20)
                        || ((aoa_comp_hsg.equals("C") || aoa_comp_hsg.equals("C/D")) && ((aoa_comp_hzdepth * aoa_comp_om) <= 10) && aoa_comp_kfact <= 0.28)
                        || ((aoa_comp_hsg.equals("C") || aoa_comp_hsg.equals("C/D")) && ((aoa_comp_hzdepth * aoa_comp_om) >= 10))) {
                    if (!aoa_comp_cracksgr24) {
                        comp_pslp_number = LOW;
                    } else {
                        comp_pslp_number = INTERMEDIATE;
                    }
                } else if (aoa_comp_hsg.equals("D")) {
                    if (!aoa_comp_cracksgr24) {
                        comp_pslp_number = VERY_LOW;
                    } else {
                        comp_pslp_number = LOW;
                    }
                } else if (!aoa_comp_cracksgr24) {
                    comp_pslp_number = INTERMEDIATE;
                } else {
                    comp_pslp_number = HIGH;
                }

                comp_pslp = comp_pslp_string[comp_pslp_number];

                return ret_val;
            }

            public JSONArray putResults() throws ServiceException {
                JSONArray componentData;
                componentData = new JSONArray();

                try {
                    componentData.put(JSONUtils.dataDesc("cokey", cokey, "Soil component key"));
                    componentData.put(JSONUtils.dataDesc("compname", compname, "Soil component name"));
                    componentData.put(JSONUtils.dataDesc("comp_pslp", comp_pslp, "Pesticide soil leaching potential representing the soil component"));
                } catch (JSONException ex) {
                    error_msg += " " + ex.getMessage();
                    LOG.log(Level.SEVERE, "Error in processing the response JSON for WQM-7!", ex);
                    throw new ServiceException("Error in processing the response JSON.", ex);
                }

                return componentData;
            }
        }
    }

    public class Input {

        private String error_msg;
        private JSONArray AoAList;

        Input(ArrayList<AoA> aoAs) throws ServiceException {
            error_msg = "";

            try {
                AoAList = parameter().getJSONArray("AoAList");
                if (null != AoAList) {
                    for (int i = 0; i < AoAList.length(); i++) {
                        Map<String, JSONObject> componentGroup = JSONUtils.preprocess(AoAList.getJSONArray(i));

                        int AoAId = JSONUtils.getIntParam(componentGroup, "AoAId", -1);

                        if (AoAId != -1) {
                            JSONArray components = JSONUtils.getJSONArrayParam(componentGroup, "componentlist");
                            AoA aoA = new AoA(AoAId, components);
                            if (aoA.getError()) {
                                error_msg += " " + aoA.getErrorMsg();
                                break;
                            } else {
                                aoAs.add(aoA);
                            }
                        } else {
                            error_msg += " The AoAId is not valid or is missing, cannot proceed";
                            break;
                        }
                    }//  End for loop of AoA's
                } else {
                    error_msg += " There were no AoA's found in the input payload.  Cannot proceed.";
                }
            } catch (ServiceException | JSONException ex) {
                error_msg += " " + ex.getMessage();
                LOG.log(Level.SEVERE, "Error in processing the request JSON for WQM-7!", ex);
                throw new ServiceException("Error in processing the request JSON.", ex);
            }
        }

        public String getErrorMsg() {
            return error_msg;
        }

        public Boolean getError() {
            return (!error_msg.isEmpty());
        }
    }

    public class Result {

        JSONArray AoAList;

        Result(ArrayList<AoA> aoAs) throws Exception {
            if (!aoAs.isEmpty()) {
                AoAList = new JSONArray();
                for (AoA aoA : aoAs) {
                    AoAList.put(aoA.putResults());
                    if (aoA.getError()) {
                        AoAList.put(JSONUtils.dataDesc("Error:", aoA.getErrorMsg(), "Error message returned while trying to build result output"));
                        break;
                    }
                }
            } else {
                AoAList = null;
            }
        }

        public void putResults() {
            if (null != AoAList) {
                results().put("AoAList", AoAList);
            }
        }
    }

}