/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.libreplan.web.planner.tabs;
import static org.libreplan.web.I18nHelper._;
import static org.libreplan.web.planner.tabs.MultipleTabsPlannerController.BREADCRUMBS_SEPARATOR;
import static org.libreplan.web.planner.tabs.MultipleTabsPlannerController.getSchedulingLabel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.Validate;
import org.apache.commons.logging.LogFactory;
import org.joda.time.LocalDate;
import org.libreplan.business.common.IAdHocTransactionService;
import org.libreplan.business.common.IOnTransaction;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.planner.entities.AggregateOfResourceAllocations;
import org.libreplan.business.planner.entities.CalculatedValue;
import org.libreplan.business.planner.entities.Task;
import org.libreplan.business.planner.entities.TaskElement;
import org.libreplan.business.planner.entities.TaskGroup;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.web.planner.allocation.AdvancedAllocationController;
import org.libreplan.web.planner.allocation.AdvancedAllocationController.AllocationInput;
import org.libreplan.web.planner.allocation.AdvancedAllocationController.IAdvanceAllocationResultReceiver;
import org.libreplan.web.planner.allocation.AdvancedAllocationController.IBack;
import org.libreplan.web.planner.allocation.AdvancedAllocationController.Restriction;
import org.libreplan.web.planner.allocation.AdvancedAllocationController.Restriction.IRestrictionSource;
import org.libreplan.web.planner.allocation.AllocationResult;
import org.libreplan.web.planner.order.PlanningStateCreator;
import org.libreplan.web.planner.order.PlanningStateCreator.PlanningState;
import org.libreplan.web.planner.tabs.CreatedOnDemandTab.IComponentCreator;
import org.zkoss.ganttz.extensions.ITab;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zul.Image;
import org.zkoss.zul.Label;
/**
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
public class AdvancedAllocationTabCreator {
private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(AdvancedAllocationTabCreator.class);
private final class ResultReceiver implements IAdvanceAllocationResultReceiver {
private final CalculatedValue calculatedValue;
private final AggregateOfResourceAllocations aggregate;
private AllocationResult allocationResult;
private final Task task;
private final PlanningState planningState;
public ResultReceiver(PlanningState planningState, Task task) {
this.planningState = planningState;
this.calculatedValue = task.getCalculatedValue();
this.allocationResult = AllocationResult.createCurrent(planningState.getCurrentScenario(), task);
this.aggregate = this.allocationResult.getAggregate();
this.task = task;
}
@Override
public Restriction createRestriction() {
return Restriction.build(createRestrictionSource());
}
private IRestrictionSource createRestrictionSource() {
return new IRestrictionSource() {
@Override
public EffortDuration getTotalEffort() {
return aggregate.getTotalEffort();
}
@Override
public LocalDate getStart() {
if (aggregate.isEmpty()) {
LOG.info("the aggregate for task " + task.getName() + " is empty");
return task.getStartAsLocalDate();
}
return aggregate.getStart().getDate();
}
@Override
public LocalDate getEnd() {
if (aggregate.isEmpty()) {
LOG.info("the aggregate for task " + task.getName() + " is empty");
return task.getEndAsLocalDate();
}
return aggregate.getEnd().asExclusiveEnd();
}
@Override
public CalculatedValue getCalculatedValue() {
return calculatedValue;
}
};
}
@Override
public void cancel() {
// Do nothing
}
@Override
public void accepted(AggregateOfResourceAllocations modifiedAllocations) {
Validate.isTrue(allocationResult.getAggregate() == modifiedAllocations);
allocationResult.applyTo(planningState.getCurrentScenario(), task);
if (task.isManualAnyAllocation()) {
Task.convertOnStartInFixedDate(task);
}
updateParentsPositions(task);
}
private void updateParentsPositions(TaskElement task) {
TaskGroup current = task.getParent();
while (current != null) {
current.fitStartAndEndDatesToChildren();
current = current.getParent();
}
}
public AggregateOfResourceAllocations getAggregate() {
return aggregate;
}
}
private final String ADVANCED_ALLOCATION_VIEW = _("Advanced Allocation");
private final Mode mode;
private final IAdHocTransactionService adHocTransactionService;
private AdvancedAllocationController advancedAllocationController;
private final IBack onBack;
private final PlanningStateCreator planningStateCreator;
private final Component breadcrumbs;
public static ITab create(final Mode mode,
IAdHocTransactionService adHocTransactionService,
PlanningStateCreator planningStateCreator,
IBack onBack,
Component breadcrumbs) {
return new AdvancedAllocationTabCreator(
mode, adHocTransactionService, planningStateCreator, onBack, breadcrumbs).build();
}
private AdvancedAllocationTabCreator(Mode mode,
IAdHocTransactionService adHocTransactionService,
PlanningStateCreator planningStateCreator, IBack onBack,
Component breadcrumbs) {
Validate.notNull(mode);
Validate.notNull(adHocTransactionService);
Validate.notNull(onBack);
Validate.notNull(planningStateCreator);
Validate.notNull(breadcrumbs);
this.adHocTransactionService = adHocTransactionService;
this.mode = mode;
this.onBack = onBack;
this.planningStateCreator = planningStateCreator;
this.breadcrumbs = breadcrumbs;
}
private class AdvanceAssignmentCreator implements IComponentCreator {
private PlanningState planningState;
@Override
public Component create(final Component parent) {
return adHocTransactionService.runOnReadOnlyTransaction(new IOnTransaction<Component>() {
@Override
public Component execute() {
planningState = createPlanningState(parent, mode.getOrder());
return createComponent(parent, planningState);
}
private PlanningState createPlanningState(final Component parent, Order order) {
return planningStateCreator.retrieveOrCreate(parent.getDesktop(), order);
}
});
}
public PlanningState getState() {
if (planningState == null) {
throw new IllegalStateException("the planningState has not been created yet");
}
return planningState;
}
}
private ITab build() {
final AdvanceAssignmentCreator advanceAllocationComponentCreator = new AdvanceAssignmentCreator();
return new CreatedOnDemandTab(
ADVANCED_ALLOCATION_VIEW,
"advanced-allocation",
advanceAllocationComponentCreator) {
private boolean firstTime = true;
@Override
protected void afterShowAction() {
breadcrumbs.getChildren().clear();
breadcrumbs.appendChild(new Image(BREADCRUMBS_SEPARATOR));
breadcrumbs.appendChild(new Label(getSchedulingLabel()));
breadcrumbs.appendChild(new Image(BREADCRUMBS_SEPARATOR));
breadcrumbs.appendChild(new Label(_("Advanced Allocation")));
breadcrumbs.appendChild(new Image(BREADCRUMBS_SEPARATOR));
breadcrumbs.appendChild(new Label(mode.getOrder().getName()));
if (firstTime) {
firstTime = false;
return;
}
adHocTransactionService.runOnReadOnlyTransaction(new IOnTransaction<Void>() {
@Override
public Void execute() {
resetController(advanceAllocationComponentCreator.getState());
return null;
}
});
}
};
}
private Component createComponent(final Component parent, PlanningState planningState) {
return Executions.createComponents("advance_allocation.zul", parent, argsWithController(planningState));
}
private Map<String, Object> argsWithController(PlanningState planningState) {
Map<String, Object> result = new HashMap<>();
advancedAllocationController = new AdvancedAllocationController(
planningState.getOrder(),
onBack,
createAllocationInputsFor(planningState));
result.put("advancedAllocationController", advancedAllocationController);
return result;
}
private List<AllocationInput> createAllocationInputsFor(PlanningState planningState) {
planningState.reattach();
planningState.reassociateResourcesWithSession();
return createAllocationsWithOrderReloaded(planningState);
}
private List<AllocationInput> createAllocationsWithOrderReloaded(PlanningState planningState) {
List<Task> allTasks = planningState.getAllTasks();
List<AllocationInput> result = new ArrayList<>();
for (Task each : allTasks) {
if (each.hasSomeSatisfiedAllocation()) {
result.add(createAllocationInputFor(planningState, each));
}
}
return result;
}
private AllocationInput createAllocationInputFor(PlanningState planningState, Task task) {
ResultReceiver resultReceiver = new ResultReceiver(planningState, task);
return new AllocationInput(resultReceiver.getAggregate(), task, resultReceiver);
}
private void resetController(PlanningState planningState) {
advancedAllocationController.reset(planningState.getOrder(), onBack, createAllocationInputsFor(planningState));
}
}