/**
   * Initialises the RODA client services.
   *
   * @throws PluginException if an error occurred during initialisation
   */
  private void initClientServices() throws PluginException {

    final String rodaClientServiceUrl =
        getParameterValues().get(AbstractPlugin.PARAMETER_RODA_CORE_URL().getName());
    final String rodaClientUsername =
        getParameterValues().get(AbstractPlugin.PARAMETER_RODA_CORE_USERNAME().getName());
    final String rodaClientPassword =
        getParameterValues().get(AbstractPlugin.PARAMETER_RODA_CORE_PASSWORD().getName());

    try {

      rodaClient =
          new RODAClient(new URL(rodaClientServiceUrl), rodaClientUsername, rodaClientPassword);
      rodaUploader =
          new Uploader(new URL(rodaClientServiceUrl), rodaClientUsername, rodaClientPassword);
      rodaDownloader = rodaClient.getDownloader();

    } catch (RODAClientException e) {
      logger.debug("Exception creating RODA Client - " + e.getMessage(), e);
      throw new PluginException("Exception creating RODA Client - " + e.getMessage(), e);
    } catch (LoginException e) {
      logger.debug("Exception creating RODA Client - " + e.getMessage(), e);
      throw new PluginException("Exception creating RODA Client - " + e.getMessage(), e);
    } catch (MalformedURLException e) {
      logger.debug("Exception creating service URL - " + e.getMessage(), e);
      throw new PluginException("Exception creating service URL - " + e.getMessage(), e);
    } catch (DownloaderException e) {
      logger.debug("Exception creating RODA downloader - " + e.getMessage(), e);
      throw new PluginException("Exception creating service URL - " + e.getMessage(), e);
    }
  }
  private List<String> getRepresentationPIDsFromBrowser(String type, List<String> subTypes)
      throws RODAException {

    Filter filter = new Filter();
    if (!StringUtils.isBlank(type)) {

      filter.add(new SimpleFilterParameter("type", type));

      // No sense to use subType without type
      if (subTypes != null && subTypes.size() > 0) {

        String[] subTypesArray = subTypes.toArray(new String[subTypes.size()]);

        filter.add(new OneOfManyFilterParameter("subtype", subTypesArray));
      }
    }

    Sorter sorter = new Sorter();
    sorter.add(new SortParameter("pid", false));

    try {

      SimpleRepresentationObject[] simpleROs =
          this.browserService.getSimpleRepresentationObjects(
              new ContentAdapter(filter, sorter, null));
      List<String> pids = new ArrayList<String>();
      if (simpleROs != null) {
        for (SimpleRepresentationObject simpleRO : simpleROs) {
          pids.add(simpleRO.getPid());
        }
      }

      return pids;

    } catch (BrowserException e) {
      logger.debug(
          "Error getting representations with specified type and subType - " + e.getMessage(), e);
      throw new BrowserException(
          "Error getting representations with specified type and subType - " + e.getMessage(), e);
    } catch (RemoteException e) {
      RODAException rodaException = RODAClient.parseRemoteException(e);
      logger.debug(
          "Error getting representations with specified type and subType - "
              + rodaException.getMessage(),
          rodaException);
      throw rodaException;
    }
  }
  private ReportItem executeOn(String representationPID) throws PluginException {

    ReportItem reportItem = new ReportItem("Convertion of representation " + representationPID);
    reportItem.addAttribute(new Attribute("start datetime", DateParser.getIsoDate(new Date())));

    RepresentationObject originalRObject = null;
    try {

      reportItem.addAttribute(
          new Attribute(
              "Download original representation - start datetime",
              DateParser.getIsoDate(new Date())));

      // Get original representation from RODA Core
      originalRObject = this.browserService.getRepresentationObject(representationPID);

      logger.info("Original representation is " + originalRObject);

      reportItem.addAttribute(
          new Attribute(
              "Download original representation - finnish datetime",
              DateParser.getIsoDate(new Date())));

    } catch (BrowserException e) {
      logger.debug(
          "Error accessing representation " + representationPID + " - " + e.getMessage(), e);
      throw new PluginException(
          "Error accessing representation " + representationPID + " - " + e.getMessage(),
          e,
          reportItem);
    } catch (NoSuchRODAObjectException e) {
      logger.debug(
          "Error accessing representation " + representationPID + " - " + e.getMessage(), e);
      throw new PluginException(
          "Error accessing representation " + representationPID + " - " + e.getMessage(),
          e,
          reportItem);
    } catch (RemoteException e) {
      logger.debug(
          "Error accessing representation " + representationPID + " - " + e.getMessage(), e);
      throw new PluginException(
          "Error accessing representation " + representationPID + " - " + e.getMessage(),
          e,
          reportItem);
    }

    // Download representation files and verify if the files are already
    // normalised

    if (isRepresentationConverted(originalRObject)) {
      // No need to call the convert plugin

      try {

        String normalizedROPID =
            this.ingestService.setDONormalizedRepresentation(
                originalRObject.getDescriptionObjectPID(), originalRObject.getPid());

        logger.info(
            "Marked representation "
                + normalizedROPID //$NON-NLS-1$
                + " as normalized"); //$NON-NLS-1$

        reportItem.addAttributes(
            new Attribute("Action", "Representation was marked as normalized"));

      } catch (NoSuchRODAObjectException e) {
        reportItem.addAttributes(
            new Attribute(
                "Error",
                "Error setting representation status to normalized (" + e.getMessage() + ")"));
        throw new PluginException(
            "Error setting representation status to normalized - " + e.getMessage(), e, reportItem);
      } catch (IngestException e) {
        reportItem.addAttributes(
            new Attribute(
                "Error",
                "Error setting representation status to normalized (" + e.getMessage() + ")"));
        throw new PluginException(
            "Error setting representation status to normalized - " + e.getMessage(), e, reportItem);
      } catch (RemoteException e) {
        RODAException exception = RODAClient.parseRemoteException(e);
        reportItem.addAttributes(
            new Attribute(
                "Error",
                "Error setting representation status to normalized (" + e.getMessage() + ")"));
        throw new PluginException(
            "Error setting representation status to normalized - " + exception.getMessage(),
            exception,
            reportItem);
      }

      reportItem.addAttributes(
          new Attribute("finnish datetime", DateParser.getIsoDate(new Date())));

      return reportItem;

    } else {

      // Representation is not normalized and we need to call the
      // converter. Continue...

    }

    ConversionResult convertResult = null;
    RepresentationObject convertedRObject = null;
    try {

      reportItem.addAttribute(
          new Attribute("Conversion - start datetime", DateParser.getIsoDate(new Date())));

      logger.info("Converting representation " + originalRObject.getPid());

      // Calling converter
      convertResult = convert(originalRObject);
      convertedRObject = convertResult.getRepresentation();

      logger.info("Convert " + originalRObject.getPid() + " finished");

      reportItem.addAttribute(
          new Attribute("Conversion - finnish datetime", DateParser.getIsoDate(new Date())));
      reportItem.addAttribute(
          new Attribute(
              "Conversion - converted representation",
              convertResult.getRepresentation().toString()));

      if (convertResult.getMigrationEvent() == null) {

        logger.warn("Migration event is null");

      } else {

        logger.info(
            "Conversion outcome is "
                + convertResult.getMigrationEvent().getOutcomeDetailExtension());

        logger.info("Converted representation is " + convertedRObject);

        reportItem.addAttribute(
            new Attribute(
                "Conversion - outcome details",
                convertResult.getMigrationEvent().getOutcomeDetailExtension()));
      }

    } catch (RepresentationConverterException e) {
      logger.debug(
          "Error converting representation " + representationPID + " - " + e.getMessage(), e);
      e.setReportItem(reportItem);
      throw e;
    }

    RepresentationObject writtenRO = null;
    String roPID = null;
    try {

      // Create temporary directory
      File temporaryDirectory = TempDir.createUniqueTemporaryDirectory("rep");

      reportItem.addAttributes(
          new Attribute(
              "Download converted representation - temporary directory",
              temporaryDirectory.toString()),
          new Attribute(
              "Download converted representation - start datetime",
              DateParser.getIsoDate(new Date())));

      logger.info("Writting converted representation to " + temporaryDirectory);

      // Write representation to temporary directory
      writtenRO =
          this.migratorClient.writeRepresentationObject(convertedRObject, temporaryDirectory);

      logger.info("Representation written");

      reportItem.addAttribute(
          new Attribute(
              "Download converted representation - finnish datetime",
              DateParser.getIsoDate(new Date())));

      reportItem.addAttribute(
          new Attribute("Ingest - start datetime", DateParser.getIsoDate(new Date())));

      logger.info("Ingesting converted representation");

      if (isNormalization()) {
        logger.info(
            "This is a normalization process. Setting representation status to "
                + RepresentationObject.STATUS_NORMALIZED);
        writtenRO.setStatuses(new String[] {RepresentationObject.STATUS_NORMALIZED});
      } else {
        logger.info(
            "This is NOT a normalization process. Setting representation status to "
                + RepresentationObject.STATUS_ALTERNATIVE);
        writtenRO.setStatuses(new String[] {RepresentationObject.STATUS_ALTERNATIVE});
      }

      // Ingest converted representation
      roPID = ingestRepresentation(writtenRO);

      logger.info("Representation ingested with PID " + roPID);

      reportItem.addAttribute(
          new Attribute("Ingest - finnish datetime", DateParser.getIsoDate(new Date())));
      reportItem.addAttribute(new Attribute("Ingest - ingested representation PID", roPID));

    } catch (MigratorClientException e) {
      logger.debug("Error downloading converted representation - " + e.getMessage(), e);
      throw new PluginException(
          "Error downloading converted representation  - " + e.getMessage(), e, reportItem);
    } catch (IOException e) {
      logger.debug("Error downloading converted representation - " + e.getMessage(), e);
      throw new PluginException(
          "Error downloading converted representation  - " + e.getMessage(), e, reportItem);
    } catch (IngestException e) {
      logger.debug("Error downloading converted representation - " + e.getMessage(), e);
      throw new PluginException(
          "Error downloading converted representation  - " + e.getMessage(), e, reportItem);
    }

    AgentPreservationObject agentPO = null;
    try {

      logger.info("Registering derivation event");

      reportItem.addAttribute(
          new Attribute("Register event - start datetime", DateParser.getIsoDate(new Date())));

      // Getting converter Agent
      AgentPreservationObject migratorAgent = getConverter().getAgent();

      reportItem.addAttribute(
          new Attribute("Register event - converter agent", migratorAgent.toString()));

      agentPO = new AgentPreservationObject();
      agentPO.setAgentType(AgentPreservationObject.PRESERVATION_AGENT_TYPE_MIGRATOR);
      agentPO.setAgentName(getName() + "/" + getVersion() + " - " + migratorAgent.getAgentName());

      logger.info("Agent is " + agentPO);

      reportItem.addAttribute(new Attribute("Register event - event agent", agentPO.toString()));

    } catch (Exception e) {
      // getConverter().getAgent();
      logger.debug("Error getting converter agent - " + e.getMessage(), e);

      // Delete roPID
      // delete(roPID);
      // try {
      // logger
      // .warn("Ingest of new representation failed. Removing created object "
      // + roPID);
      //
      // this.ingestService.removeObjects(new String[] { roPID });
      //
      // } catch (RemoteException e1) {
      // logger.warn("Error removing representation " + roPID + " - "
      // + e1.getMessage() + ". IGNORING", e1);
      // }

      throw new PluginException("Error getting converter agent - " + e.getMessage(), e, reportItem);
    }

    try {

      EventPreservationObject eventPO = convertResult.getMigrationEvent();

      if (eventPO == null) {
        eventPO = new EventPreservationObject();
      }

      if (isNormalization()) {
        eventPO.setEventType(EventPreservationObject.PRESERVATION_EVENT_TYPE_NORMALIZATION);
      } else {
        eventPO.setEventType(EventPreservationObject.PRESERVATION_EVENT_TYPE_MIGRATION);
      }
      if (StringUtils.isBlank(eventPO.getOutcome())) {
        eventPO.setOutcome("success");
      }
      if (StringUtils.isBlank(eventPO.getOutcomeDetailNote())) {
        eventPO.setOutcomeDetailNote("Converter details");
      }
      if (StringUtils.isBlank(eventPO.getOutcomeDetailExtension())) {
        eventPO.setOutcomeDetailExtension("no details");
      }

      logger.info("Event is " + eventPO);

      // reportItem.addAttribute(new Attribute(
      // "Register event - event outcome details", eventPO
      // .getOutcomeDetailExtension()));

      logger.info("Calling registerDerivationEvent(...)");

      // Register derivation event
      String epoPID =
          this.ingestService.registerDerivationEvent(
              originalRObject.getPid(), roPID, eventPO, agentPO, getParameterMakeObjectsActive());

      logger.info("Event registration finnished. Derivation event is " + epoPID);

      reportItem.addAttributes(
          new Attribute("Register event - event PID", epoPID),
          new Attribute("finnish datetime", DateParser.getIsoDate(new Date())));

      return reportItem;

    } catch (NoSuchRODAObjectException e) {
      // registerDerivationEvent(...)
      logger.debug("Error registering convertion event - " + e.getMessage(), e);
      throw new PluginException(
          "Error registering convertion event - " + e.getMessage(), e, reportItem);
    } catch (IngestException e) {
      // registerDerivationEvent(...)
      logger.debug("Error registering convertion event - " + e.getMessage(), e);
      throw new PluginException(
          "Error registering convertion event - " + e.getMessage(), e, reportItem);
    } catch (RemoteException e) {
      // registerDerivationEvent(...)
      logger.debug("Error registering convertion event - " + e.getMessage(), e);
      throw new PluginException(
          "Error registering convertion event - " + e.getMessage(), e, reportItem);
    }
  }
  private String createDerivedRepresentation(
      final String roPID,
      final Map<String, WorkflowExecutionOutput> outputs,
      final ReportItem reportItem)
      throws ExecutePlanException {

    logger.trace(String.format("createNewRepresentation(%s, %s)", roPID, outputs));

    RepresentationObject roOriginal = null;
    LocalRepresentationObject roLocalDerived = null;
    final StringBuilder sbExecDetails = new StringBuilder();

    try {

      roOriginal = rodaClient.getBrowserService().getRepresentationObject(roPID);

      roLocalDerived = downloadRepresentationToLocalDisk(roOriginal);
      roLocalDerived.setId(DateParser.getIsoDate(new Date()));
      roLocalDerived.setStatuses(new String[] {RepresentationObject.STATUS_NORMALIZED});

      sbExecDetails.append(
          String.format("<planExecutionDetails plan=\"%s\">%n", planFile.getName()));

      // Check if root file was changed
      if (outputs.containsKey(roLocalDerived.getRootFile().getId())) {
        final WorkflowExecutionOutput output = outputs.get(roLocalDerived.getRootFile().getId());
        updateFile(roLocalDerived.getRootFile(), output);
        sbExecDetails.append(
            getPlanExecutionDetailsForFile(roLocalDerived.getRootFile().getId(), output));
      }

      if (roLocalDerived.getPartFiles() != null) {
        for (RepresentationFile rFile : roLocalDerived.getPartFiles()) {
          if (outputs.containsKey(rFile.getId())) {
            final WorkflowExecutionOutput output = outputs.get(rFile.getId());
            updateFile(rFile, output);
            sbExecDetails.append(getPlanExecutionDetailsForFile(rFile.getId(), output));
          }
        }
      }

      sbExecDetails.append(String.format("</planExecutionDetails>%n"));

      roLocalDerived.setType(RepresentationObject.DIGITALIZED_WORK);
      final String subtype = RepresentationBuilder.getRepresentationSubtype(roLocalDerived);
      roLocalDerived.setSubType(subtype);

    } catch (RODAException e) {
      deleteTemporaryLocalRepresentation(roLocalDerived);
      logger.error(e.getMessage(), e);
      throw new ExecutePlanException(e.getMessage(), e);
    } catch (RemoteException e) {
      deleteTemporaryLocalRepresentation(roLocalDerived);
      logger.error(e.getMessage(), e);
      throw new ExecutePlanException(e.getMessage(), e);
    } catch (IOException e) {
      deleteTemporaryLocalRepresentation(roLocalDerived);
      logger.error(e.getMessage(), e);
      throw new ExecutePlanException(e.getMessage(), e);
    }

    String derivedROPID = null;
    try {

      derivedROPID = ingestRepresentation(roLocalDerived);
      reportItem.addAttribute(new Attribute("Derived representation PID", derivedROPID));

    } catch (IngestException e) {

      logger.error("Error ingesting new representation - " + e.getMessage(), e);
      throw new ExecutePlanException("Error ingesting new representation - " + e.getMessage(), e);

    } finally {

      deleteTemporaryLocalRepresentation(roLocalDerived);
    }

    try {

      final String epoPID =
          createPreservationEvent(
              roOriginal.getPid(), derivedROPID, sbExecDetails.toString(), reportItem);
      reportItem.addAttribute(new Attribute("Derivation event PID", epoPID));

    } catch (ExecutePlanException e) {
      logger.debug("Error registering convertion event - " + e.getMessage(), e);

      try {
        logger.warn("Error registering convertion event. Removing created object " + derivedROPID);

        this.rodaClient.getIngestService().removeObjects(new String[] {derivedROPID});

      } catch (RODAClientException e1) {
        logger.warn(
            "Error removing representation " + roPID + " - " + e1.getMessage() + ". IGNORING", e1);
      } catch (RemoteException e1) {
        logger.warn(
            "Error removing representation " + roPID + " - " + e1.getMessage() + ". IGNORING", e1);
      } catch (IngestException e1) {
        logger.warn(
            "Error removing representation " + roPID + " - " + e1.getMessage() + ". IGNORING", e1);
      }

      throw new ExecutePlanException(
          "Error registering convertion event - " + e.getMessage(), e, reportItem);
    }

    return derivedROPID;
  }
  /** @param args */
  public static void main(String[] args) {

    try {

      RODAClient rodaClient = null;

      if (args.length == 3) {

        // http://localhost:8180/
        String hostUrl = args[0];
        String username = args[1];
        String password = args[2];
        rodaClient = new RODAClient(new URL(hostUrl), username, password);

      } else {
        System.err.println(
            BrowserSimpleROTest.class.getSimpleName()
                + " protocol://hostname:port/core-service [username password]");
        System.exit(1);
      }

      Browser browserService = rodaClient.getBrowserService();

      System.out.println("\n**************************************");
      System.out.println("Number of Event Preservation Objects");
      System.out.println("**************************************");

      int count = browserService.getSimpleEventPreservationObjectCount(null);
      System.out.println(count + " event preservation objects in the repository");

      System.out.println("\n**************************************");
      System.out.println("Number of Event Preservation Objects (Inactive)");
      System.out.println("**************************************");

      Filter filterInactive = new Filter();
      filterInactive.add(new SimpleFilterParameter("state", RODAObject.STATE_INACTIVE));

      count = browserService.getSimpleEventPreservationObjectCount(filterInactive);
      System.out.println(count + " inactive event preservation objects in the repository");

      SimpleEventPreservationObject[] simpleEPOs =
          browserService.getSimpleEventPreservationObjects(null);

      System.out.println("\n**************************************");
      System.out.println("List of Event Preservation Objects");
      System.out.println("**************************************");

      for (int i = 0; simpleEPOs != null && i < simpleEPOs.length; i++) {
        System.out.println(simpleEPOs[i]);
      }

      if (simpleEPOs != null && simpleEPOs.length > 0) {

        System.out.println("\n*********************************************");
        System.out.println(
            "Getting EventPreservationObject of the first representation ("
                + simpleEPOs[0].getPid()
                + ")");
        System.out.println("*********************************************");

        EventPreservationObject rObject =
            browserService.getEventPreservationObject(simpleEPOs[0].getPid());
        System.out.println(rObject);
      }

    } catch (Throwable e) {
      e.printStackTrace();
      if (e.getCause() != null) {
        System.err.println("Cause exception:");
        e.getCause().printStackTrace();
      }
    }
  }