/*
   * Return the SelectionListValue's associated with a single ReportParameter
   * that is specified by its id. This endpoint can be tested with:
   *
   * $ mvn clean spring-boot:run $ curl -i -H "Accept: application/json;v=1"
   * -X GET \
   * http://localhost:8080/rest/reportParameters/0955ab6e-6ce6-4b90-a60c-
   * aa0548e4d358/selectionListValues\ ?expand=selectionListValues
   *
   * @Transactional is used to avoid org.hibernate.LazyInitializationException
   * being thrown when evaluating reportParameter.getSelectionListValues().
   */
  @Transactional
  @Path("/{id}" + ResourcePath.SELECTIONLISTVALUES_PATH)
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @PreAuthorize("hasAuthority('" + Authority.AUTHORITY_NAME_MANAGE_REPORTS + "')")
  public SelectionListValueCollectionResource getSelectionListValuesByReportParameterId(
      @PathParam("id") final UUID id,
      @HeaderParam("Accept") final String acceptHeader,
      @QueryParam(ResourcePath.EXPAND_QP_NAME) final List<String> expand,
      @QueryParam(ResourcePath.SHOWALL_QP_NAME) final List<String> showAll,
      @QueryParam(ResourcePath.PARENTPARAMVALUE_QP_NAME) final List<String> parentParamValues,
      @Context final UriInfo uriInfo)
      throws RptdesignOpenFromStreamException, BirtException {
    Map<String, List<String>> queryParams = new HashMap<>();
    queryParams.put(ResourcePath.EXPAND_QP_KEY, expand);
    queryParams.put(ResourcePath.SHOWALL_QP_KEY, showAll);
    queryParams.put(ResourcePath.PARENTPARAMVALUE_QP_NAME, parentParamValues);
    RestApiVersion apiVersion = RestUtils.extractAPIVersion(acceptHeader, RestApiVersion.v1);

    ReportParameter reportParameter = reportParameterRepository.findOne(id);
    RestUtils.ifNullThen404(
        reportParameter, ReportParameter.class, "reportParameterId", id.toString());

    /*
     * Create a SelectionListValueCollectionResource to represent the
     * selection list values. If the selection list is dynamic, i.e., it
     * must be generated by running a query that is represented by a data
     * source specified in the rptdesign file, then it is necessary to use
     * the BIRT API to request these values.
     *
     * If, on the other hand, the selection list is static or there is no
     * selection list at all, then it is a simple matter of creating a
     * SelectionListValueCollectionResource from SelectionListValue entities
     * stored in the report server database.
     */
    SelectionListValueCollectionResource selectionListValueCollectionResource = null;
    if (reportParameter.getSelectionListType().equals(IParameterDefn.SELECTION_LIST_DYNAMIC)) {
      /*
       * The selection list is *dynamic*, i.e., it is defined by some sort
       * of database query, instead of consisting of static values that
       * were chosen when the rptdesign file was created.
       */
      try {
        logger.info("parentParamValues = {}", parentParamValues);
        String rptdesign = reportParameter.getReportVersion().getRptdesign();

        List<SelectionListValue> selectionListValues =
            reportParameterService.getDynamicSelectionListValues(
                reportParameter, parentParamValues, rptdesign, uriInfo, queryParams, apiVersion);

        /*
         * Note: The SelectionListValue entities in the list
         *       "selectionListValues" will have
         *       selectionListValueId = null because they were not
         *       retrieved from the database.
         *
         *       In addition, "createdOn" for each entity will be the
         *       datetime when the entity object was CONSTRUCTED.
         */
        selectionListValueCollectionResource =
            new SelectionListValueCollectionResource(
                selectionListValues,
                SelectionListValue.class,
                AbstractBaseResource.createHref(
                    uriInfo, ReportParameter.class, reportParameter.getReportParameterId(), null),
                ResourcePath.SELECTIONLISTVALUES_PATH,
                uriInfo,
                queryParams,
                apiVersion);

      } catch (DynamicSelectionListKeyException e) {
        throw new RestApiException(
            RestError.FORBIDDEN_DYN_SEL_LIST_PARENT_KEY_COUNT, e.getMessage());
      }

    } else {
      /*
       * Either the selection list is STATIC or there is NO selection list
       * at all. It is a simple matter of returning a
       * SelectionListValueCollectionResource based on SelectionListValue
       * entities stored in the report server database that are linked to
       * the specified report parameter.
       */
      selectionListValueCollectionResource =
          new SelectionListValueCollectionResource(
              reportParameter, uriInfo, queryParams, apiVersion);
    }
    return selectionListValueCollectionResource;
  }
  /*
   * This endpoint can be tested with:
   *
   * $ mvn clean spring-boot:run $ curl -iH "Accept: application/json;v=1" -H
   * "Content-Type: application/json" -X PUT -d \
   * '{"reportVersion":{"reportVersionId":
   * "7ff1da11-a229-45c5-90be-79417a628125"},"orderIndex":999,\
   * "controlType":0,"promptText":"New prompt text","required":false,\
   * "defaultValue":"new default value","displayName":"new display name",\
   * "helpText":"new help text","displayFormat":"new display format",\
   * "alignment":3,"allowNewValues":true,\
   * "parameterGroup":{"parameterGroupId":
   * "abc81f46-3940-44bb-a401-d937c55a10bc"}}' \
   * http://localhost:8080/rest/reportParameters/fecd8f20-5ff0-4b0f-ae0c-
   * 0829cea1c938
   *
   * All UUID values must be replaced here with actual resource id's from the
   * report server database, of course.
   *
   * If the ReportParameter to be updated is not linked to a ParameterGroup,
   * then the "parameterGroup" attribute in the PUT data should be omitted
   * entirely.
   */
  @Path("/{id}")
  @PUT
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  @PreAuthorize("hasAuthority('" + Authority.AUTHORITY_NAME_MANAGE_REPORTS + "')")
  public Response updateById(
      ReportParameterResource reportParameterResource,
      @PathParam("id") final UUID id,
      @HeaderParam("Accept") final String acceptHeader,
      @QueryParam(ResourcePath.EXPAND_QP_NAME) final List<String> expand,
      @QueryParam(ResourcePath.SHOWALL_QP_NAME) final List<String> showAll,
      @Context final ServletContext servletContext,
      @Context final UriInfo uriInfo) {
    Map<String, List<String>> queryParams = new HashMap<>();
    queryParams.put(ResourcePath.EXPAND_QP_KEY, expand);
    queryParams.put(ResourcePath.SHOWALL_QP_KEY, showAll);
    RestApiVersion apiVersion = RestUtils.extractAPIVersion(acceptHeader, RestApiVersion.v1);

    /*
     * Retrieve ReportParameter entity to be updated.
     */
    ReportParameter reportParameter = reportParameterRepository.findOne(id);
    RestUtils.ifNullThen404(reportParameter, ReportParameter.class, "reportId", id.toString());

    /*
     * Treat attributes of reportParameterResource that are effectively
     * required. These attributes can be omitted in the PUT data, but in
     * that case they are then set here to the CURRENT values from the
     * ReportParameter entity. These are that attributes that are required,
     * but if their value does not need to be changed, they do not need to
     * be included in the PUT data.
     */
    if (reportParameterResource.getOrderIndex() == null) {
      reportParameterResource.setOrderIndex(reportParameter.getOrderIndex());
    }
    if (reportParameterResource.getControlType() == null) {
      reportParameterResource.setControlType(reportParameter.getControlType());
    }
    if (reportParameterResource.getValueConcealed() == null) {
      reportParameterResource.setValueConcealed(reportParameter.getValueConcealed());
    }
    if (reportParameterResource.getAllowNewValues() == null) {
      reportParameterResource.setAllowNewValues(reportParameter.getAllowNewValues());
    }
    if (reportParameterResource.getAlignment() == null) {
      reportParameterResource.setAlignment(reportParameter.getAlignment());
    }
    if (reportParameterResource.getPromptText() == null) {
      reportParameterResource.setPromptText(reportParameter.getPromptText());
    }

    /*
     * The values for the following attributes cannot be changed. These
     * attributes should not appear in the PUT data, but if any do, their
     * values will not be used because they will be overridden here by
     * forcing their values to be the same as the current value stored for
     * the ReportParameter entity.
     */
    reportParameterResource.setReportParameterId(reportParameter.getReportParameterId());
    reportParameterResource.setName(reportParameter.getName());
    reportParameterResource.setDataType(reportParameter.getDataType());
    reportParameterResource.setRequired(reportParameter.getRequired());
    reportParameterResource.setMultivalued(reportParameter.getMultivalued());
    reportParameterResource.setHidden(reportParameter.getHidden());
    reportParameterResource.setDisplayInFixedOrder(reportParameter.getDisplayInFixedOrder());
    reportParameterResource.setParameterType(reportParameter.getParameterType());
    reportParameterResource.setSelectionListType(reportParameter.getSelectionListType());
    reportParameterResource.setAutoSuggestThreshold(reportParameter.getAutoSuggestThreshold());
    reportParameterResource.setValueExpr(reportParameter.getValueExpr());
    reportParameterResource.setCreatedOn(reportParameter.getCreatedOn());
    /*
     * Construct a ReportVersionResource to specify the CURRENTLY
     * selected ReportVersion.
     */
    UUID currentReportVersionId = reportParameter.getReportVersion().getReportVersionId();
    ReportVersionResource reportVersionResource = new ReportVersionResource();
    reportVersionResource.setReportVersionId(currentReportVersionId);
    reportParameterResource.setReportVersionResource(reportVersionResource);

    /*
     * The values of the following attributes be *cleared* if they do not
     * appear in the PUT data. These are attributes where SQL NULL is a
     * legal value in the underlying database [report_parameter] table.
     *
     *   defaultValue
     *   displayFormat
     *   parameterGroup
     *   displayName
     *   helpText
     */

    /*
     * Although the parameterGroup attribute can be omitted from the PUT
     * data, if it *is* present, the ParameterGroupResource resource *must*
     * have a value for its parameterGroupId attribute.
     */
    if (reportParameterResource.getParameterGroupResource() != null) {
      /*
       * If a "reportParameterResource" attribute *is* supplied in the PUT
       * data, it *must* have a value set for its "reportParameterId"
       * attribute...
       */
      RestUtils.ifAttrNullOrBlankThen403(
          reportParameterResource.getParameterGroupResource().getParameterGroupId(),
          ParameterGroup.class,
          "parameterGroupId");
      /*
       * ... and the value for "parameterGroupId" must correspond to an
       * existing ParameterGroup entity.
       */
      UUID parameterGroupId =
          reportParameterResource.getParameterGroupResource().getParameterGroupId();
      RestUtils.ifNullThen404(
          parameterGroupRepository.findOne(parameterGroupId),
          ParameterGroup.class,
          "parameterGroupId",
          parameterGroupId.toString());
    }

    /*
     * Save updated entity.
     */
    reportParameter = reportParameterService.saveExistingFromResource(reportParameterResource);

    return Response.status(Response.Status.OK).build();
  }