/*
 * Copyright 2019 Red Hat, Inc. and/or its affiliates.
 *
 * 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.kie.server.services.taskassigning.solver;
dialect "java"

import org.optaplanner.core.api.score.buildin.bendable.BendableScoreHolder;

import org.kie.server.services.taskassigning.core.model.ModelConstants;
import org.kie.server.services.taskassigning.core.model.Task;
import org.kie.server.services.taskassigning.core.model.TaskAssigningSolution;
import org.kie.server.services.taskassigning.core.model.solver.PriorityHelper

global BendableScoreHolder scoreHolder;

// ############################################################################
// Hard constraints
// ############################################################################

// A task can only be assigned to one of it's potential owners.
rule "Required Potential Owner"
    when
        Task(user != null, !acceptsAssignedUser)
    then
        scoreHolder.addHardConstraintMatch(kcontext, 0, -1);
end

/*
rule "Skill"
    when
        Task(user != null, !hasAllTheSkills)
    then
        scoreHolder.addHardConstraintMatch(kcontext, 1, -1);
end
*/

// ############################################################################
// Soft constraints
// ############################################################################

// First level soft constraint for penalizing the assignment of the PLANNING_USER. The PLANNING_USER is by definition
// a potential owner for all the tasks. But we want to avoid the assigning of this user "as soon as possible".
// The PLANNING_USER is e.g. a jBPM user with administration grants for enabling the management of Tasks that couldn't
// be assigned to anybody. e.g. if a Task needs to be assigned to user that belongs to group HR, but no users with that
// group exists the PLANNING_USER is assigned instead.
// This tasks can e.g. be later manually completed by the PLANNING_USER or manually reassigned
// to a different jBPM user by using the jBPM tasks lists administration, etc.
rule "PlanningUser assignment"
    when
        Task(user != null && ModelConstants.IS_PLANNING_USER.test(user.getEntityId()))
    then
        // a penalization is added each time the PLANNING_USER is assigned.
        scoreHolder.addSoftConstraintMatch(kcontext, 0, -1);
end

rule "High level priority"
    when
        $task : Task(user != null, PriorityHelper.isHighLevel(priority))
    then
        scoreHolder.addSoftConstraintMatch(kcontext, 1, PriorityHelper.calculateWeightedPenalty($task.getPriority(), $task.getEndTimeInMinutes()));
end

rule "Minimize makespan (starting with the latest ending user first)"
    when
        Task(user != null, nextTask == null, $endTimeInMinutes : endTimeInMinutes)
    then
        // Fairness and load balancing trick (see docs): squared to assure correctness in corner cases
        scoreHolder.addSoftConstraintMatch(kcontext, 2, - ($endTimeInMinutes * $endTimeInMinutes));
end

rule "Medium level priority"
    when
        $task : Task(user != null, PriorityHelper.isMediumLevel(priority))
    then
        scoreHolder.addSoftConstraintMatch(kcontext, 3, PriorityHelper.calculateWeightedPenalty($task.getPriority(), $task.getEndTimeInMinutes()));
end

rule "Low level priority"
    when
        $task : Task(user != null, PriorityHelper.isLowLevel(priority))
    then
        scoreHolder.addSoftConstraintMatch(kcontext, 4, PriorityHelper.calculateWeightedPenalty($task.getPriority(), $task.getEndTimeInMinutes()));
end