/**
  * Gets the roles for a package.
  *
  * @param packageId
  * @return A list of the roles in the package
  * @throws EslException
  */
 public List<Role> getRoles(PackageId packageId) throws EslException {
   String path =
       template.urlFor(UrlTemplate.ROLE_PATH).replace("{packageId}", packageId.getId()).build();
   String stringResponse;
   try {
     stringResponse = client.get(path);
   } catch (RequestException e) {
     throw new EslServerException(
         "Could not retrieve list of roles for package with id " + packageId.getId(), e);
   } catch (Exception e) {
     throw new EslException(
         "Could not retrieve list of roles for package with id " + packageId.getId(), e);
   }
   return Serialization.fromJson(stringResponse, RoleList.class).getResults();
 }
 /**
  * Downloads the documents (in a zip archive) from the package and returns a byte[].
  *
  * @param packageId Id of the DocumentPackage we want to download
  * @return The zipped documents in bytes
  * @throws EslException
  */
 public byte[] downloadZippedDocuments(PackageId packageId) throws EslException {
   String path =
       template.urlFor(UrlTemplate.ZIP_PATH).replace("{packageId}", packageId.getId()).build();
   try {
     return client.getBytes(path).getContents();
   } catch (RequestException e) {
     throw new EslServerException("Could not download the documents to a zip file.", e);
   } catch (Exception e) {
     throw new EslException("Could not download the documents to a zip file.", e);
   }
 }
  /**
   * Uploads the Document and file in byte[] to the package.
   *
   * @param packageId
   * @param fileName Name of the file
   * @param fileBytes The file to upload in bytes
   * @param document The document with approvals and fields
   * @throws EslException
   */
  public com.silanis.esl.sdk.Document uploadDocument(
      PackageId packageId,
      String fileName,
      byte[] fileBytes,
      com.silanis.esl.sdk.Document document,
      DocumentPackage documentPackage)
      throws EslException {
    Package apiPackage = new DocumentPackageConverter(documentPackage).toAPIPackage();
    Document apiDocument = new DocumentConverter(document).toAPIDocument(apiPackage);

    return uploadApiDocument(packageId.getId(), fileName, fileBytes, apiDocument);
  }
  /**
   * Create a new package based on an existing template.
   *
   * @param packageId
   * @param aPackage
   * @return PackageId
   */
  public PackageId createPackageFromTemplate(PackageId packageId, Package aPackage) {
    String path =
        template
            .urlFor(UrlTemplate.TEMPLATE_PATH)
            .replace("{packageId}", packageId.getId())
            .build();

    List<Role> roles = aPackage.getRoles();

    aPackage.setRoles(Collections.<Role>emptyList());

    String packageJson = Serialization.toJson(aPackage);
    PackageId newPackageId = null;
    try {

      String response = client.post(path, packageJson);

      newPackageId = Serialization.fromJson(response, PackageId.class);
    } catch (RequestException e) {
      throw new EslServerException("Could not create a new package", e);
    } catch (Exception e) {
      throw new EslException("Could not create a new package", e);
    }

    Package createdPackage = getApiPackage(newPackageId.getId());

    for (Role role : roles) {
      String roleUid = findRoleUidByName(createdPackage.getRoles(), role.getName());

      if (roleUid == null) {
        continue;
      }

      role.setId(roleUid);
      updateRole(newPackageId, role);
    }

    return newPackageId;
  }
 /**
  * Downloads the evidence summary (in PDF) from the package and returns a byte[].
  *
  * @param packageId
  * @return The evidence summary in bytes
  * @throws EslException
  */
 public byte[] downloadEvidenceSummary(PackageId packageId) throws EslException {
   String path =
       template
           .urlFor(UrlTemplate.EVIDENCE_SUMMARY_PATH)
           .replace("{packageId}", packageId.getId())
           .build();
   try {
     return client.getBytes(path).getContents();
   } catch (RequestException e) {
     throw new EslServerException("Could not download the evidence summary.", e);
   } catch (Exception e) {
     throw new EslException("Could not download the evidence summary.", e);
   }
 }
 /**
  * Deletes the document from the package.
  *
  * @param packageId
  * @param documentId
  * @throws EslException
  */
 public void deleteDocument(PackageId packageId, String documentId) throws EslException {
   String path =
       template
           .urlFor(UrlTemplate.DOCUMENT_ID_PATH)
           .replace("{packageId}", packageId.getId())
           .replace("{documentId}", documentId)
           .build();
   try {
     client.delete(path);
   } catch (RequestException e) {
     throw new EslServerException("Could not delete document from package.", e);
   } catch (Exception e) {
     throw new EslException("Could not delete document from package.", e);
   }
 }
  /**
   * Adds a role to the package.
   *
   * @param packageId
   * @param role
   * @return The role added
   * @throws EslException
   */
  public Role addRole(PackageId packageId, Role role) throws EslException {
    String path =
        template.urlFor(UrlTemplate.ROLE_PATH).replace("{packageId}", packageId.getId()).build();

    String roleJson = JacksonUtil.serializeDirty(role);
    String stringResponse;
    try {
      stringResponse = client.post(path, roleJson);
    } catch (RequestException e) {
      throw new EslServerException("Could not add role.", e);
    } catch (Exception e) {
      throw new EslException("Could not add role.", e);
    }
    return Serialization.fromJson(stringResponse, Role.class);
  }
  /**
   * Change the package's status to DRAFT.
   *
   * @param packageId
   * @throws EslException
   */
  public void changePackageStatusToDraft(PackageId packageId) throws EslException {
    String path =
        template
            .urlFor(UrlTemplate.PACKAGE_ID_PATH)
            .replace("{packageId}", packageId.getId())
            .build();

    try {
      client.put(path, "{\"status\":\"DRAFT\"}");
    } catch (RequestException e) {
      throw new EslServerException("Could not change the package status to DRAFT.", e);
    } catch (Exception e) {
      throw new EslException("Could not change the package status to DRAFT.", e);
    }
  }
 /**
  * Deletes a role from the package.
  *
  * @param packageId
  * @param role
  * @throws EslException
  */
 public void deleteRole(PackageId packageId, Role role) throws EslException {
   String path =
       template
           .urlFor(UrlTemplate.ROLE_ID_PATH)
           .replace("{packageId}", packageId.getId())
           .replace("{roleId}", role.getId())
           .build();
   try {
     client.delete(path);
   } catch (RequestException e) {
     throw new EslServerException("Could not delete role", e);
   } catch (Exception e) {
     throw new EslException("Could not delete role", e);
   }
 }
 public byte[] downloadDocument(PackageId packageId, String documentId) throws EslException {
   String path =
       template
           .urlFor(UrlTemplate.PDF_PATH)
           .replace("{packageId}", packageId.getId())
           .replace("{documentId}", documentId)
           .build();
   try {
     return client.getBytesAsOctetStream(path).getContents();
   } catch (RequestException e) {
     throw new EslServerException("Could not download the pdf document.", e);
   } catch (Exception e) {
     throw new EslException("Could not download the pdf document.", e);
   }
 }
 /**
  * Unlock a signer which has been locked out due to too many failed authentication attempts.
  *
  * @param signerId If not null, the id of the signer who's status we are to retrieve
  */
 public void unlockSigner(PackageId packageId, String signerId) {
   String path =
       template
           .urlFor(UrlTemplate.ROLE_UNLOCK_PATH)
           .replace("{packageId}", packageId.getId())
           .replace("{roleId}", signerId)
           .build();
   try {
     client.post(path, null);
   } catch (RequestException e) {
     throw new EslException("Could not unlock signer.", e);
   } catch (Exception e) {
     throw new EslException("Could not unlock signer." + " Exception: " + e.getMessage());
   }
 }
 /**
  * Sends the package.
  *
  * @param packageId
  * @throws EslException
  */
 public void sendPackage(PackageId packageId) throws EslException {
   String path =
       template
           .urlFor(UrlTemplate.PACKAGE_ID_PATH)
           .replace("{packageId}", packageId.getId())
           .build();
   String json = "{\"status\":\"SENT\"}";
   try {
     client.post(path, json);
   } catch (RequestException e) {
     throw new EslServerException("Could not send the package.", e);
   } catch (Exception e) {
     throw new EslException("Could not send the package.", e);
   }
 }
 /**
  * Removes a signer from a package
  *
  * @param packageId The id of the package containing the signer to be deleted
  * @param signerId The role id of the signer to be deleted
  */
 public void removeSigner(PackageId packageId, String signerId) {
   String path =
       template
           .urlFor(UrlTemplate.SIGNER_PATH)
           .replace("{packageId}", packageId.getId())
           .replace("{roleId}", signerId)
           .build();
   try {
     client.delete(path);
     return;
   } catch (RequestException e) {
     throw new EslServerException("Could not delete signer.", e);
   } catch (Exception e) {
     throw new EslException("Could not delete signer." + " Exception: " + e.getMessage());
   }
 }
  private void sendSmsToSigner(PackageId packageId, Role role) {
    String path =
        template
            .urlFor(UrlTemplate.SEND_SMS_TO_SIGNER_PATH)
            .replace("{packageId}", packageId.getId())
            .replace("{roleId}", role.getId())
            .build();

    try {
      client.post(path, null);
    } catch (RequestException e) {
      throw new EslException("Could not send SMS to the signer.", e);
    } catch (Exception e) {
      throw new EslException("Could not send SMS to the signer." + " Exception: " + e.getMessage());
    }
  }
  /**
   * Deletes the specified package.
   *
   * @param packageId The id of the package to be deleted
   */
  public void deletePackage(PackageId packageId) {
    String path =
        template
            .urlFor(UrlTemplate.PACKAGE_ID_PATH)
            .replace("{packageId}", packageId.getId())
            .build();

    try {
      client.delete(path);
    } catch (RequestException e) {
      throw new EslServerException("Unable to delete package.", e);
    } catch (Exception e) {
      e.printStackTrace();
      throw new EslException("Unable to delete package. Exception: " + e.getMessage());
    }
  }
  private void notifySigner(PackageId packageId, String roleId) {
    String path =
        template
            .urlFor(UrlTemplate.NOTIFY_ROLE_PATH)
            .replace("{packageId}", packageId.getId())
            .replace("{roleId}", roleId)
            .build();

    try {
      client.post(path, null);
    } catch (RequestException e) {
      throw new EslServerException("Could not send email notification.", e);
    } catch (Exception e) {
      throw new EslException("Could not send email notification.  " + e.getMessage());
    }
  }
  /**
   * Gets the field summary for the package and returns a list of field summaries.
   *
   * @param packageId
   * @return A list of field summaries
   * @throws com.silanis.esl.sdk.EslException
   */
  public List<FieldSummary> getFieldSummary(PackageId packageId) throws EslException {
    String path =
        template
            .urlFor(UrlTemplate.FIELD_SUMMARY_PATH)
            .replace("{packageId}", packageId.getId())
            .build();

    List<FieldSummary> fieldSummary;
    try {
      String stringResponse = client.get(path);

      fieldSummary = Serialization.fromJsonToList(stringResponse, FieldSummary.class);
    } catch (Exception e) {
      throw new EslException("Could not get the field summary.", e);
    }
    return fieldSummary;
  }
  /**
   * Archive the specified package.
   *
   * @param packageId The id of the package to be archived
   */
  public void archive(PackageId packageId) {
    String path =
        template
            .urlFor(UrlTemplate.PACKAGE_ID_PATH)
            .replace("{packageId}", packageId.getId())
            .build();

    String json = "{\"status\":\"ARCHIVED\"}";
    try {
      client.post(path, json);
    } catch (RequestException e) {
      throw new EslServerException("Unable to archive the package.", e);
    } catch (Exception e) {
      e.printStackTrace();
      throw new EslException("Unable to archive the package. Exception: " + e.getMessage());
    }
  }
  private String getFastTrackUrl(PackageId packageId, Boolean signing) {
    String path =
        template
            .urlFor(UrlTemplate.FAST_TRACK_URL_PATH)
            .replace("{packageId}", packageId.getId())
            .replace("{signing}", signing.toString())
            .build();

    try {
      String json = client.get(path);
      SigningUrl signingUrl = Serialization.fromJson(json, SigningUrl.class);
      return signingUrl.getUrl();
    } catch (RequestException e) {
      throw new EslException("Could not get a fastTrack url.", e);
    } catch (Exception e) {
      throw new EslException("Could not get a fastTrack url." + " Exception: " + e.getMessage());
    }
  }
  /**
   * Get thank you dialog content.
   *
   * @param packageId The id of the package to get thank you dialog content.
   * @return thank you dialog content
   */
  public String getThankYouDialogContent(PackageId packageId) {
    String path =
        template
            .urlFor(UrlTemplate.THANK_YOU_DIALOG_PATH)
            .replace("{packageId}", packageId.getId())
            .build();

    try {
      String json = client.get(path);
      Properties thankYouDialogContent = Serialization.fromJson(json, Properties.class);
      return thankYouDialogContent.getProperty("body");
    } catch (RequestException e) {
      throw new EslException("Could not get thank you dialog content.", e);
    } catch (Exception e) {
      throw new EslException(
          "Could not get thank you dialog content." + " Exception: " + e.getMessage());
    }
  }
  /**
   * Get a signer from the specified package
   *
   * @param packageId The id of the package in which to get the signer
   * @param signerId The id of signer to get
   * @return The signer
   */
  public com.silanis.esl.sdk.Signer getSigner(PackageId packageId, String signerId) {
    String path =
        template
            .urlFor(UrlTemplate.SIGNER_PATH)
            .replace("{packageId}", packageId.getId())
            .replace("{roleId}", signerId)
            .build();

    try {
      String response = client.get(path);
      Role apiRole = Serialization.fromJson(response, Role.class);
      return new SignerConverter(apiRole).toSDKSigner();

    } catch (RequestException e) {
      throw new EslServerException("Could not get signer.", e);
    } catch (Exception e) {
      throw new EslException("Could not get signer." + " Exception: " + e.getMessage());
    }
  }
  private String getSigningUrl(PackageId packageId, Role role) {

    String path =
        template
            .urlFor(UrlTemplate.SIGNER_URL_PATH)
            .replace("{packageId}", packageId.getId())
            .replace("{roleId}", role.getId())
            .build();

    try {
      String response = client.get(path);
      SigningUrl signingUrl = Serialization.fromJson(response, SigningUrl.class);
      return signingUrl.getUrl();
    } catch (RequestException e) {
      throw new EslException("Could not get a signing url.", e);
    } catch (Exception e) {
      throw new EslException("Could not get a signing url." + " Exception: " + e.getMessage());
    }
  }
  /**
   * Get package support configuration.
   *
   * @param packageId The id of the package to get package support configuration.
   * @return package support configuration
   */
  public SupportConfiguration getConfig(PackageId packageId) {
    String path =
        template
            .urlFor(UrlTemplate.PACKAGE_INFORMATION_CONFIG_PATH)
            .replace("{packageId}", packageId.getId())
            .build();

    try {
      String json = client.get(path);
      com.silanis.esl.api.model.SupportConfiguration apiSupportConfiguration =
          Serialization.fromJson(json, com.silanis.esl.api.model.SupportConfiguration.class);
      return new SupportConfigurationConverter(apiSupportConfiguration).toSDKSupportConfiguration();
    } catch (RequestException e) {
      throw new EslException("Could not get support configuration.", e);
    } catch (Exception e) {
      throw new EslException(
          "Could not get support configuration." + " Exception: " + e.getMessage());
    }
  }
  /**
   * Updates a signer's information from a package
   *
   * @param packageId The id of the package containing the signer to be updated
   * @param signer The signer with the updated information
   */
  public void updateSigner(PackageId packageId, com.silanis.esl.sdk.Signer signer) {
    Role apiPayload =
        new SignerConverter(signer).toAPIRole(UUID.randomUUID().toString().replace("-", ""));

    String path =
        template
            .urlFor(UrlTemplate.SIGNER_PATH)
            .replace("{packageId}", packageId.getId())
            .replace("{roleId}", signer.getId())
            .build();

    try {
      String json = Serialization.toJson(apiPayload);
      client.put(path, json);
    } catch (RequestException e) {
      throw new EslException("Could not update signer.", e);
    } catch (Exception e) {
      throw new EslException("Could not update signer." + " Exception: " + e.getMessage());
    }
  }
  /**
   * Notifies the specified signer by email, including a custom message.
   *
   * @param packageId The id of the package containing the signer to be notified
   * @param signerEmail The email of the signer to be notified
   * @param message The custom message to be included in the email sent as notification to the
   *     signer
   */
  public void notifySigner(PackageId packageId, String signerEmail, String message) {
    String path =
        template
            .urlFor(UrlTemplate.CUSTOM_NOTIFICATIONS_PATH)
            .replace("{packageId}", packageId.getId())
            .build();

    Map<String, Object> jsonMap = new HashMap<String, Object>();
    jsonMap.put("email", signerEmail);
    jsonMap.put("message", message);

    try {
      String payload = JacksonUtil.serialize(jsonMap);
      client.post(path, payload);
    } catch (RequestException e) {
      throw new EslException("Could not send email notification to signer.", e);
    } catch (Exception e) {
      throw new EslException(
          "Could not send email notification to signer. Exception: " + e.getMessage());
    }
  }
  /**
   * Adds a signer to the specified package
   *
   * @param packageId The id of the package in which the signer will be added
   * @param signer The signer to be added
   * @return The role id of the signer
   */
  public String addSigner(PackageId packageId, com.silanis.esl.sdk.Signer signer) {
    Role apiPayload =
        new SignerConverter(signer).toAPIRole(UUID.randomUUID().toString().replace("-", ""));

    String path =
        template
            .urlFor(UrlTemplate.ADD_SIGNER_PATH)
            .replace("{packageId}", packageId.getId())
            .build();

    try {
      String json = Serialization.toJson(apiPayload);
      String response = client.post(path, json);
      Role apiRole = Serialization.fromJson(response, Role.class);
      return apiRole.getId();

    } catch (RequestException e) {
      throw new EslServerException("Could not add signer.", e);
    } catch (Exception e) {
      throw new EslException("Could not add signer." + " Exception: " + e.getMessage());
    }
  }
  /**
   * Retrieves the current signing status of the DocumentPackage, Document or Signer specified.
   *
   * @param packageId Id of the DocumentPackage who's status we are to retrieve
   * @param signerId If not null, the id of the signer who's status we are to retrieve
   * @param documentId If not null, the id of the document who's status we are to retrieve
   * @return One of the following values: INACTIVE - process is not active COMPLETE - process has
   *     been completed ARCHIVED - process has been archived SIGNING-PENDING - process is active,
   *     but not all signatures have been added SIGNING-COMPLETE - process is active, all signaures
   *     have been added
   */
  public SigningStatus getSigningStatus(
      PackageId packageId, SignerId signerId, DocumentId documentId) {
    String path =
        template
            .urlFor(UrlTemplate.SIGNING_STATUS_PATH)
            .replace("{packageId}", packageId.getId())
            .replace("{signerId}", signerId != null ? signerId.getId() : "")
            .replace("{documentId}", documentId != null ? documentId.getId() : "")
            .build();

    try {
      String stringResponse = client.get(path);
      ObjectMapper objectMapper = new ObjectMapper();
      JsonNode topNode = objectMapper.readTree(stringResponse);
      String statusString = topNode.get("status").textValue();
      return SigningStatus.statusForToken(statusString);
    } catch (RequestException e) {
      throw new EslServerException("Could not retrieve signing status.", e);
    } catch (Exception e) {
      throw new EslException("Could not retrieve signing status.", e);
    }
  }
  /**
   * Updates the package's fields and roles.
   *
   * @param packageId
   * @param sdkPackage
   * @throws EslException
   */
  public void updatePackage(PackageId packageId, DocumentPackage sdkPackage) throws EslException {
    String path =
        template
            .urlFor(UrlTemplate.PACKAGE_ID_PATH)
            .replace("{packageId}", packageId.getId())
            .build();

    Package aPackage = new DocumentPackageConverter(sdkPackage).toAPIPackage();

    String packageJson = Serialization.toJson(aPackage);
    try {
      client.put(path, packageJson);
    } catch (RequestException e) {
      throw new EslServerException("Could not update the package.", e);
    } catch (Exception e) {
      throw new EslException("Could not update the package.", e);
    }
    // Update roles
    List<Role> roleList = aPackage.getRoles();
    for (Role role : roleList) {
      updateRole(packageId, role);
    }
  }
  /**
   * Get a signing url
   *
   * @param packageId The id of the package in which to get the signing url
   * @param signerId The id of signer to get the signing url
   * @return The signing url
   */
  public String getSigningUrl(PackageId packageId, String signerId) {
    Package aPackage = getApiPackage(packageId.getId());

    return getSigningUrl(packageId, getRole(aPackage, signerId));
  }
  public DocumentPackage getPackage(PackageId packageId) {
    Package aPackage = getApiPackage(packageId.getId());

    return packageConverter(aPackage).toSDKPackage();
  }