/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU Lesser General Public License, v. 2.1.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License,
 * v.2.1 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */
package org.jboss.as.controller.client.helpers.standalone.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.jboss.as.controller.client.helpers.ClientConstants;
import org.jboss.as.controller.client.helpers.standalone.ServerDeploymentActionResult;
import org.jboss.as.controller.client.helpers.standalone.ServerDeploymentPlanResult;
import org.jboss.as.controller.client.helpers.standalone.ServerUpdateActionResult.Result;
import org.jboss.as.controller.client.helpers.standalone.SimpleServerDeploymentActionResult;
import org.jboss.dmr.ModelNode;

/**
 * Adapts from Future<ModelNode> to Future<ServerDeploymentPlanResult>.
 *
 * @author Brian Stansberry (c) 2011 Red Hat Inc.
 */
class ServerDeploymentPlanResultFuture implements Future<ServerDeploymentPlanResult> {

    private final Future<ModelNode> nodeFuture;
    private final DeploymentPlanImpl plan;

    ServerDeploymentPlanResultFuture(final DeploymentPlanImpl plan, final Future<ModelNode> nodeFuture) {
        this.plan = plan;
        this.nodeFuture = nodeFuture;
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        boolean cancelled = nodeFuture.cancel(mayInterruptIfRunning);
        if (cancelled) {
            plan.cleanup();
        } // else wait for finalize()
        return cancelled;
    }

    @Override
    public boolean isCancelled() {
        return nodeFuture.isCancelled();
    }

    @Override
    public boolean isDone() {
        return nodeFuture.isDone();
    }

    @Override
    public ServerDeploymentPlanResult get() throws InterruptedException, ExecutionException {
        boolean cleanup = true;
        ModelNode node;
        try {
            node = nodeFuture.get();
        } catch (InterruptedException ie) {
            cleanup = false;  // still may be in progress, so wait for finalize()
            throw ie;
        } finally {
            if (cleanup) {
                plan.cleanup();
            }
        }
        return getResultFromNode(node.get(ClientConstants.RESULT));
    }

    @Override
    public ServerDeploymentPlanResult get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,
            TimeoutException {
        boolean cleanup = true;
        ModelNode node;
        try {
            node = nodeFuture.get(timeout, unit);
        } catch (InterruptedException ie) {
            cleanup = false;  // still may be in progress, so wait for finalize()
            throw ie;
        } catch (TimeoutException te) {
            cleanup = false;  // still may be in progress, so wait for finalize()
            throw te;
        } finally {
            if (cleanup) {
                plan.cleanup();
            }
        }
        return getResultFromNode(node.get(ClientConstants.RESULT));
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        plan.cleanup();
    }

    private ServerDeploymentPlanResult getResultFromNode(ModelNode planResultNode) {
        UUID planId = plan.getId();
        Map<UUID, ServerDeploymentActionResult> actionResults = new HashMap<UUID, ServerDeploymentActionResult>();
        List<DeploymentActionImpl> actions = plan.getDeploymentActionImpls();
        for (int i = 0; i < actions.size(); i++) {
            DeploymentActionImpl action = actions.get(i);
            UUID actionId = action.getId();
            ModelNode actionResultNode = planResultNode.get("step-" + (i + 1));
            actionResults.put(actionId, getActionResult(actionId, actionResultNode));
        }
        return new DeploymentPlanResultImpl(planId, actionResults);
    }

    private ServerDeploymentActionResult getActionResult(UUID actionId, ModelNode actionResultNode) {
        ServerDeploymentActionResult actionResult = null;
        String outcome = actionResultNode.hasDefined("outcome") ? actionResultNode.get("outcome").asString() : null;
        if (outcome == null || "cancelled".equals(outcome)) {
            actionResult = new SimpleServerDeploymentActionResult(actionId, Result.NOT_EXECUTED);
        } else if ("failed".equals(outcome)) {
            Exception e = actionResultNode.hasDefined("failure-description") ? new Exception(actionResultNode.get("failure-description").toString()) : null;
            if (actionResultNode.hasDefined("rolled-back") && actionResultNode.get("rolled-back").asBoolean()) {
                if (e == null) {
                    actionResult = new SimpleServerDeploymentActionResult(actionId, Result.ROLLED_BACK);
                } else {
                    actionResult = new SimpleServerDeploymentActionResult(actionId, Result.ROLLED_BACK, e);
                }
            } else {
                actionResult = new SimpleServerDeploymentActionResult(actionId, e);
            }
        } else {
            actionResult = new SimpleServerDeploymentActionResult(actionId, Result.EXECUTED);
        }
        // FIXME deal with shutdown possibilities
        return actionResult;
    }

}
