/**
   * Parses AGGREGATE results for Oskari front
   *
   * @param response WPS vec:aggregate execute results
   * @param analysisLayer analysis layer params (field/columns info)
   * @return JSON.toSting() eg. aggregate WPS results
   *     **********************************************************************
   */
  public String parseAggregateResults(String response, AnalysisLayer analysisLayer) {

    try {

      // convert xml/text String to JSON

      final JSONObject json = XML.toJSONObject(response); // all
      // Add field name
      final AggregateMethodParams aggreParams =
          (AggregateMethodParams) analysisLayer.getAnalysisMethodParams();
      String fieldName = aggreParams.getAggreField1();
      if (analysisLayer.getInputAnalysisId() != null) {
        fieldName =
            analysisDataService.SwitchField2OriginalField(
                fieldName, analysisLayer.getInputAnalysisId());
      }
      json.put("fieldName", fieldName);
      return json.toString();

    } catch (JSONException e) {
      log.error(e, "XML to JSON failed", response);
    }

    return "{}";
  }
  /**
   * Get properties (native fields) of analysis layer
   *
   * @param sid
   * @return properties in array syntax eg. "{default:["t1","t2",...]}"
   */
  private String getAnalysisFeatureProperties(String sid) {
    String properties = null; // Field names
    if (sid.indexOf(ANALYSIS_PREFIX) > -1) {
      String[] values = sid.split("_");
      if (values.length > 0)
        properties = analysisDataService.getAnalysisNativeColumns(values[values.length - 1]);
    }

    return properties;
  }
  /**
   * Handles action_route CreateAnalysisLayer
   *
   * @param params Ajax request parameters
   *     **********************************************************************
   */
  public void handleAction(ActionParameters params) throws ActionException {

    if (params.getUser().isGuest()) {
      throw new ActionDeniedException("Session expired");
    }

    final String analyse = params.getRequiredParam(PARAM_ANALYSE, ERROR_ANALYSE_PARAMETER_MISSING);
    JSONObject analyseJson = JSONHelper.createJSONObject(analyse);
    if (analyseJson == null) {
      // json corrupted/parsing failed
      throw new ActionParamsException(ERROR_ANALYSE_PARAMETER_MISSING);
    }

    // filter conf data
    final String filter1 = params.getHttpParam(PARAM_FILTER1);
    final String filter2 = params.getHttpParam(PARAM_FILTER2);

    // Get baseProxyUrl
    final String baseUrl = getBaseProxyUrl(params);

    // User
    String uuid = params.getUser().getUuid();
    // note! analysisLayer is replaced in aggregate handling!!
    AnalysisLayer analysisLayer = getAnalysisLayer(analyseJson, filter1, filter2, baseUrl, uuid);
    Analysis analysis = null;

    if (analysisLayer.getMethod().equals(AnalysisParser.LAYER_UNION)) {
      // no WPS for merge analysis
      analysis = analysisDataService.mergeAnalysisData(analysisLayer, analyse, params.getUser());
    } else {
      // Generate WPS XML
      String featureSet = executeWPSprocess(analysisLayer);
      if (analysisLayer.getMethod().equals(AnalysisParser.UNION)
          || analysisLayer.getMethod().equals(AnalysisParser.INTERSECT)
          || analysisLayer.getMethod().equals(AnalysisParser.SPATIAL_JOIN)) {
        // Harmonize namespaces and element names
        featureSet = analysisParser.harmonizeElementNames(featureSet, analysisLayer);
      }

      // Add data to analysis db  - we must create still an union in aggregate case
      if (analysisLayer.getMethod().equals(AnalysisParser.AGGREGATE)) {
        // No store to analysis db for aggregate - set results in to the
        // response
        // Save analysis results - use union of input data
        analysisLayer.setWpsLayerId(-1);
        final String aggregateResult =
            this.localiseAggregateResult(
                analysisParser.parseAggregateResults(featureSet, analysisLayer), analyseJson);
        log.debug("\nAggregate results:\n", aggregateResult, "\n");
        /*
        Aggregate results:
         {"fi_url_1":{"Count":4},"tmp_id":{"Sum":45301,"Median":12232,"Count":4,"Standar
        d deviation":3186.3551571505645,"Maximum":14592,"Average":11325.25,"Minimum":624
        5},"fi_url_3":{"Count":4},"postinumero":{"Count":4},"fi_url_2":{"Count":4},"fi_s
        posti_1":{"Count":4},"kuntakoodi":{"Count":4},"fi_osoite":{"Count":4},"fi_nimi":
        {"Count":4},"kto_tarkennus":{"Count":4}}
                         */
        analysisLayer.setResult(aggregateResult);

        // Get geometry for aggretage features
        try {
          // Just return result as JSON and don't save analysis to DB
          if (!params.getHttpParam(PARAM_SAVE_BLN, true)) {
            // NOTE!! Replacing the analysisLayer!
            // Get response as geojson when no db store
            analysisLayer =
                getAggregateLayer(analyse, filter1, filter2, baseUrl, analysisLayer, JSONFORMAT);
            featureSet = wpsService.requestFeatureSet(analysisLayer);
            // Just return result as JSON and don't save analysis to DB
            // Get geometry as geojson for hilighting features of aggregate result
            JSONObject geojson = JSONHelper.createJSONObject(featureSet);
            JSONObject jsaggregate = JSONHelper.createJSONObject(aggregateResult);
            JSONObject results = new JSONObject();
            JSONHelper.putValue(results, JSON_KEY_GEOJSON, geojson);
            JSONHelper.putValue(results, JSON_KEY_AGGREGATE_RESULT, jsaggregate);
            ResponseHelper.writeResponse(params, results);
            return;
          }
          // NOTE!! Replacing the analysisLayer!  - response is gml
          analysisLayer =
              getAggregateLayer(analyse, filter1, filter2, baseUrl, analysisLayer, null);
          featureSet = wpsService.requestFeatureSet(analysisLayer);
          // Harmonize namespaces and element names
          featureSet = analysisParser.harmonizeElementNames(featureSet, analysisLayer);
          featureSet =
              analysisParser.mergeAggregateResults2FeatureSet(
                  featureSet,
                  analysisLayer,
                  this.getRowOrder(analyseJson),
                  this.getColumnOrder(analyseJson));
          // Redefine column types
          analysisLayer.setFieldtypeMap(
              this.getAggregateFieldTypes(this.getColumnOrder(analyseJson)));
        } catch (ServiceException e) {
          throw new ActionException(ERROR_UNABLE_TO_GET_FEATURES_FOR_UNION, e);
        }
      }
      // Add extra TypeNames (depends on wps method)
      analysisParser.fixTypeNames(analysisLayer);

      // Fix geometry property name for WFST (could be any, depends on Wps method )
      fixGeometryPropertyName(analysisLayer);

      analysis =
          analysisDataService.storeAnalysisData(
              featureSet, analysisLayer, analyse, params.getUser());
    }

    if (analysis == null) {
      this.MyError(ERROR_UNABLE_TO_STORE_ANALYSIS_DATA, params, null);
      return;
    }

    analysisLayer.setWpsLayerId(analysis.getId()); // aka. analysis_id
    // Analysis field mapping
    analysisLayer.setLocaleFields(analysis);
    analysisLayer.setNativeFields(analysis);

    // copy permissions from source layer to new analysis
    final Resource sourceResource =
        getSourcePermission(analysisParser.getSourceLayerId(analyseJson), params.getUser());
    if (sourceResource != null) {
      final Resource analysisResource = new Resource();
      analysisResource.setType(AnalysisLayer.TYPE);
      analysisResource.setMapping("analysis", Long.toString(analysis.getId()));
      for (Permission p : sourceResource.getPermissions()) {
        // check if user has role matching permission?
        if (p.isOfType(Permissions.PERMISSION_TYPE_PUBLISH)
            || p.isOfType(Permissions.PERMISSION_TYPE_VIEW_PUBLISHED)) {
          analysisResource.addPermission(p.clonePermission());
        }
      }
      log.debug(
          "Trying to save permissions for analysis",
          analysisResource,
          analysisResource.getPermissions());
      permissionsService.saveResourcePermissions(analysisResource);
    } else {
      log.warn("Couldn't get source permissions for analysis, result will have none");
    }

    // Get analysisLayer JSON for response to front
    final JSONObject analysisLayerJSON = AnalysisHelper.getlayerJSON(analysis);

    // Additional param for new layer creation when merging layers:
    // - Notify client to remove merged layers since they are removed from backend
    JSONArray mlayers = new JSONArray();
    if (analysisLayer.getMergeAnalysisLayers() != null) {
      for (String lay : analysisLayer.getMergeAnalysisLayers()) {
        mlayers.put(lay);
      }
    }
    JSONHelper.putValue(analysisLayerJSON, "mergeLayers", mlayers);

    Set<String> permissionsList = permissionsService.getPublishPermissions(AnalysisLayer.TYPE);
    Set<String> downloadPermissionsList =
        permissionsService.getDownloadPermissions(AnalysisLayer.TYPE);
    Set<String> editAccessList = null;
    String permissionKey = "analysis+" + analysis.getId();
    JSONObject permissions =
        OskariLayerWorker.getPermissions(
            params.getUser(),
            permissionKey,
            permissionsList,
            downloadPermissionsList,
            editAccessList);
    JSONHelper.putValue(analysisLayerJSON, "permissions", permissions);

    ResponseHelper.writeResponse(params, analysisLayerJSON);
  }
  /**
   * Parses method parameters to WPS execute xml syntax definition
   *
   * @param layerJSON method parameters and layer info from the front
   * @param baseUrl Url for Geoserver WPS reference input (input FeatureCollection)
   * @return AnalysisLayer parameters for WPS execution
   *     **********************************************************************
   */
  public AnalysisLayer parseAnalysisLayer(String layerJSON, String filter, String baseUrl)
      throws ServiceException {
    AnalysisLayer analysisLayer = new AnalysisLayer();

    JSONObject json = JSONHelper.createJSONObject(layerJSON);
    WFSLayerConfiguration lc = null;

    // analysis input data type - default is WFS layer
    analysisLayer.setInputType(ANALYSIS_INPUT_TYPE_WFS);
    // analysis rendering url
    analysisLayer.setWpsUrl(analysisRenderingUrl);
    // analysis element name
    analysisLayer.setWpsName(analysisRenderingElement);

    analysisLayer.setInputAnalysisId(null);
    int id = 0;
    try {
      // Analysis input property types
      this.prepareFieldtypeMap(analysisLayer, json);

      String sid = json.getString(JSON_KEY_LAYERID);

      // Input is wfs layer or analaysis layer or my places
      if (sid.indexOf(LAYER_PREFIX) == 0) {
        // Analysislayer is input
        if (!this.prepareAnalysis4Analysis(analysisLayer, json))
          throw new ServiceException("AnalysisInAnalysis parameters are invalid");
        id = analysisLayer.getId();
      } else if (sid.indexOf(MYPLACES_LAYER_PREFIX) == 0) {
        // myplaces is input
        if (!this.prepareAnalysis4Myplaces(analysisLayer, json))
          throw new ServiceException("AnalysisInMyPlaces parameters are invalid");
        id = analysisLayer.getId();
      } else {
        // Wfs layer id
        id = ConversionHelper.getInt(sid, -1);
      }
    } catch (JSONException e) {
      throw new ServiceException("AnalysisInAnalysis parameters are invalid");
    }
    // --- WFS layer is analysis input
    analysisLayer.setId(id);

    // Get wfs layer configuration
    lc = layerConfigurationService.findConfiguration(id);

    final OskariLayer wfsLayer = mapLayerService.find(id);
    log.debug("got wfs layer", wfsLayer);
    analysisLayer.setMinScale(wfsLayer.getMinScale());
    analysisLayer.setMaxScale(wfsLayer.getMaxScale());

    // Set WFS input type, other than analysis_ and myplaces_- default is REFERENCE
    this.setWpsInputLayerType(lc.getWps_params(), analysisLayer);

    // Extract parameters for analysis methods from layer

    String name = json.optString("name");
    if (name.isEmpty()) {
      throw new ServiceException("Analysis name missing.");
    } else {
      analysisLayer.setName(name);
    }

    JSONArray fields_in = json.optJSONArray("fields");
    List<String> fields = new ArrayList<String>();

    if (fields_in == null) {
      throw new ServiceException("Fields missing.");
    } else {
      // Add fields of WFS service, if empty and all fields mode on
      if (fields_in.length() == 0) fields_in = this.getWfsFields(analysisLayer);
      // Remove internal fields
      try {
        for (int i = 0; i < fields_in.length(); i++) {
          if (!HIDDEN_FIELDS.contains(fields_in.getString(i))) fields.add(fields_in.getString(i));
        }
      } catch (JSONException e) {
        throw new ServiceException("Method fields parameters missing.");
      }
      analysisLayer.setFields(fields);
    }

    String style = json.optString("style");
    if (style.isEmpty()) {
      throw new ServiceException("Style missing.");
    } else {
      analysisLayer.setStyle(style);
    }

    Integer opacity = json.optInt("opacity");
    if (opacity == 0) opacity = DEFAULT_OPACITY;
    analysisLayer.setOpacity(opacity);

    String analysisMethod = json.optString("method"); // "union_geom"; test
    analysisLayer.setMethod(analysisMethod);

    analysisLayer.setAggreFunctions(null);
    analysisLayer.setMergeAnalysisLayers(null);

    // ------------------LAYER_UNION -----------------------
    if (LAYER_UNION.equals(analysisMethod)) {
      JSONObject params;
      try {
        params = json.getJSONObject(JSON_KEY_METHODPARAMS);
      } catch (JSONException e) {
        throw new ServiceException("Method parameters missing.");
      }
      JSONArray sids = params.optJSONArray(JSON_KEY_LAYERS);
      // Loop merge layers - get analysis ids
      List<Long> ids = new ArrayList<Long>();
      List<String> mergelays = new ArrayList<String>();
      if (sids == null) {
        throw new ServiceException("merge layers missing");
      } else {
        try {
          for (int i = 0; i < sids.length(); i++) {
            Long aid = this.getAnalysisId(sids.getString(i));
            if (aid > 0) {
              ids.add(aid);
              mergelays.add(sids.getString(i));
            }
          }
        } catch (JSONException e) {
          throw new ServiceException("Merge layers missing.");
        }
        // Merge analysis Ids
        analysisLayer.setMergeAnalysisIds(ids);
        // Merge analysis Layers
        analysisLayer.setMergeAnalysisLayers(mergelays);
      }
    }
    // ------------------ BUFFER -----------------------
    else if (BUFFER.equals(analysisMethod)) {
      // when analysisMethod == vec:BufferFeatureCollection

      // Set params for WPS execute

      BufferMethodParams method = this.parseBufferParams(lc, json, baseUrl);

      method.setWps_reference_type(analysisLayer.getInputType());
      analysisLayer.setAnalysisMethodParams(method);

      // WFS filter
      analysisLayer
          .getAnalysisMethodParams()
          .setFilter(
              this.parseFilter(
                  lc,
                  filter,
                  analysisLayer.getInputAnalysisId(),
                  analysisLayer.getInputCategoryId()));
      // WFS Query properties
      analysisLayer
          .getAnalysisMethodParams()
          .setProperties(
              this.parseProperties(
                  analysisLayer.getFields(),
                  lc.getFeatureNamespace(),
                  lc.getGMLGeometryProperty()));
      // ------------------ INTERSECT -----------------------
    } else if (INTERSECT.equals(analysisMethod)) {
      JSONObject params;
      try {
        params = json.getJSONObject(JSON_KEY_METHODPARAMS);
      } catch (JSONException e) {
        throw new ServiceException("Method parameters missing.");
      }
      WFSLayerConfiguration lc2 = null;
      int id2 = 0;
      String sid = "";
      try {
        sid = params.getString(JSON_KEY_LAYERID);
        // Input is wfs layer or analaysis layer
        if (sid.indexOf(LAYER_PREFIX) == 0) {
          // Analysislayer is input
          // eg. analyse_216_340
          id2 = ConversionHelper.getInt(analysisBaseLayerId, 0);

        } else if (sid.indexOf(MYPLACES_LAYER_PREFIX) == 0) {
          // Myplaces is input
          id2 = ConversionHelper.getInt(myplacesBaseLayerId, 0);

        } else {
          // Wfs layer id
          id2 = ConversionHelper.getInt(sid, -1);
        }
      } catch (JSONException e) {
        throw new ServiceException("AnalysisInAnalysis parameters are invalid");
      }

      // Get wfs layer configuration for union input 2
      lc2 = layerConfigurationService.findConfiguration(id2);

      // Set params for WPS execute

      IntersectMethodParams method = this.parseIntersectParams(lc, lc2, json, baseUrl);

      method.setWps_reference_type(analysisLayer.getInputType());
      if (sid.indexOf(LAYER_PREFIX) == 0 || sid.indexOf(MYPLACES_LAYER_PREFIX) == 0) {
        method.setWps_reference_type2(ANALYSIS_INPUT_TYPE_GS_VECTOR);
      } else {
        method.setWps_reference_type2(ANALYSIS_INPUT_TYPE_WFS);
      }
      // Set WFS input type, other than analysis_ and myplaces_- default is REFERENCE
      this.setWpsInputLayerType(lc.getWps_params(), analysisLayer);

      // WFS filter

      method.setFilter(
          this.parseFilter(
              lc, filter, analysisLayer.getInputAnalysisId(), analysisLayer.getInputCategoryId()));

      if (sid.indexOf(MYPLACES_LAYER_PREFIX) == 0) {
        method.setFilter2(this.parseFilter(lc2, null, null, this.getAnalysisInputId(params)));
      } else {
        method.setFilter2(this.parseFilter(lc2, null, this.getAnalysisInputId(params), null));
      }
      // WFS Query properties
      method.setProperties(
          this.parseProperties(
              analysisLayer.getFields(), lc.getFeatureNamespace(), lc.getGMLGeometryProperty()));

      analysisLayer.setAnalysisMethodParams(method);
      // ------------------ AGGREGATE -----------------------
    } else if (AGGREGATE.equals(analysisMethod)) {

      // 1 to n aggregate wps tasks
      String aggre_field = null;
      try {

        aggre_field = json.getJSONObject(JSON_KEY_METHODPARAMS).optString(JSON_KEY_AGGRE_ATTRIBUTE);
        if (analysisLayer.getInputType().equals(ANALYSIS_INPUT_TYPE_GS_VECTOR)) {
          if (analysisLayer.getInputAnalysisId() != null) {
            aggre_field =
                analysisDataService.SwitchField2AnalysisField(
                    aggre_field, analysisLayer.getInputAnalysisId());
          }
        }
        JSONArray aggre_func_in =
            json.getJSONObject(JSON_KEY_METHODPARAMS).optJSONArray(JSON_KEY_FUNCTIONS);
        List<String> aggre_funcs = new ArrayList<String>();
        if (aggre_func_in == null) {
          throw new ServiceException("Aggregate functions missing.");
        } else {
          try {
            for (int i = 0; i < aggre_func_in.length(); i++) {
              aggre_funcs.add(aggre_func_in.getString(i));
            }
          } catch (JSONException e) {
            throw new ServiceException("Aggregate functions missing.");
          }
          analysisLayer.setAggreFunctions(aggre_funcs);
        }

      } catch (JSONException e) {
        throw new ServiceException("Method parameters missing.");
      }

      // Set params for WPS execute
      if (aggre_field == null) throw new ServiceException("Aggregate field parameter missing.");
      AggregateMethodParams method =
          this.parseAggregateParams(
              lc, json, baseUrl, aggre_field, analysisLayer.getAggreFunctions());

      method.setWps_reference_type(analysisLayer.getInputType());
      analysisLayer.setAnalysisMethodParams(method);
      // WFS filter

      analysisLayer
          .getAnalysisMethodParams()
          .setFilter(
              this.parseFilter(
                  lc,
                  filter,
                  analysisLayer.getInputAnalysisId(),
                  analysisLayer.getInputCategoryId()));
      // ------------------ UNION -----------------------
    } else if (UNION.equals(analysisMethod)) {
      JSONObject params;
      try {
        params = json.getJSONObject(JSON_KEY_METHODPARAMS);
      } catch (JSONException e) {
        throw new ServiceException("Method parameters missing.");
      }

      // Set params for WPS execute

      UnionMethodParams method = this.parseUnionParams(lc, json, baseUrl);
      method.setWps_reference_type(analysisLayer.getInputType());

      // WFS filter

      method.setFilter(
          this.parseFilter(
              lc, filter, analysisLayer.getInputAnalysisId(), analysisLayer.getInputCategoryId()));

      analysisLayer.setAnalysisMethodParams(method);

    } else {
      throw new ServiceException("Method parameters missing.");
    }

    return analysisLayer;
  }