package org.ovirt.engine.core.bll.storage;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.ovirt.engine.core.bll.InternalCommandAttribute;
import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute;
import org.ovirt.engine.core.bll.RemoveAllCinderDisksCommandCallBack;
import org.ovirt.engine.core.bll.VmCommand;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.tasks.CommandCoordinatorUtil;
import org.ovirt.engine.core.bll.tasks.interfaces.CommandCallback;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.ImagesContainterParametersBase;
import org.ovirt.engine.core.common.action.RemoveCinderDiskParameters;
import org.ovirt.engine.core.common.action.RemoveCinderDiskVolumeParameters;
import org.ovirt.engine.core.common.action.RestoreAllCinderSnapshotsParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.Snapshot;
import org.ovirt.engine.core.common.businessentities.SubjectEntity;
import org.ovirt.engine.core.common.businessentities.storage.CinderDisk;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;

@InternalCommandAttribute
@NonTransactiveCommandAttribute
public class RestoreAllCinderSnapshotsCommand<T extends RestoreAllCinderSnapshotsParameters> extends VmCommand<T> {

    public RestoreAllCinderSnapshotsCommand(T parameters, CommandContext cmdContext) {
        super(parameters, cmdContext);
    }

    @Override
    protected void executeVmCommand() {
        for (CinderDisk cinderDisk : getParameters().getCinderDisksToRestore()) {
            ImagesContainterParametersBase params = getRestoreFromSnapshotParams(cinderDisk);
            restoreCinderDisk(cinderDisk, params);

            // In case we want to undo the previewed snapshot.
            if (getParameters().getSnapshot().getType() != Snapshot.SnapshotType.REGULAR) {
                DiskImage cinderDiskFromLastActiveSnap =
                        getDiskImageDao().getDiskSnapshotForVmSnapshot(cinderDisk.getId(),
                                getParameters().getSnapshot().getId());
                cinderDiskFromLastActiveSnap.setActive(true);
                getImageDao().update(cinderDiskFromLastActiveSnap.getImage());
            }
        }
        List<CinderDisk> cinderDisksToRemove = getParameters().getCinderDisksToRemove();
        for (CinderDisk cinderDisk : cinderDisksToRemove) {
            RemoveCinderDiskParameters removeDiskParam =
                    new RemoveCinderDiskParameters(cinderDisk.getImageId());
            removeDiskParam.setRemovedVolume(cinderDisk);
            removeDiskParam.setParentCommand(getActionType());
            removeDiskParam.setStorageDomainId(cinderDisk.getStorageIds().get(0));
            removeDiskParam.setParentParameters(getParameters());

            Future<VdcReturnValueBase> future = CommandCoordinatorUtil.executeAsyncCommand(
                    VdcActionType.RemoveCinderDisk,
                    removeDiskParam,
                    cloneContextAndDetachFromParent(),
                    new SubjectEntity(VdcObjectType.Storage, cinderDisk.getStorageIds().get(0)));
            try {
                future.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
                log.error("Error removing Cinder disk");
            }
        }
        List<CinderDisk> cinderVolumesToRemove = getParameters().getCinderVolumesToRemove();
        for (CinderDisk cinderVolume : cinderVolumesToRemove) {
            RemoveCinderDiskVolumeParameters removeDiskVolumeParam =
                    new RemoveCinderDiskVolumeParameters(cinderVolume);
            removeDiskVolumeParam.setParentCommand(getActionType());
            removeDiskVolumeParam.setParentParameters(getParameters());

            Future<VdcReturnValueBase> future = CommandCoordinatorUtil.executeAsyncCommand(
                    VdcActionType.RemoveCinderDiskVolume,
                    removeDiskVolumeParam,
                    cloneContextAndDetachFromParent(),
                    new SubjectEntity(VdcObjectType.Storage, cinderVolume.getStorageIds().get(0)));
            try {
                future.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
                log.error("Error removing Cinder disk");
            }
        }
        setSucceeded(true);
    }

    private ImagesContainterParametersBase getRestoreFromSnapshotParams(CinderDisk cinderDisk) {
        RemoveCinderDiskParameters params =
                new RemoveCinderDiskParameters(cinderDisk.getImageId());
        params.setRemovedVolume(cinderDisk);
        params.setParentCommand(getActionType());
        params.setStorageDomainId(cinderDisk.getStorageIds().get(0));
        params.setParentParameters(getParameters());
        return params;
    }

    private VdcReturnValueBase restoreCinderDisk(CinderDisk cinderDisk, ImagesContainterParametersBase params) {
        Future<VdcReturnValueBase> future = CommandCoordinatorUtil.executeAsyncCommand(
                VdcActionType.RestoreFromCinderSnapshot,
                params,
                cloneContextAndDetachFromParent(),
                new SubjectEntity(VdcObjectType.Storage, cinderDisk.getStorageIds().get(0)));
        try {
            return future.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
            log.error("Error restoring snapshot");
        }
        return null;
    }

    @Override
    protected void endSuccessfully() {
        if (!getParameters().isParentHasTasks()) {
            getBackend().endAction(getParameters().getParentCommand(),
                    getParameters().getParentParameters(),
                    getContext().clone().withoutCompensationContext().withoutExecutionContext().withoutLock());
        }
    }

    @Override
    protected void endWithFailure() {
        if (!getParameters().isParentHasTasks()) {
            getParameters().getParentParameters().setTaskGroupSuccess(false);
            getBackend().endAction(getParameters().getParentCommand(),
                    getParameters().getParentParameters(),
                    cloneContextAndDetachFromParent());
        }
        setSucceeded(true);
    }

    @Override
    public CommandCallback getCallback() {
        return new RemoveAllCinderDisksCommandCallBack<>();
    }
}

@@ -5802,24 +5802,74 @@
 ssfully() %7B%0A
+        removeRedundantVolumesForOrphanedDisks();%0A
         if (
@@ -5899,32 +5899,32 @@
 ntHasTasks()) %7B%0A
-
             getB
@@ -6214,24 +6214,74 @@
 Failure() %7B%0A
+        removeRedundantVolumesForOrphanedDisks();%0A
         if (
@@ -6585,32 +6585,32 @@
 t());%0A        %7D%0A
-
         setSucce
@@ -6620,32 +6620,418 @@
 d(true);%0A    %7D%0A%0A
+    private void removeRedundantVolumesForOrphanedDisks() %7B%0A        List%3CCinderDisk%3E cinderVolumesToRemove = getParameters().getCinderVolumesToRemove();%0A        for (CinderDisk cinderVolume : cinderVolumesToRemove) %7B%0A            getDbFacade().getImageStorageDomainMapDao().remove(cinderVolume.getImageId());%0A            getImageDao().remove(cinderVolume.getImageId());%0A        %7D%0A    %7D%0A%0A
     @Override%0A  

Remove redundant Cinder volumes for commited snapshot.