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

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.ovirt.engine.core.bll.ValidationResult;
import org.ovirt.engine.core.common.businessentities.DiskImage;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatus;
import org.ovirt.engine.core.common.businessentities.StorageDomainType;
import org.ovirt.engine.core.common.businessentities.storage_domains;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dal.VdcBllMessages;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;

public class StorageDomainValidator {

    private storage_domains storageDomain;

    public StorageDomainValidator(storage_domains domain) {
        storageDomain = domain;
    }

    public boolean isDomainExistAndActive(List<String> messages) {
        if (storageDomain == null) {
            messages.add(VdcBllMessages.ACTION_TYPE_FAILED_STORAGE_DOMAIN_NOT_EXIST.toString());
            return false;
        }
        if (storageDomain.getstatus() == null || storageDomain.getstatus() != StorageDomainStatus.Active) {
            messages.add(VdcBllMessages.ACTION_TYPE_FAILED_STORAGE_DOMAIN_STATUS_ILLEGAL.toString());
            return false;
        }
        return true;
    }

    public boolean domainIsValidDestination(List<String> messages) {
        if (storageDomain.getstorage_domain_type() == StorageDomainType.ISO
                || storageDomain.getstorage_domain_type() == StorageDomainType.ImportExport) {
            messages.add(VdcBllMessages.ACTION_TYPE_FAILED_STORAGE_DOMAIN_TYPE_ILLEGAL.toString());
            return false;
        }
        return true;
    }

    private String storageName() {
        return String.format("$%1$s %2$s", "storageName", storageDomain.getstorage_name());
    }

    public ValidationResult isDomainHasSpaceForRequest(final long requestedSize) {
        return isDomainHasSpaceForRequest(requestedSize, true);
    }

    public ValidationResult isDomainHasSpaceForRequest(final long requestedSize, final boolean useThresHold) {
        long size = useThresHold ? getLowDiskSpaceThreshold() : 0l;
        if (storageDomain.getavailable_disk_size() != null &&
                storageDomain.getavailable_disk_size() - requestedSize < size) {
            return new ValidationResult(VdcBllMessages.ACTION_TYPE_FAILED_DISK_SPACE_LOW_ON_TARGET_STORAGE_DOMAIN,
                    storageName());
        }
        return ValidationResult.VALID;
    }

    private static Integer getLowDiskSpaceThreshold() {
        return Config.<Integer> GetValue(ConfigValues.FreeSpaceCriticalLowInGB);
    }

    public static Map<storage_domains, Integer> getSpaceRequirementsForStorageDomains(Collection<DiskImage> images,
            Map<Guid, storage_domains> storageDomains, Map<Guid, DiskImage> imageToDestinationDomainMap) {
        Map<DiskImage, storage_domains> spaceMap = new HashMap<DiskImage, storage_domains>();
        for (DiskImage image : images) {
            Guid storageId = imageToDestinationDomainMap.get(image.getId()).getstorage_ids().get(0);
            storage_domains domain = storageDomains.get(storageId);
            if (domain == null) {
                domain = DbFacade.getInstance().getStorageDomainDao().get(storageId);
            }
            spaceMap.put(image, domain);
        }
        return StorageDomainValidator.getSpaceRequirementsForStorageDomains(spaceMap);
    }

    public static Map<storage_domains, Integer> getSpaceRequirementsForStorageDomains(Map<DiskImage, storage_domains> imageToDomainMap) {
        Map<storage_domains, Integer> map = new HashMap<storage_domains, Integer>();
        if (!imageToDomainMap.isEmpty()) {
            for (Map.Entry<DiskImage, storage_domains> entry : imageToDomainMap.entrySet()) {
                storage_domains domain = entry.getValue();
                int size = (int) entry.getKey().getActualSize();
                if (map.containsKey(domain)) {
                    map.put(domain, map.get(domain) + size);
                } else {
                    map.put(domain, size);
                }
            }
        }
        return map;
    }
}
