/*

 Copyright (c) [2023] [pcm]
 [pcm-coordinator] is licensed under Mulan PSL v2.
 You can use this software according to the terms and conditions of the Mulan PSL v2.
 You may obtain a copy of Mulan PSL v2 at:
          http://license.coscl.org.cn/MulanPSL2
 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 EITHER EXPaRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 See the Mulan PSL v2 for more details.

*/

package providerPricing

import (
	"gonum.org/v1/gonum/mat"
	"math"
)

func ComputeProfit(task *Task, tasksolution []int, resourcesolution []int, providerList []*Provider) float64 {
	var timeexecution int //记录任务的实际最大执行时间
	var costSum float64   //该任务在多个云厂商所需支付的成本总价

	for i, provider := range providerList {

		//如果该厂商分的任务为0，则直接跳过该厂商，循环到下一厂商
		if tasksolution[i] == 0 {
			continue
		}

		//先计算下该云厂商的执行时间ddl，并替换任务的最大执行时间,向上取整
		t := math.Ceil(float64(tasksolution[i])/float64(resourcesolution[i])) * float64(task.Time)
		if int(t) > timeexecution {
			timeexecution = int(t)
		}

		//计算前几份资源多执行任务
		forOneMoreTaskNUm := tasksolution[i] % resourcesolution[i]

		for j := 0; j < resourcesolution[i]; j++ {
			if j < forOneMoreTaskNUm {
				t = math.Ceil(float64(tasksolution[i])/float64(resourcesolution[i])) * float64(task.Time)
			} else {
				t = math.Floor(float64(tasksolution[i])/float64(resourcesolution[i])) * float64(task.Time)
			}

			//如果这份资源分的的任务数
			cost := (provider.CpuCost*task.Cpu + provider.MemCost*task.Mem + provider.DiskCost*task.Disk) * t * (math.Pow(float64(j+1), math.Log2(provider.LearnIndex)))
			costSum += cost
		}

	}

	//计算用户的支付价格pay
	pay := task.Pr
	if timeexecution == task.Time { //没有排队等待，且只有一个副本直接执行或者多个副本完全并行执行
		if pay < costSum {
			pay = costSum
		}
	} else if timeexecution >= task.T0 && timeexecution <= task.T1 { //有排队时间或者任务存在串行执行
		if task.T1 == task.T0 { //仅有一个副本，时间中有排队时间
			e := math.Exp(float64(-task.B) * float64(timeexecution-task.T1))
			pay = (1 - 1/(1+e)) * task.Pr
		} else { //多个副本
			e := math.Exp(float64(-task.B) * float64(timeexecution-task.T1) / float64(task.T1-task.T0))
			pay = (1 - 1/(1+e)) * task.Pr
		}

		if pay < costSum {
			pay = costSum
		}
	} else { //超出用户满意度的完全串行时间
		pay = 1 / 2 * task.Pr
		if pay < costSum {
			pay = costSum
		}
	}

	profitSum := pay - costSum
	return profitSum
}

func ComputeHighDegree(task *Task, resourcesolution []int, providerList []*Provider) float64 {
	var highDegreeSum float64
	// 依次计算每个云厂商的资源可用度
	for i, provider := range providerList {
		// 定义两个四维向量
		// 未来任务资源需求比例
		futureDemand := mat.NewVecDense(3, []float64{1, 1, 1})

		// 定义假设按此方案分配后的剩余资源可用量，时间虽然有差异，但是先按那个时刻算吧，这里可能还要改一下
		nowLeft_cpu := provider.CpuAvail - task.Cpu*float64(resourcesolution[i])
		nowLeft_mem := provider.MemAvail - task.Mem*float64(resourcesolution[i])
		nowLeft_disk := provider.DiskAvail - task.Disk*float64(resourcesolution[i])

		nowLeft := mat.NewVecDense(3, []float64{nowLeft_cpu, nowLeft_mem, nowLeft_disk})
		// 使用余弦相似度计算两个比值的相近度
		// 计算向量的内积
		dot_product := mat.Dot(futureDemand, nowLeft)

		// 计算向量的模长
		magnitude1 := mat.Norm(futureDemand, 2)
		magnitude2 := mat.Norm(nowLeft, 2)

		// 计算余弦相似度
		//临时处理被除数为0的特殊情况
		var cosineSimilarity = 0.0
		if magnitude1 != 0 && magnitude2 != 0 {
			cosineSimilarity = dot_product / (magnitude1 * magnitude2)
		}

		highDegreeSum += cosineSimilarity
	}

	return highDegreeSum / float64(len(providerList))
}

func Back_trace_task(ReplicaNum int, DoneReplicasNum int, providerList []*Provider, staclu int, res *[][]int, sum int) {
	//var count int = 0
	pnum := len(providerList)

	//所有的任务数都已经进行分配
	if DoneReplicasNum == ReplicaNum {
		var a []int
		for i := 0; i < pnum; i++ {
			a = append(a, providerList[i].CurReplicas)
		}
		*res = append(*res, a)
		//(*res)[0] = append((*res)[0], a)
		//count += 1
		return
	}

	//遍历完所有的云厂商序号
	if staclu >= pnum {
		return
	}

	if providerList[staclu].CurReplicas < providerList[staclu].MaxTaskCanRun {
		providerList[staclu].CurReplicas += 1
		Back_trace_task(ReplicaNum, DoneReplicasNum+1, providerList, staclu, res, sum)
		providerList[staclu].CurReplicas -= 1
		Back_trace_task(ReplicaNum, DoneReplicasNum, providerList, staclu+1, res, sum)
	} else {
		Back_trace_task(ReplicaNum, DoneReplicasNum, providerList, staclu+1, res, sum)
	}

}

func Back_trace_resource(list []int, i int, path []int, pathlist *[][]int) {
	if i == len(list) {
		var pathCopy = make([]int, len(path))
		copy(pathCopy, path)
		*pathlist = append(*pathlist, pathCopy)
		return
	}

	if list[i] == 0 {
		path = append(path, 0)
		Back_trace_resource(list, i+1, path, pathlist)
		path = path[:len(path)-1]
	} else {
		for j := 1; j < list[i]+1; j++ {
			path = append(path, j)
			Back_trace_resource(list, i+1, path, pathlist)
			path = path[:len(path)-1]
		}
	}

}
