package org.cloudbus.cloudsim.power;

import org.cloudbus.cloudsim.Host;
import org.cloudbus.cloudsim.Log;
import org.cloudbus.cloudsim.Vm;
import org.cloudbus.cloudsim.power.lists.PowerVmList;

import java.util.*;

public class PowerVmAllocationPolicyMigrationlunwenshixian
        extends PowerVmAllocationPolicyMigrationAbstract {
    private PowerVmAllocationPolicyMigrationAbstract fallbackVmAllocationPolicy;
    private double safetyParameter = 0;

    /**
     * Instantiates a new PowerVmAllocationPolicyMigrationAbstract.
     *
     * @param hostList          the host list
     * @param vmSelectionPolicy the vm selection policy
     */
    public PowerVmAllocationPolicyMigrationlunwenshixian(
            List<? extends Host> hostList,
            PowerVmSelectionPolicy vmSelectionPolicy,
            double safetyParameter,
            PowerVmAllocationPolicyMigrationAbstract fallbackVmAllocationPolicy) {
        super(hostList, vmSelectionPolicy);
        setSafetyParameter(safetyParameter);
        setFallbackVmAllocationPolicy(fallbackVmAllocationPolicy);
    }
    /*
	论文中判断主机是否过载得方法
	 */
    @Override
    protected boolean isHostOverUtilized(PowerHost host) {
//        List<Integer> c = new ArrayList<>();
////        for (PowerHost host1 : this.<PowerHost>getHostList()) {
////            List<? extends Vm> a = host1.getVmList();
////            int b = a.size();
////            c.add(b);
////        }
////        System.out.println(c);
        double mipmax = Double.MIN_VALUE;
        double mipmin = Double.MAX_VALUE;
//        double rammax = Double.MIN_VALUE;
//        double rammin = Double.MAX_VALUE;
//        double bwmax = Double.MIN_VALUE;
//        double bwmin = Double.MAX_VALUE;
        for (Vm vm : host.getVmList()){
            double vmmip = vm.getMips()*vm.getCurrentUtilizationOfCpu();
//            double vmram = vm.getRam();
//            double vmbw = vm.getBw();
            if (vmmip > mipmax){
                mipmax = vmmip;
            }
            if (vmmip < mipmin){
                mipmin = vmmip;
            }
//            if (vmram > rammax){
//                rammax = vmram;
//            }
//            if (vmram < rammin){
//                rammin = vmram;
//            }
//            if (vmbw > bwmax){
//                bwmax = vmbw;
//            }
//            if (vmbw < bwmin){
//                bwmin = vmbw;
//            }
        }
        double Wvm = 0;
        for (Vm vm : host.getVmList()){
            double vmmip = vm.getMips() * vm.getCurrentUtilizationOfCpu();
            double s;
            if (mipmax == mipmin){
                s = 0;
            }
            else {
                s = (vmmip - mipmin)/(mipmax - mipmin);
            }
            Wvm += s;
//            double vmram = vm.getRam();
//            if (rammax == rammin){
//                s = 0;
//            }
//            else {
//                s = (vmram - rammin)/(rammax - rammin);
//            }
//            Wvm += s;
//            double vmbw = vm.getBw();
//            if (bwmax == bwmin) {
//                s = 0;
//            }
//            else{
//                s = (vmbw - bwmin)/(bwmax - bwmin);
//            }
//            Wvm += s;
        }
        double T= 3.0;
//        System.out.println(hostResourceWeight1(host));
//        int whost =(int)hostResourceWeight().get(host.getId());
//        System.out.println(whost);
        double Whost = hostResourceWeight1(host);
//        System.out.println(Wvm);
//        System.out.println((Wvm - Whost)/3);
        return (Wvm - Whost)> T;
    }
    /*
	自己仿照论文为虚拟机寻找主机方法
	 */
    @Override
    protected List<Map<String, Object>> getNewVmPlacement(
            List<? extends Vm> vmsToMigrate,
            Set<? extends Host> excludedHosts){
        List<Map<String, Object>> migrationMap = new LinkedList<Map<String, Object>>();
        PowerVmList.sortByCpuUtilization(vmsToMigrate);
        for (Vm vm : vmsToMigrate){
            double Umin = Double.MAX_VALUE;
            Host allocatedhost = null;
            for (PowerHost host : this.<PowerHost>getHostList()){
                if (excludedHosts.contains(host)) {
                    continue;
                }
                boolean flag = true;
                double Akmip = host.getAvailableMips();
                double Xkmip = vm.getMips()*vm.getCurrentUtilizationOfCpu();
                double Pkmip = host.getTotalMips();
                if (Akmip + Xkmip > Pkmip){
                    flag = false;
                }
//                double Akram = host.getAvailableRam();
////                double Xkram = vm.getRam();
////                double Pkram = host.getRam();
////                if (Akram + Xkram > Pkram){
////                    flag = false;
////                }
////                double Akbw = host.getAvailbleBw();
////                double Xkbw = vm.getBw();
////                double Pkbw = host.getBw();
////                if (Akbw + Xkbw > Pkbw){
////                    flag = false;
////                }
                int hostid;
                if (flag == true){
                    double U = (Xkmip/Pkmip) ;//+ (Xkram/Pkram) + (Xkbw/Pkbw);
                    if (U < Umin){
                        Umin = U;
                        allocatedhost = host;
                    }
                }
            }
            Map<String, Object> migrate = new HashMap<String, Object>();
            migrate.put("vm", vm);
            migrate.put("host",allocatedhost);
            migrationMap.add(migrate);


        }
        return migrationMap;
    }
    protected double hostResourceWeight1(PowerHost host1){
        List<Map<String, Object>> migrationMap = new LinkedList<Map<String, Object>>();
        double mipmax = Double.MIN_VALUE;
        double mipmin = Double.MAX_VALUE;
//        double rammax = Double.MIN_VALUE;
//        double rammin = Double.MAX_VALUE;
//        double rwmax = Double.MIN_VALUE;
//        double rwmin = Double.MAX_VALUE;
        Map<String, Object> migrate = new HashMap<String, Object>();
        for (PowerHost host : this.<PowerHost> getHostList()){
            double hostmip = host.getTotalMips();//host.getMaxAvailableMips()*host.getNumberOfPes();
//            double hostram = host.getRam();
//            double hostrw = host.getBw();
            if (hostmip > mipmax){
                mipmax = hostmip;
            }
            if (hostmip < mipmin){
                mipmin = hostmip;
            }

        }
        double Whost = 0;
        double whost ;

        for (PowerHost host : this.<PowerHost> getHostList()){
            if (mipmax == mipmin){
                whost = Double.MIN_VALUE;
            }
            else {
                double hostmip = host.getTotalMips();//getMaxAvailableMips()*host.getNumberOfPes();
                whost = (hostmip - mipmin)/(mipmax - mipmin);
            }
            Whost += whost;
            Map<String, Object> migrate1 = new HashMap<String, Object>();

            migrate1.put("host", host);
            migrate1.put("resourceweight",Whost);
            migrationMap.add(migrate1);

            //migrate.put("host.getId()",Whost);
        }
        double result = 0;
        for (Map<String, Object> map :migrationMap){
            if (map.get("host") == host1){
                result = (double) map.get("resourceweight");
            }
        }
        return result;
    }

    /**
     * 谷航宇模拟论文的整合方法
     * 1、计算每个主机得资源权重（cpu\ram\bw）Map<String, Object>
     */
    protected Map<String, Object> hostResourceWeight(){
        double mipmax = Double.MIN_VALUE;
        double mipmin = Double.MAX_VALUE;
//        double rammax = Double.MIN_VALUE;
//        double rammin = Double.MAX_VALUE;
//        double rwmax = Double.MIN_VALUE;
//        double rwmin = Double.MAX_VALUE;
        Map<String, Object> migrate = new HashMap<String, Object>();
        for (PowerHost host : this.<PowerHost> getHostList()){
            double hostmip = host.getMaxAvailableMips()*host.getNumberOfPes();
//            double hostram = host.getRam();
//            double hostrw = host.getBw();
            if (hostmip > mipmax){
                mipmax = hostmip;
            }
            if (hostmip < mipmin){
                mipmin = hostmip;
            }
//            if (hostram > rammax){
//                rammax = hostram;
//            }
//            if (hostram < rammin){
//                rammin = hostram;
//            }if (hostrw > rwmax){
//                rwmax = hostrw;
//            }
//            if (hostrw < rwmin){
//                rwmin = hostrw;
//            }
        }
        double Whost = 0;
        double whost ;
        for (PowerHost host : this.<PowerHost> getHostList()){
            if (mipmax == mipmin){
                whost = Double.MIN_VALUE;
            }
            else {
                double hostmip = host.getMaxAvailableMips()*host.getNumberOfPes();
                whost = (hostmip - mipmin)/(mipmax - mipmin);
            }
            Whost += whost;
//            if (rammax == rammin){
//                whost = Double.MIN_VALUE;
//            }
//            else {
//                double hostram = host.getRam();
//                whost = (hostram - rammin)/(rammax - rammin);
//            }
//            Whost += whost;
//            if (rwmax == rwmin) {
//                whost = Double.MIN_VALUE;
//            }
//            else {
//                double hostrw = host.getBw();
//                whost = (hostrw - rwmin)/(rwmax - rwmin);
//            }
//            Whost += whost;
            migrate.put("host.getId()",Whost);
        }
        return migrate;
    }








    /*
	论文中获取欠载主机得方法
	 */
    @Override
    protected PowerHost getUnderUtilizedHost(Set<? extends Host> excludedHosts){
        double Ball;
        PowerHost underhost = null;
        double X = Double.MAX_VALUE;
        for (PowerHost host : this.<PowerHost>getHostList()){
            if (excludedHosts.contains(host)) {
                continue;
            }
            double Akmip = host.getAvailableMips()*host.getNumberOfPes();
            double Pkmip = host.getNumberOfPes()*host.getTotalMips();
            double B1 = Akmip/Pkmip;
//            double Akram = host.getAvailableRam();
//            double Pkram = host.getRam();
//            double B2 = Akram/Pkram;
//            double Akbw = host.getAvailbleBw();
//            double Pkbw = host.getBw();
//            double B3 = Akbw/Pkbw;

            Ball = B1 ;//+ B2 + B3;
            if (Ball < X){
                X = Ball;
                underhost = host;
            }
        }
        return underhost;
    }
    /**
     * Sets the safety parameter.
     *
     * @param safetyParameter the new safety parameter
     */
    protected void setSafetyParameter(double safetyParameter) {
        if (safetyParameter < 0) {
            Log.printConcatLine("The safety parameter cannot be less than zero. The passed value is: ",
                    safetyParameter);
            System.exit(0);
        }
        this.safetyParameter = safetyParameter;
    }
    /**
     * Sets the fallback vm allocation policy.
     *
     * @param fallbackVmAllocationPolicy the new fallback vm allocation policy
     */
    public void setFallbackVmAllocationPolicy(
            PowerVmAllocationPolicyMigrationAbstract fallbackVmAllocationPolicy) {
        this.fallbackVmAllocationPolicy = fallbackVmAllocationPolicy;
    }
}
