/*
   * @see org.societies.api.internal.privacytrust.trust.ITrustBroker#unregisterTrustUpdateListener(org.societies.api.privacytrust.trust.event.ITrustUpdateEventListener, org.societies.api.privacytrust.trust.TrustQuery)
   */
  @Override
  public void unregisterTrustUpdateListener(
      final ITrustUpdateEventListener listener, final TrustQuery query) throws TrustException {

    if (query == null) throw new NullPointerException("query can't be null");

    if (query.getTrusteeType() != null) {
      this.doUnregisterTrustUpdateListenerByType(
          null, listener, query.getTrustorId(), query.getTrusteeType(), query.getTrustValueType());
    } else {
      this.doUnregisterTrustUpdateListener(
          null, listener, query.getTrustorId(), query.getTrusteeId(), query.getTrustValueType());
    }
  }
  /*
   * @see org.societies.api.privacytrust.trust.ITrustBroker#retrieveTrustValue(org.societies.api.identity.Requestor, org.societies.api.privacytrust.trust.TrustQuery)
   */
  @Override
  @Async
  public Future<Double> retrieveTrustValue(final Requestor requestor, final TrustQuery query)
      throws TrustException {

    if (requestor == null) {
      throw new NullPointerException("requestor can't be null");
    }
    if (query == null) {
      throw new NullPointerException("query can't be null");
    }
    if (query.getTrusteeId() == null) {
      throw new IllegalArgumentException("trusteeId in query can't be null");
    }
    if (query.getTrustValueType() == null) {
      throw new IllegalArgumentException("trustValueType in query can't be null");
    }

    LOG.debug(
        "Retrieving trust value matching query '{}'" + " on behalf of requestor '{}'",
        query,
        requestor);

    if (this.isLocalQuery(query)) {
      // L O C A L
      LOG.debug("query '{}' is LOCAL", query);
      return new AsyncResult<Double>(this.retrieveLocalTrustValue(requestor, query));
    } else {
      // R E M O T E
      LOG.debug("query '{}' is REMOTE", query);
      return new AsyncResult<Double>(this.retrieveRemoteTrustValue(requestor, query));
    }
  }
  private Set<ITrustedEntity> retrieveTrustedEntities(final TrustQuery query)
      throws TrustException {

    final Set<ITrustedEntity> result = new LinkedHashSet<ITrustedEntity>();

    try {
      if (this.trustRepo == null) {
        throw new TrustBrokerException("ITrustRepository service is not available");
      }
      if (query.getTrusteeId() != null) {
        final ITrustedEntity trustedEntity =
            this.trustRepo.retrieveEntity(query.getTrustorId(), query.getTrusteeId());
        if (trustedEntity != null) result.add(trustedEntity);
      } else {
        result.addAll(
            this.trustRepo.retrieveEntities(
                query.getTrustorId(), query.getTrusteeType(), query.getTrustValueType()));
      }

    } catch (ServiceUnavailableException sue) {
      throw new TrustBrokerException(sue.getLocalizedMessage(), sue);
    }

    return result;
  }
  private static Set<ExtTrustRelationship> entitiesToExtRelationships(
      final Set<ITrustedEntity> entities, final TrustQuery query) {

    final Set<ExtTrustRelationship> result =
        new LinkedHashSet<ExtTrustRelationship>(entities.size());

    final TrustValueType trustValueType = query.getTrustValueType();
    for (final ITrustedEntity entity : entities) {

      final Set<TrustEvidence> evidenceSet = iEvidenceToEvidence(entity.getEvidence());
      // TODO Needs optimisation
      final Set<TrustEvidence> directEvidenceSet = new LinkedHashSet<TrustEvidence>();
      final Set<TrustEvidence> indirectEvidenceSet = new LinkedHashSet<TrustEvidence>();
      for (final TrustEvidence evidence : evidenceSet) {
        if (evidence.getSourceId() == null) {
          directEvidenceSet.add(evidence);
        } else {
          indirectEvidenceSet.add(evidence);
        }
      }

      if (entity.getDirectTrust().getValue() != null
          && (null == trustValueType || TrustValueType.DIRECT == trustValueType)) {
        result.add(
            new ExtTrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.DIRECT,
                entity.getDirectTrust().getValue(),
                entity.getDirectTrust().getLastUpdated(),
                directEvidenceSet));
      }
      if (entity.getIndirectTrust().getValue() != null
          && (null == trustValueType || TrustValueType.INDIRECT == trustValueType)) {
        result.add(
            new ExtTrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.INDIRECT,
                entity.getIndirectTrust().getValue(),
                entity.getIndirectTrust().getLastUpdated(),
                indirectEvidenceSet));
      }
      if (entity.getUserPerceivedTrust().getValue() != null
          && (null == trustValueType || TrustValueType.USER_PERCEIVED == trustValueType)) {
        result.add(
            new ExtTrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.USER_PERCEIVED,
                entity.getUserPerceivedTrust().getValue(),
                entity.getUserPerceivedTrust().getLastUpdated(),
                new HashSet<TrustEvidence>()));
      }
    }

    return result;
  }
  private Double retrieveLocalTrustValue(final Requestor requestor, final TrustQuery query)
      throws TrustException {

    final Set<ITrustedEntity> entities = this.retrieveTrustedEntities(query);

    if (entities.isEmpty()) {
      return null;
    } else if (entities.size() == 1) {
      final ITrustedEntity entity = entities.iterator().next();
      if (TrustValueType.DIRECT == query.getTrustValueType()) {
        return entity.getDirectTrust().getValue();
      } else if (TrustValueType.INDIRECT == query.getTrustValueType()) {
        return entity.getIndirectTrust().getValue();
      } else { // if (TrustValueType.USER_PERCEIVED == query.getTrustValueType())
        return entity.getUserPerceivedTrust().getValue();
      }
    } else {
      throw new NonUniqueTrustQueryResultException(
          "Query returned " + entities.size() + " results");
    }
  }
  private boolean removeLocalTrustRelationships(final TrustQuery query) throws TrustException {

    try {
      if (this.trustRepo == null) {
        throw new TrustBrokerException("ITrustRepository service is not available");
      }
      if (query.getTrusteeId() != null) {
        return this.trustRepo.removeEntity(query.getTrustorId(), query.getTrusteeId());
      } else {
        return this.trustRepo.removeEntities(
            query.getTrustorId(), query.getTrusteeType(), query.getTrustValueType());
      }
    } catch (ServiceUnavailableException sue) {
      throw new TrustBrokerException(sue.getLocalizedMessage(), sue);
    }
  }
  private static Set<TrustRelationship> entitiesToRelationships(
      final Set<ITrustedEntity> entities, final TrustQuery query) {

    final Set<TrustRelationship> result = new LinkedHashSet<TrustRelationship>();

    final TrustValueType trustValueType = query.getTrustValueType();
    for (final ITrustedEntity entity : entities) {

      if (entity.getDirectTrust().getValue() != null
          && (null == trustValueType || TrustValueType.DIRECT == trustValueType)) {
        result.add(
            new TrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.DIRECT,
                entity.getDirectTrust().getValue(),
                entity.getDirectTrust().getLastUpdated()));
      }
      if (entity.getIndirectTrust().getValue() != null
          && (null == trustValueType || TrustValueType.INDIRECT == trustValueType)) {
        result.add(
            new TrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.INDIRECT,
                entity.getIndirectTrust().getValue(),
                entity.getIndirectTrust().getLastUpdated()));
      }
      if (entity.getUserPerceivedTrust().getValue() != null
          && (null == trustValueType || TrustValueType.USER_PERCEIVED == trustValueType)) {
        result.add(
            new TrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.USER_PERCEIVED,
                entity.getUserPerceivedTrust().getValue(),
                entity.getUserPerceivedTrust().getLastUpdated()));
      }
    }

    return result;
  }