/* (non-Javadoc)
   * @see com.evolveum.midpoint.model.sync.Action#handle(com.evolveum.midpoint.model.lens.LensContext, com.evolveum.midpoint.model.sync.SynchronizationSituation, java.util.Map, com.evolveum.midpoint.task.api.Task, com.evolveum.midpoint.schema.result.OperationResult)
   */
  @Override
  public <F extends FocusType> void handle(
      LensContext<F> context,
      SynchronizationSituation<F> situation,
      Map<QName, Object> parameters,
      Task task,
      OperationResult parentResult) {
    ActivationStatusType desiredStatus = ActivationStatusType.DISABLED;

    LensProjectionContext projectionContext = context.getProjectionContextsIterator().next();
    PrismObject<ShadowType> objectCurrent = projectionContext.getObjectCurrent();
    if (objectCurrent != null) {
      PrismProperty<Object> administrativeStatusProp =
          objectCurrent.findProperty(SchemaConstants.PATH_ACTIVATION_ADMINISTRATIVE_STATUS);
      if (administrativeStatusProp != null) {
        if (desiredStatus.equals(administrativeStatusProp.getRealValue())) {
          // Desired status already set, nothing to do
          return;
        }
      }
    }
    ObjectDelta<ShadowType> activationDelta =
        ObjectDelta.createModificationReplaceProperty(
            ShadowType.class,
            projectionContext.getOid(),
            SchemaConstants.PATH_ACTIVATION_ADMINISTRATIVE_STATUS,
            getPrismContext(),
            desiredStatus);
    projectionContext.setPrimaryDelta(activationDelta);
  }
  <F extends ObjectType> void processPasswordPolicy(
      LensProjectionContext projectionContext,
      LensContext<F> context,
      Task task,
      OperationResult result)
      throws SchemaException, PolicyViolationException {

    ObjectDelta accountDelta = projectionContext.getDelta();

    if (accountDelta == null) {
      LOGGER.trace("Skipping processing password policies. Shadow delta not specified.");
      return;
    }

    if (ChangeType.DELETE == accountDelta.getChangeType()) {
      return;
    }

    PrismObject<ShadowType> accountShadow = null;
    PrismProperty<PasswordType> password = null;
    if (ChangeType.ADD == accountDelta.getChangeType()) {
      accountShadow = accountDelta.getObjectToAdd();
      if (accountShadow != null) {
        password = accountShadow.findProperty(SchemaConstants.PATH_PASSWORD_VALUE);
      }
    }
    if (ChangeType.MODIFY == accountDelta.getChangeType() || password == null) {
      PropertyDelta<PasswordType> passwordValueDelta = null;
      if (accountDelta != null) {
        passwordValueDelta = accountDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE);
        // Modification sanity check
        if (accountDelta.getChangeType() == ChangeType.MODIFY
            && passwordValueDelta != null
            && (passwordValueDelta.isAdd() || passwordValueDelta.isDelete())) {
          throw new SchemaException(
              "Shadow password value cannot be added or deleted, it can only be replaced");
        }
        if (passwordValueDelta == null) {
          LOGGER.trace(
              "Skipping processing password policies. Shadow delta does not contain password change.");
          return;
        }
        password = (PrismProperty<PasswordType>) passwordValueDelta.getItemNewMatchingPath(null);
      }
    }

    //		PrismProperty<PasswordType> password = getPassword(projectionContext);
    ValuePolicyType passwordPolicy = null;
    if (isCheckOrgPolicy(context)) {
      passwordPolicy = determineValuePolicy(context.getFocusContext().getObjectAny(), task, result);
      context.getFocusContext().setOrgPasswordPolicy(passwordPolicy);
    } else {
      passwordPolicy = projectionContext.getEffectivePasswordPolicy();
    }

    processPasswordPolicy(passwordPolicy, password, result);
  }
  @Override
  public TaskRunResult run(Task task) {

    OperationResult result = task.getResult().createSubresult(DOT_CLASS + "run");
    TaskRunResult runResult = new TaskRunResult();

    LensContextType contextType = task.getModelOperationContext();
    if (contextType == null) {
      LOGGER.trace("No model context found, skipping the model operation execution.");
      if (result.isUnknown()) {
        result.computeStatus();
      }
      runResult.setRunResultStatus(TaskRunResult.TaskRunResultStatus.FINISHED);
    } else {
      LensContext context = null;
      try {
        context =
            LensContext.fromLensContextType(contextType, prismContext, provisioningService, result);
      } catch (SchemaException e) {
        throw new SystemException(
            "Cannot recover model context from task " + task + " due to schema exception", e);
      } catch (ObjectNotFoundException e) {
        throw new SystemException("Cannot recover model context from task " + task, e);
      } catch (CommunicationException e) {
        throw new SystemException(
            "Cannot recover model context from task " + task, e); // todo wait and retry
      } catch (ConfigurationException e) {
        throw new SystemException("Cannot recover model context from task " + task, e);
      }

      if (LOGGER.isTraceEnabled()) {
        LOGGER.trace("Context to be executed = {}", context.debugDump());
      }

      try {
        // here we brutally remove all the projection contexts -- because if we are continuing after
        // rejection of a role/resource assignment
        // that resulted in such projection contexts, we DO NOT want them to appear in the context
        // any more
        context.rot();
        Iterator<LensProjectionContext> projectionIterator =
            context.getProjectionContextsIterator();
        while (projectionIterator.hasNext()) {
          LensProjectionContext projectionContext = projectionIterator.next();
          if (projectionContext.getPrimaryDelta() != null
              && !projectionContext.getPrimaryDelta().isEmpty()) {
            continue; // don't remove client requested actions!
          }
          if (LOGGER.isTraceEnabled()) {
            LOGGER.trace(
                "Removing projection context {}", projectionContext.getHumanReadableName());
          }
          projectionIterator.remove();
        }
        if (task.getChannel() == null) {
          task.setChannel(context.getChannel());
        }
        clockwork.run(context, task, result);

        task.setModelOperationContext(context.toLensContextType());
        task.savePendingModifications(result);

        if (result.isUnknown()) {
          result.computeStatus();
        }
        runResult.setRunResultStatus(TaskRunResult.TaskRunResultStatus.FINISHED);
      } catch (Exception e) { // too many various exceptions; will be fixed with java7 :)
        String message = "An exception occurred within model operation, in task " + task;
        LoggingUtils.logException(LOGGER, message, e);
        result.recordPartialError(message, e);
        // TODO: here we do not know whether the error is temporary or permanent (in the future we
        // could discriminate on the basis of particular exception caught)
        runResult.setRunResultStatus(TaskRunResult.TaskRunResultStatus.TEMPORARY_ERROR);
      }
    }

    task.getResult().recomputeStatus();
    runResult.setOperationResult(task.getResult());
    return runResult;
  }