/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.camunda.bpm.engine.impl.jobexecutor;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.impl.bpmn.parser.BpmnParse;
import org.camunda.bpm.engine.impl.context.Context;
import org.camunda.bpm.engine.impl.context.ProcessApplicationContextUtil;
import org.camunda.bpm.engine.impl.core.variable.mapping.value.ParameterValueProvider;
import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity;
import org.camunda.bpm.engine.impl.persistence.entity.JobDefinitionEntity;
import org.camunda.bpm.engine.impl.pvm.process.ProcessDefinitionImpl;


/**
 * @author Thorben Lindhauer
 *
 */
public class DefaultJobPriorityProvider implements JobPriorityProvider {

  private static final Logger LOG = Logger.getLogger(DefaultJobPriorityProvider.class.getName());

  public static int DEFAULT_PRIORITY = 0;

  public static int DEFAULT_PRIORITY_ON_RESOLUTION_FAILURE = 0;

  @Override
  public int determinePriority(ExecutionEntity execution, JobDeclaration<?, ?> jobDeclaration) {

    Integer jobDefinitionPriority = getJobDefinitionPriority(execution, jobDeclaration);
    if (jobDefinitionPriority != null) {
      return jobDefinitionPriority;
    }

    Integer activityPriority = getActivityPriority(execution, jobDeclaration);
    if (activityPriority != null) {
      return activityPriority;
    }

    Integer processDefinitionPriority = getProcessDefinitionPriority(execution, jobDeclaration);
    if (processDefinitionPriority != null) {
      return processDefinitionPriority;
    }

    return DEFAULT_PRIORITY;
  }

  protected Integer getJobDefinitionPriority(ExecutionEntity execution, JobDeclaration<?, ?> jobDeclaration) {
    JobDefinitionEntity jobDefinition = getJobDefinitionFor(jobDeclaration);

    if (jobDefinition != null) {
      return jobDefinition.getOverridingJobPriority();
    }

    return null;
  }

  protected Integer getProcessDefinitionPriority(ExecutionEntity execution, JobDeclaration<?, ?> jobDeclaration) {
    ProcessDefinitionImpl processDefinition = jobDeclaration.getProcessDefinition();

    if (processDefinition != null) {
      ParameterValueProvider priorityProvider = (ParameterValueProvider) processDefinition.getProperty(BpmnParse.PROPERTYNAME_JOB_PRIORITY);

      if (priorityProvider != null) {
        return evaluateValueProvider(priorityProvider, execution, jobDeclaration);
      }
    }

    return null;
  }

  protected JobDefinitionEntity getJobDefinitionFor(JobDeclaration<?, ?> jobDeclaration) {
    if (jobDeclaration.getJobDefinitionId() != null) {
      return Context.getCommandContext()
          .getJobDefinitionManager()
          .findById(jobDeclaration.getJobDefinitionId());
    }
    else {
      return null;
    }
  }

  protected Integer getActivityPriority(ExecutionEntity execution, JobDeclaration<?, ?> jobDeclaration) {
    if (jobDeclaration != null) {
      ParameterValueProvider priorityProvider = jobDeclaration.getJobPriorityProvider();
      if (priorityProvider != null) {
        return evaluateValueProvider(priorityProvider, execution, jobDeclaration);
      }
    }

    return null;
  }

  protected Integer evaluateValueProvider(ParameterValueProvider valueProvider, ExecutionEntity execution, JobDeclaration<?, ?> jobDeclaration) {
    Object value = null;
    try {
      value = valueProvider.getValue(execution);

    } catch (ProcessEngineException e) {

      if (Context.getProcessEngineConfiguration().isEnableGracefulDegradationOnContextSwitchFailure()
          && isSymptomOfContextSwitchFailure(e, execution)) {

        LOG.log(Level.WARNING, "Could not determine priority for job created in context of execution " + execution
            + ". Using default priority " + DEFAULT_PRIORITY_ON_RESOLUTION_FAILURE, e);
        value = DefaultJobPriorityProvider.DEFAULT_PRIORITY_ON_RESOLUTION_FAILURE;
      }
      else {
        throw e;
      }
    }

    if (!(value instanceof Number)) {
      throw new ProcessEngineException(describeContext(jobDeclaration, execution)
          + ": Priority value is not an Integer");
    }
    else {
      Number numberValue = (Number) value;
      if (isValidIntegerValue(numberValue)) {
        return numberValue.intValue();
      }
      else {
        throw new ProcessEngineException(describeContext(jobDeclaration, execution)
            + ": Priority value must be either Short, Integer, or Long in Integer range");
      }
    }
  }

  protected boolean isSymptomOfContextSwitchFailure(Throwable t, ExecutionEntity contextExecution) {
    // a context switch failure can occur, if the current engine has no PA registration for the deployment
    // subclasses may assert the actual throwable to narrow down the diagnose
    return ProcessApplicationContextUtil.getTargetProcessApplication(contextExecution) == null;
  }

  protected String describeContext(JobDeclaration<?, ?> jobDeclaration, ExecutionEntity executionEntity) {
    return "Job " + jobDeclaration.getActivityId()
            + "/" + jobDeclaration.getJobHandlerType() + " instantiated "
            + "in context of " + executionEntity;
  }

  protected boolean isValidIntegerValue(Number value) {
    return
      value instanceof Short ||
      value instanceof Integer ||
      (value instanceof Long && ((long) value.intValue()) == value.longValue());
  }

}

@@ -1533,16 +1533,211 @@
 E = 0;%0A%0A
+  public static int getDefaultPriority() %7B%0A    return DEFAULT_PRIORITY;%0A  %7D%0A%0A  public static int getDefaultPriorityOnResolutionFailure() %7B%0A    return DEFAULT_PRIORITY_ON_RESOLUTION_FAILURE;%0A  %7D%0A%0A
   @Overr
@@ -2364,32 +2364,36 @@
  return 
-DEFAULT_PRIORITY
+getDefaultPriority()
 ;%0A  %7D%0A%0A 
@@ -4117,17 +4117,10 @@
 alue
- = null
 ;%0A
+
     
@@ -4378,24 +4378,82 @@
 cution)) %7B%0A%0A
+        value = getDefaultPriorityOnResolutionFailure();%0A%0A
         LOG.
@@ -4603,133 +4603,17 @@
 %22 + 
-DEFAULT_PRIORITY_ON_RESOLUTION_FAILURE, e);%0A        value = DefaultJobPriorityProvider.DEFAULT_PRIORITY_ON_RESOLUTION_FAILURE
+value, e)
 ;%0A  

add methods for default priorities to priority provider