package org.zstack.compute.allocator;

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.header.allocator.HostCapacityVO;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;

import javax.persistence.LockModeType;
import javax.persistence.TypedQuery;
import java.util.List;

/**
 * Created by frank on 11/2/2015.
 */
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class HostCapacityUpdater {
    private static final CLogger logger = Utils.getLogger(HostCapacityUpdater.class);

    @Autowired
    private DatabaseFacade dbf;
    @Autowired
    private CloudBus bus;

    private String hostUuid;
    private TypedQuery<HostCapacityVO> query;
    private HostCapacityVO capacityVO;
    private HostCapacityVO originalCopy;

    public HostCapacityUpdater(String hostUuid) {
        this.hostUuid = hostUuid;
    }

    public HostCapacityUpdater(TypedQuery<HostCapacityVO> query) {
        this.query = query;
    }

    private void logDeletedHost() {
        logger.warn(String.format("[Host Capacity] unable to update capacity for the host[uuid:%s]. It may have been deleted, cannot find it in database",
                hostUuid));
    }

    private void logCapacityChange() {
        if (logger.isTraceEnabled()) {
            StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
            int index = 0;
            String fileName = HostCapacityUpdater.class.getSimpleName() + ".java";
            for (int i=0; i<stackTraceElements.length; i++) {
                if (fileName.equals(stackTraceElements[i].getFileName())) {
                    index = i;
                }
            }
            StackTraceElement caller = stackTraceElements[index+1];
            logger.trace(String.format("[Host Capacity] %s:%s:%s changed the capacity of the host[uuid:%s] as:\n" +
                            "total cpu: %s --> %s\n" +
                            "available cpu: %s --> %s\n" +
                            "total memory: %s --> %s\n" +
                            "available memory: %s --> %s\n" +
                            "total physical memory: %s --> %s\n" +
                            "available physical memory: %s --> %s\n", caller.getFileName(), caller.getMethodName(), caller.getLineNumber(), capacityVO.getUuid(),
                    originalCopy.getTotalCpu(), capacityVO.getTotalCpu(),
                    originalCopy.getAvailableCpu(), capacityVO.getAvailableCpu(),
                    originalCopy.getTotalMemory(), capacityVO.getTotalMemory(),
                    originalCopy.getAvailableMemory(), capacityVO.getAvailableMemory(),
                    originalCopy.getTotalPhysicalMemory(), capacityVO.getTotalPhysicalMemory(),
                    originalCopy.getAvailablePhysicalMemory(), capacityVO.getAvailablePhysicalMemory()));
        }
    }

    private boolean lockCapacity() {
        if (hostUuid != null) {
            capacityVO = dbf.getEntityManager().find(HostCapacityVO.class, hostUuid, LockModeType.PESSIMISTIC_WRITE);
        } else if (query != null) {
            query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
            List<HostCapacityVO> caps = query.getResultList();
            capacityVO = caps.isEmpty() ? null : caps.get(0);
        }

        if (capacityVO != null) {
            originalCopy = new HostCapacityVO();
            originalCopy.setTotalCpu(capacityVO.getTotalCpu());
            originalCopy.setAvailableCpu(capacityVO.getAvailableCpu());
            originalCopy.setTotalMemory(capacityVO.getTotalMemory());
            originalCopy.setAvailableMemory(capacityVO.getAvailableMemory());
            originalCopy.setTotalPhysicalMemory(capacityVO.getTotalPhysicalMemory());
            originalCopy.setAvailablePhysicalMemory(capacityVO.getAvailablePhysicalMemory());
        }

        return capacityVO != null;
    }

    private void merge() {
        capacityVO = dbf.getEntityManager().merge(capacityVO);
        logCapacityChange();
    }

    @Transactional
    public boolean run(HostCapacityUpdaterRunnable runnable) {
        if (!lockCapacity()) {
            logDeletedHost();
            return false;
        }

        HostCapacityVO cap = runnable.call(capacityVO);
        if (cap != null) {
            capacityVO = cap;
            merge();
            return true;
        }
        return false;
    }

}

@@ -902,51 +902,8 @@
 bf;%0D
-%0A    @Autowired%0D%0A    private CloudBus bus;%0D
 %0A%0D%0A 

clean up