import math
import random

import numpy as np


class AerialVehicle(object):
    def __init__(self, CustomerNum, TotalContentNum, ContentSize, Hight,
                 EnvA, EnvB, Frequency, Bandwidth, TransmitPower, SpeedOfLight,
                 AvgLOS, AvgNLOS, ConstrainLOS, Noise, MaxPlaceX, MinPlaceX,
                 MaxPlaceY, MinPlaceY):
        self.CustomerNum = CustomerNum          # 区域内用户数量
        self.TotalContentNum = TotalContentNum  # 文件总数量
        self.ContentSize = ContentSize          # 单个文件大小
        self.K = 0                              # 分片数量
        self.PlaceX = 0                         # 无人机位置水平坐标x
        self.PlaceY = 0                         # 无人机位置水平坐标y
        self.PlaceH = Hight                     # 无人机飞行高度H
        self.EnvA = EnvA                        # 系统环境参数a
        self.EnvB = EnvB                        # 系统环境参数b

        self.Frequency = Frequency              # 无人机载波频率
        self.SpeedOfLight = SpeedOfLight        # 光速取值
        self.AvgLOS = AvgLOS                    # 无人机LOS传输平均额外损失
        self.AvgNLOS = AvgNLOS                  # 无人机NLOS传输平均额外损失
        self.ConstrainLOS = ConstrainLOS        # 最低LOS概率限制

        self.Bandwidth = Bandwidth              # 无人机总带宽
        self.TransmitPower = TransmitPower      # 无人机传输总功率
        self.Noise = Noise                      # 高斯白噪声

        self.ServiceNum = 0                     # 本轮服务的数量
        self.ServiceList = []                   # 本轮服务的用户List

        self.MaxPlaceX = MaxPlaceX              # 区域范围
        self.MinPlaceX = MinPlaceX
        self.MaxPlaceY = MaxPlaceY
        self.MinPlaceY = MinPlaceY

        self.TotalContentNum = TotalContentNum

        self.ServiceRadius = self.getRadiusOfUAV()                         # 无人机服务半径
        print("RadiusOfUAV " +  str(self.ServiceRadius))
        self.CachedContentList = [0 for _ in range(TotalContentNum)]       # 无人机缓存列表F

    def cacheContent(self, i):
        self.CachedContentList[i] = 1

    def clearCacheContent(self):
        self.CachedContentList = [0 for _ in range(self.TotalContentNum)]

    def whetherCacheContent(self, i):
        return self.CachedContentList[i] == 1

    def getCacheList(self):
        return self.CachedContentList

    def setK(self, K):
        self.K = K

    def getPlaceX(self):
        return self.PlaceX

    def getPlaceY(self):
        return self.PlaceY

    # 移动无人机位置
    def moveTo(self,x, y):
        self.PlaceX = x
        self.PlaceY = y

    def moveToByDist(self, dist, direction):
        x = self.PlaceX + dist * math.cos(direction)
        y = self.PlaceY + dist * math.sin(direction)


        self.PlaceX = x
        self.PlaceY = y
        return self.InPlace()

    def isBeyond(self, x, y):
        if x > self.MaxPlaceX or x < self.MinPlaceX or y > self.MaxPlaceY or y < self.MinPlaceY:
            return True
        return False

    # 限制无人机飞行范围
    def InPlace(self):
        punish = 0
        if self.PlaceX > self.MaxPlaceX:
            self.PlaceX = self.MaxPlaceX
            punish+=3
        if self.PlaceX > self.MaxPlaceX - 5:
            punish += 1
        if self.PlaceX < self.MinPlaceX:
            self.PlaceX = self.MinPlaceX
            punish += 3
        if self.PlaceX < self.MinPlaceX + 5:
            punish += 1
        if self.PlaceY > self.MaxPlaceY:
            self.PlaceY = self.MaxPlaceY
            punish += 3
        if self.PlaceY > self.MaxPlaceY - 5:
            punish += 1
        if self.PlaceY < self.MinPlaceY:
            self.PlaceY = self.MinPlaceY
            punish += 3
        if self.PlaceY < self.MinPlaceY + 5:
            punish += 1
        return punish

    # 计算水平距离
    def getDist(self,x, y):
        return math.sqrt(math.pow(self.PlaceX - x, 2) + math.pow(self.PlaceY - y, 2))

    # 计算LOS路径的概率
    def getPossOfLos(self, x, y):
        var1 = 180 / math.pi * math.atan(self.PlaceH / self.getDist(x, y))
        var2 = - self.EnvB * (var1 - self.EnvA)
        var3 = 1 + self.EnvA * math.exp(var2)
        return 1 / var3

    # 计算LOS路径的概率
    def getPossOfLosByDist(self, dist):
        var1 = (180)/3.1415 * math.atan(self.PlaceH / dist)
        var2 = - self.EnvB * (var1 - self.EnvA)
        var3 = 1 + self.EnvA * math.exp(var2)
        return 1 / var3

    # 计算路损的公共部分
    def getNormalLoss(self, x, y):
        var1 = math.log10(4 * math.pi * self.Frequency * self.getDist(x, y) / self.SpeedOfLight)
        if var1 <= 0:
            return 0
        return 20 * var1

    # 计算LOS路损
    def getLOS(self, x, y):
        return self.getNormalLoss(x, y) + self.AvgLOS

    # 计算NLOS路损
    def getNLOS(self, x, y):
        return self.getNormalLoss(x, y) + self.AvgNLOS

    # 计算平均路损
    def getAvgLOS(self, x, y):
        return self.getLOS(x, y) * self.getPossOfLos(x, y) + self.getNLOS(x, y) * (1 - self.getPossOfLos(x, y))

    # 计算无人机服务半径
    def getRadiusOfUAV(self):
        var1 = math.log10((1 - self.ConstrainLOS) / (self.EnvA * self.ConstrainLOS))
        var2 = math.tan(self.EnvA - (1 / self.EnvB) * var1)
        return self.PlaceH / var2

    # 无人机分配传输功率
    def allocTransmitPower(self):
        return self.TransmitPower

    # 无人机分配带宽
    def allocBandWidth(self):
        return self.Bandwidth / self.ServiceNum

    # 尝试是否在无人机服务范围内
    def tryGetService(self, x, y, requestIndex, UserIndex):
        print("tryGetService")
        if self.getDist(x, y) > self.ServiceRadius:
           # 水平距离大于服务半径
           return False
        elif not self.whetherCacheContent(requestIndex):
            # 该无人机未缓存该内容
           print("nonononono")
           return False
        else:
            self.ServiceNum = self.ServiceNum + 1
            self.ServiceList.append(UserIndex)
            return True

    def addService(self, UserIndex):
        self.ServiceNum = self.ServiceNum + 1
        self.ServiceList.append(UserIndex)


    # 计算下行传输速率
    def getTransSpeed(self, x, y):
        var1 = self.allocTransmitPower() / (self.Noise * np.power(10, self.getAvgLOS(x, y) / 10))
        return self.allocBandWidth() * math.log2(var1 + 1) / (1024 * 1024 * 8)

    # 计算分片大小
    def getSizeOFSlice(self):
        return self.ContentSize / self.K

    # 能否从无人机处获得分片
    def tryGetContent(self, x, y, time, UserIndex, transSpeedBaseLine, requestIndex):
        if self.CachedContentList[requestIndex] != 1:
            return False, -1
        if UserIndex not in self.ServiceList:
            return False, -1
        speed = self.getTransSpeed(x, y)

        if speed < transSpeedBaseLine:
            return False, (self.getSizeOFSlice() / speed)
        if (self.getSizeOFSlice() / speed) > time:
            return False, (self.getSizeOFSlice() / speed)
        return True, (self.getSizeOFSlice() / speed)

    def tryGetContent2(self, x, y):

        speed = self.getTransSpeed(x, y)
        print(speed)
        print((self.getSizeOFSlice() / speed))
        return (self.getSizeOFSlice() / speed)


    def clearServiceList(self):
        self.ServiceNum = 0
        return self.ServiceList.clear()



    def cal_bs_communication_delay(self, num):
        bs_transmit_power = 2
        noise_equivalent_power = np.power(0.1, 13)
        bs_bandwidth = 60 * np.power(10, 6)
        average_path_loss = self.getAvgLOS(-2000, -2000)
        signal_noise_ratio = bs_transmit_power / (
                noise_equivalent_power * np.power(10, average_path_loss / 10))
        transmission_speed = (bs_bandwidth / num) * math.log2(1 + signal_noise_ratio) / (1024 * 1024 * 8)
        return self.getSizeOFSlice() / transmission_speed






        # transmission_speed = (self.bs_bandwidth / self.uav_num) * math.log2(
        #     1 + self.unit_distance_channel_gain / distance * self.bs_transmit_power / self.uav_num / self.noise_equivalent_power)
        # return self.file_size / transmission_speed





####################################leader
    def setLeader(self, Role):
        self.Role = Role

    def isLeader(self):
        return self.Role



a = AerialVehicle(20, 50, 256, 200, 11.9, 0.13, 2e9, 40e6, 2, 3e8, 6, 20, 0.02, 1e-13, 500, 500,0,0)
a.ServiceNum = 6
a.PlaceX = 100
a.PlaceY = 100
a.K = 1
a.tryGetContent2(0,0)



