package org.ovirt.engine.core.bll;

import org.ovirt.engine.core.bll.command.utils.StorageDomainSpaceChecker;
import org.ovirt.engine.core.bll.interfaces.BackendInternal;
import org.ovirt.engine.core.bll.storage.StorageDomainCommandBase;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.action.MoveOrCopyImageGroupParameters;
import org.ovirt.engine.core.common.action.MoveOrCopyParameters;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.DiskImage;
import org.ovirt.engine.core.common.businessentities.DiskImageTemplate;
import org.ovirt.engine.core.common.businessentities.ImageOperation;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatus;
import org.ovirt.engine.core.common.businessentities.StoragePoolIsoMapId;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.businessentities.storage_domains;
import org.ovirt.engine.core.common.queries.GetStorageDomainsByVmTemplateIdQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.common.vdscommands.GetImageDomainsListVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.LogCompat;
import org.ovirt.engine.core.compat.LogFactoryCompat;
import org.ovirt.engine.core.dal.VdcBllMessages;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.utils.transaction.TransactionMethod;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;

public class MoveOrCopyTemplateCommand<T extends MoveOrCopyParameters> extends StorageDomainCommandBase<T> {

    /**
     * Constructor for command creation when compensation is applied on startup
     *
     * @param commandId
     */
    protected MoveOrCopyTemplateCommand(Guid commandId) {
        super(commandId);
    }

    public MoveOrCopyTemplateCommand(T parameters) {
        super(parameters);
        setVmTemplateId(parameters.getContainerId());
        parameters.setEntityId(getVmTemplateId());
    }

    private storage_domains _sourceDomain;
    private Guid _sourceDomainId = new Guid();

    protected storage_domains getSourceDomain() {
        if (_sourceDomain == null && !_sourceDomainId.equals(Guid.Empty)) {
            _sourceDomain = getStorageDomainDAO().getForStoragePool(_sourceDomainId, getStoragePool().getId());
        } else if (_sourceDomain == null) {
            java.util.ArrayList<storage_domains> result = (java.util.ArrayList<storage_domains>) getBackend()
                    .runInternalQuery(VdcQueryType.GetStorageDomainsByVmTemplateId,
                            new GetStorageDomainsByVmTemplateIdQueryParameters(getVmTemplateId())).getReturnValue();
            if (result != null) {
                for (storage_domains domain : result) {
                    if (domain.getstatus() != null && domain.getstatus() == StorageDomainStatus.Active) {
                        _sourceDomain = domain;
                    }
                }
            }
        }
        return _sourceDomain;
    }

    protected BackendInternal getBackend() {
        return Backend.getInstance();
    }

    protected void SetSourceDomainId(Guid storageId) {
        _sourceDomainId = storageId;
    }

    protected ImageOperation getMoveOrCopyImageOperation() {
        return ImageOperation.Copy;

    }

    private java.util.ArrayList<DiskImage> _templateDisks;

    protected java.util.ArrayList<DiskImage> getTemplateDisks() {
        if (_templateDisks == null && getVmTemplate() != null) {
            VmTemplateHandler.UpdateDisksFromDb(getVmTemplate());
            _templateDisks = new java.util.ArrayList<DiskImage>();
            for (DiskImageTemplate diTemplate : getVmTemplate().getDiskMap().values()) {
                _templateDisks.add(DbFacade.getInstance().getDiskImageDAO().getSnapshotById(diTemplate.getId()));
            }
        }
        return _templateDisks;
    }

    @Override
    protected boolean canDoAction() {
        boolean retValue = true;
        if (getVmTemplate() == null) {
            retValue = false;
            addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_TEMPLATE_DOES_NOT_EXIST);
        } else if (getTemplateDisks() == null || getTemplateDisks().size() <= 0) {
            addCanDoActionMessage(VdcBllMessages.TEMPLATE_IMAGE_NOT_EXIST);
            retValue = false;
        }
        // check that images are ok
        if (retValue && getSourceDomain() == null) {
            addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_STORAGE_DOMAIN_STATUS_ILLEGAL);
            retValue = false;
        }
        retValue = retValue
                && VmTemplateCommand.isVmTemplateImagesReady(getVmTemplateId(), getSourceDomain().getid(),
                        getReturnValue().getCanDoActionMessages(), true, true, true, false);
        if (retValue) {
            setStoragePoolId(getVmTemplate().getstorage_pool_id());
            retValue = CheckStorageDomain()
                    && checkStorageDomainStatus(StorageDomainStatus.Active)
                    && CheckIfDisksExist(getTemplateDisks())
                    && checkFreeSpaceOnDestinationDomain(getStorageDomain(), (int) getVmTemplate().getActualDiskSize());
        }
        if (retValue
                && DbFacade.getInstance()
                        .getStoragePoolIsoMapDAO()
                        .get(new StoragePoolIsoMapId(getStorageDomain().getid(),
                                getVmTemplate().getstorage_pool_id().getValue())) == null) {
            retValue = false;
            addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_STORAGE_POOL_NOT_MATCH);
        }

        if (!retValue) {
            if (getMoveOrCopyImageOperation() == ImageOperation.Move) {
                addCanDoActionMessage(VdcBllMessages.VAR__ACTION__MOVE);
            } else {
                addCanDoActionMessage(VdcBllMessages.VAR__ACTION__COPY);
            }
            addCanDoActionMessage(VdcBllMessages.VAR__TYPE__VM_TEMPLATE);
        }
        return retValue;
    }

    private boolean checkFreeSpaceOnDestinationDomain(storage_domains domain, int requestedSizeGB) {
        if (!StorageDomainSpaceChecker.hasSpaceForRequest(domain, requestedSizeGB)) {
            addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_DISK_SPACE_LOW);
            return false;
        }
        return true;
    }

    @Override
    protected void executeCommand() {
        if (VmTemplateHandler.isTemplateStatusIsNotLocked(getVmTemplateId())) {
            VmTemplateHandler.lockVmTemplateInTransaction(getVmTemplateId(), getCompensationContext());
            MoveOrCopyAllImageGroups();
            setSucceeded(true);
        }
    }

    protected void MoveOrCopyAllImageGroups() {
        MoveOrCopyAllImageGroups(getVmTemplateId(), getTemplateDisks());
    }

    protected void MoveOrCopyAllImageGroups(final Guid containerID, final Iterable<DiskImage> disks) {
        TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() {

            @Override
            public Void runInTransaction() {
                for (DiskImage disk : disks) {
                    MoveOrCopyImageGroupParameters tempVar = new MoveOrCopyImageGroupParameters(containerID, disk
                            .getimage_group_id().getValue(), disk.getId(), getParameters().getStorageDomainId(),
                            getMoveOrCopyImageOperation());
                    tempVar.setParentCommand(getActionType());
                    tempVar.setEntityId(getParameters().getEntityId());
                    tempVar.setAddImageDomainMapping(getMoveOrCopyImageOperation() == ImageOperation.Copy);
                    MoveOrCopyImageGroupParameters p = tempVar;
                    // if copying template then AddImageDomainMapping should be true
                    if (getSourceDomain() != null) {
                        p.setSourceDomainId(getSourceDomain().getid());
                    }
                    p.setParentParemeters(getParameters());
                    VdcReturnValueBase vdcRetValue = getBackend().runInternalAction(
                            VdcActionType.MoveOrCopyImageGroup, p);
                    getParameters().getImagesParameters().add(p);

                    getReturnValue().getTaskIdList().addAll(vdcRetValue.getInternalTaskIdList());
                }
                return null;
            }
        });
    }

    @Override
    public AuditLogType getAuditLogTypeValue() {
        switch (getActionState()) {
        case EXECUTE:
            return getSucceeded() ? (getMoveOrCopyImageOperation() == ImageOperation.Move) ? AuditLogType.USER_MOVED_TEMPLATE
                    : AuditLogType.USER_COPIED_TEMPLATE
                    : (getMoveOrCopyImageOperation() == ImageOperation.Move) ? AuditLogType.USER_FAILED_MOVE_TEMPLATE
                            : AuditLogType.USER_FAILED_COPY_TEMPLATE;

        case END_SUCCESS:
            return getSucceeded() ? (getMoveOrCopyImageOperation() == ImageOperation.Move) ? AuditLogType.USER_MOVED_TEMPLATE_FINISHED_SUCCESS
                    : AuditLogType.USER_COPIED_TEMPLATE_FINISHED_SUCCESS
                    : (getMoveOrCopyImageOperation() == ImageOperation.Move) ? AuditLogType.USER_MOVED_TEMPLATE_FINISHED_FAILURE
                            : AuditLogType.USER_COPIED_TEMPLATE_FINISHED_FAILURE;

        default:
            return (getMoveOrCopyImageOperation() == ImageOperation.Move) ? AuditLogType.USER_MOVED_TEMPLATE_FINISHED_FAILURE
                    : AuditLogType.USER_COPIED_TEMPLATE_FINISHED_FAILURE;
        }
    }

    protected boolean CheckIfDisksExist(Iterable<DiskImage> disksList) {
        for (DiskImage disk : disksList) {
            java.util.ArrayList<Guid> domains = (java.util.ArrayList<Guid>) getBackend()
                    .getResourceManager()
                    .RunVdsCommand(
                            VDSCommandType.GetImageDomainsList,
                            new GetImageDomainsListVDSCommandParameters(disk.getstorage_pool_id().getValue(), disk
                                    .getimage_group_id().getValue())).getReturnValue();
            if (domains.contains(getParameters().getStorageDomainId())) {
                addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_STORAGE_DOMAIN_ALREADY_CONTAINS_DISK);
                return false;
            }
        }
        return true;
    }

    protected void EndMoveOrCopyCommand() {
        EndActionOnAllImageGroups();

        if (getVmTemplate() != null) {
            VmTemplateHandler.UnLockVmTemplate(getVmTemplateId());
            UpdateTemplateInSpm();
        }

        else {
            setCommandShouldBeLogged(false);
            log.warn("MoveOrCopyTemplateCommand::EndMoveOrCopyCommand: VmTemplate is null, not performing full EndAction");
        }

        setSucceeded(true);
    }

    protected void UpdateTemplateInSpm() {
        VmTemplateCommand.UpdateTemplateInSpm(getVmTemplate().getstorage_pool_id().getValue(),
                new java.util.ArrayList<VmTemplate>(java.util.Arrays.asList(new VmTemplate[] { getVmTemplate() })));
    }

    @Override
    protected void EndSuccessfully() {
        EndMoveOrCopyCommand();
    }

    @Override
    protected void EndWithFailure() {
        EndMoveOrCopyCommand();
    }

    protected void EndActionOnAllImageGroups() {
        for (VdcActionParametersBase p : getParameters().getImagesParameters()) {
            getBackend().EndAction(getImagesActionType(), p);
        }
    }

    protected VdcActionType getImagesActionType() {
        return VdcActionType.MoveOrCopyImageGroup;
    }

    private static LogCompat log = LogFactoryCompat.getLog(MoveOrCopyTemplateCommand.class);
}
