import numpy as np
import math
import matplotlib.pyplot as plt
from matplotlib import cm
from collections import deque
import time
import copy
from matplotlib.font_manager import FontProperties
from Parameters import *
import csv
# 初始化用户的位置
def init(num, obstacle):
    x = []
    y = []
    tmp = []
    for i in range(us_M):
        posi = np.random.randint(0, area_n)
        while (posi in tmp) or (posi in obstacle):
            posi = np.random.randint(0, area_n)
        tmp.append(posi)
    tmp.sort()

    for i in range(us_M):
        x.append(int(tmp[i] / X_SIZE))
        y.append(int(tmp[i] % X_SIZE))
    return x, y
# 用户分类
def LOSorNLOS(us_p,num):
    LOSor = np.ones(num)
    for i in range(num):
        if np.random.rand() < NLOS_RATE:
            LOSor[i] = 0
    return LOSor

def LoSorNLos_posi(us_p):
    user_num  = len(us_p[0])
    LoSor = np.ones(user_num)
    """
    for i in range(user_num):
        if 10<=us_p[0][i]<=20 and 20 <= us_p[1][i] <= 30:
            LoSor[i] = 0
        elif 10<=us_p[0][i]<=20 and 30 <= us_p[1][i] <= 40:
            LoSor[i] = 0"""
    return  LoSor
def LOSorNLOS2(us_p,num):
    sort = 25
    nlosnum = int(sort * NLOS_RATE)
    nlos_posi = []
    while len(nlos_posi) < nlosnum:
        a = np.random.randint(0, 24)
        if a not in nlos_posi:
            nlos_posi.append(a)
    nlos_posi.sort()

    LOSor = np.ones(num)
    x, y = us_p[0], us_p[1]
    for i in range(num):
        tmp = int(x[i] / 6) * 5 + int(y[i] / 6)
        if tmp in nlos_posi:
            LOSor[i] = 0
    return LOSor, nlos_posi

def us1d(us_p):
    usposition = []
    length = len(us_p[0])
    for i in range(length):
        usposition.append(us_p[0][i] * X_SIZE + us_p[1][i])
    return usposition

# 计算距离 用户 i 到可部署位置 j
def calc_dis_deploy(us_p, LOSor,num,R_input):
    U = np.zeros([num, area_n])
    U_non = []
    # 对每个用户来说，只计算以覆盖半径为半径的一圈就可以

    for i in range(num):

        if LOSor[i]:
            R = R_input
        else:
            R = R_NLOS
        tmpR = int(R) + 1
        x, y = us_p[0][i], us_p[1][i]  # 用户的坐标

        for j in range(max(x - 2*tmpR, 0), min(x + tmpR *2, X_SIZE)):
            for k in range(max(0, y - 2*tmpR), min(y + tmpR *2, Y_SIZE)):
                dist = np.sqrt((x - j) ** 2 + (y - k) ** 2)
                if dist <= R:
                    # if Calc_SNR(dist) > rth:
                    U[i][j * X_SIZE + k] = 1
        U_non.append(np.atleast_1d(U[i]).nonzero()[0])
    return U_non

# 连接条件判断
def distUAV(a, b):
    # 转为坐标后计算
    a_x, b_x = int(a / X_SIZE), int(b / X_SIZE)
    a_y, b_y = int(a % X_SIZE), int(b % X_SIZE)
    dist = np.sqrt((a_x - b_x) ** 2 + (a_y - b_y) ** 2)
    return dist
def isConnected(a, b):
    if a > area_n:
        a = a - area_n
    if b > area_n:
        b = b - area_n
    if distUAV(a, b) <= distance_comm:
        return True
    else:
        return False


def isUSerCoverd(us_non,pop_non):
    tmp = set(us_non) & set(pop_non)
    #print(len(tmp))
    if len(tmp) == 0:
        return False
    return True

# 轮盘赌  select
def selectParents(pop,fitness):
    repop = copy.deepcopy(pop)
    idx = np.random.choice(np.arange(POP_SIZE),size=2,replace=False,
                           p=fitness/(fitness.sum()))
    # 重新排列 area_n 个
    return idx

# 轮盘赌  select
def selectParents2(fitness):

    fitsum = sum(fitness)
    rndpoint = np.random.uniform(0,fitsum)
    acc = 0
    for ind,val in enumerate(fitness):
        acc += val
        if acc >= rndpoint:
            father = ind
    rndpoint = np.random.uniform(0, fitsum)
    acc = 0
    for ind, val in enumerate(fitness):
        acc += val
        if acc >= rndpoint:
            mother = ind
    return father,mother


def read_csv(addr):
    ue_list = []
    with open(addr, 'r') as f:
        f = csv.reader(f, delimiter=',')
        #next(f)
        for row in f:
            ue_list.append(row)
    for i in range(len(ue_list)):
        ue_list[i] = list(map(float, ue_list[i]))
    return ue_list

# clac nextone
def base_coveredplace(start,R=R_base):
    x,y = int(start/X_SIZE),start%X_SIZE
    tmp = []
    for i in range(max(0,int(x-2*R)),min(X_SIZE,int(x+2*R))):
        for j in range(max(0,int(y-2*R)),min(Y_SIZE,int(y+2*R))):
            dist = math.sqrt((x-i)**2+(y-j)**2)
            if dist <= R:
                tmp.append(i*X_SIZE+j)

    return tmp

def base_get_fit2(pop,us1d):
    usernum = us1d.__len__()
    fit = np.zeros(POP_SIZE)
    for k in range(POP_SIZE):
        # 基站
        dna = []
        dna_ = pop[k].nonzero()[0]
        for j in range(len(dna_)):
            if dna_[j] >= area_n:
                dna.append(dna_[j])
        lendna = len(dna)
        # 基站可覆盖的区域
        coveredplace = []
        for i in range(lendna):
            coveredplace.extend(base_coveredplace(dna[i]))
        for i in range(usernum):
            if us1d[i] in coveredplace:
                fit[k] += 1
        # print(fit[np.argmax(fit)])
    return fit


def base_safescle(start):
    x,y = int(start/X_SIZE),start%X_SIZE
    tmp = []
    for i in range(max(0,int(x-2*safe_dist)),min(X_SIZE,int(x+2*safe_dist))):
        for j in range(max(0,int(y-2*safe_dist)),min(Y_SIZE,int(y+2*safe_dist))):
            dist = math.sqrt((x-i)**2+(y-j)**2)
            if dist <= safe_dist:
                tmp.append(i*X_SIZE+j)

    return tmp

# 基站部署位置编号-可连接到的基站部署位置编号
def base_neighbor(start):
    x,y = int(start/X_SIZE),start%X_SIZE
    tmp = []
    for i in range(max(0,int(x-2*R_base)),min(X_SIZE,int(x+2*R_base))):
        for j in range(max(0,int(y-2*R_base)),min(Y_SIZE,int(y+2*R_base))):
            dist = math.sqrt((x-i)**2+(y-j)**2)
            if safe_dist <= dist <= distance_comm:
                tmp.append(i*X_SIZE+j)
    k = np.random.randint(0,tmp.__len__())

    return tmp[k]

# 圆环区域
def calc_nextone(start,dmax,dmin=0):
    x = int(start/X_SIZE)
    y = start % X_SIZE

    U = []
    for i in range(max(int(x - 2*dmax), 0), min(int(x + dmax * 2), X_SIZE)):
        for j in range(max(0, int(y - 2*dmax)), min(int(y + dmax *2), Y_SIZE)):
                dist = np.sqrt((x - i) ** 2 + (y - j) ** 2)
                if dmin <= dist <= dmax:
                    U.append(i*X_SIZE+j)

    return U

# 计算 如果部署在这个地方 可以覆盖多少用户
def user_density(us1d,R_cover):
    density = np.zeros(area_n)
    # 用户i
    for i in range(len(us1d)):
        if us1d[i]<0:continue
        U = calc_nextone(us1d[i],R_cover,dmin=0)
        for j in range(len(U)):
            density[U[j]] += 1
    return density
# 轮盘赌
def select_density(density):

    densitysum = np.sum(density)
    rndpoint = np.random.uniform(0,densitysum)

    acc = 0
    for ind,val in enumerate(density):
        acc += val
        if acc >= rndpoint:return ind
# tournament
def select_density_tournament(fitness,k=3):

    father = np.random.randint(len(fitness))
    for ix in np.random.randint(0, len(fitness), k - 1):
        # check if better (e.g. perform a tournament)
        if fitness[ix] > fitness[father]:
            father = ix
    return father
# 安全距离约束
def SafeDist_perdna(deployed):

    deleted = []

    for j in range(len(deployed)):
            # 坐标
        uav_j = deployed[j]
        if uav_j > area_n:continue
            # print(uav_j)
        if uav_j not in deleted:
            for k in range(len(deployed)):
                    # 计算距离
                uav_k = deployed[k]
                if uav_k > area_n : continue
                if uav_k not in deleted:
                    if 0 < distUAV(uav_j, uav_k) < safe_dist:
                            # 删除
                        deleted.append(uav_k)
    return deleted

#
def cover_delete(pop,Depl_non,usernum):
    new_pop1 = np.zeros([POP_SIZE, DNA_SIZE])
    for i in range(POP_SIZE):

        deployed = pop[i].nonzero()[0]
        deleted = []
        for j in range(len(deployed)):
            iscovered = 1

            deleted.append(j)
            for k in range(len(deleted)):
                DNA = np.delete(deployed, deleted)

            # print(DNA,deleted)
            for k in range(usernum):
                us_non = Depl_non[k]
                # print(us_non,DNA)
                if len(set(us_non) & set(DNA)) == 0:
                    iscovered = 0
                    break
            # print(iscovered)
            if iscovered == 0:
                deleted.pop(-1)

        newdeployed = np.delete(deployed, deleted)

        for j in range(len(newdeployed)):
            new_pop1[i][newdeployed[j]] = 1
    return new_pop1


def cover_delete2(pop,pop_R,us_p,usernum):
    us_px, us_py = us_p[0], us_p[1]
    new_pop1 = np.zeros([POP_SIZE, DNA_SIZE])
    new_pop_R = np.zeros([POP_SIZE, DNA_SIZE])
    for i in range(POP_SIZE):
        # 基站位置
        deployed = pop[i].nonzero()[0]
        R_uav = []
        uav_x = []
        uav_y = []
        for uav in deployed:
            R_uav.append(pop_R[i][uav])
            uav_x.append(int(uav/X_SIZE))
            uav_y.append(uav%X_SIZE)
        deleted = []

        # 记录下覆盖每个用户的基站
        uav_for_us = []
        for j in range(usernum):
            tmp = []
            for k in range(len(deployed)):
                dist = math.sqrt((uav_x[k] - us_px[j]) ** 2 + (uav_y[k] - us_py[j]) ** 2)
                if dist<= R_uav[k]:
                    tmp.append(k)
            uav_for_us.append(tmp)

        for j in range(len(deployed)):
            iscovered = 1
            deleted.append(j)
            for k in range(usernum):
                if j not in uav_for_us[j]:
                    iscovered =0
                    break
            # print(iscovered)
            if iscovered == 0:
                deleted.pop(-1)

        newdeployed = np.delete(deployed, deleted)

        for j in range(len(newdeployed)):
            new_pop1[i][newdeployed[j]] = 1
            new_pop_R[i][newdeployed[j]] = pop_R[i][[newdeployed[j]]]
        R_new = []
        a = new_pop_R[i].nonzero()[0]
        for ind in a:
            R_new.append(new_pop_R[i][ind])

    return new_pop1,new_pop_R


def tournament_selection(fitness, k=3):
    # first random selection
    father = np.random.randint(len(fitness))
    for ix in np.random.randint(0, len(fitness), k - 1):
        # check if better (e.g. perform a tournament)
        if fitness[ix] < fitness[father]:
            father = ix

    mother = np.random.randint(len(fitness))
    for ix in np.random.randint(0, len(fitness), k - 1):
        # check if better (e.g. perform a tournament)
        if fitness[ix] < fitness[mother]:
            mother = ix
    return father,mother

def tournament_selection_gnd(fitness, k=3):
    # first random selection
    father = np.random.randint(len(fitness))
    for ix in np.random.randint(0, len(fitness), k - 1):
        # check if better (e.g. perform a tournament)
        if fitness[ix] > fitness[father]:
            father = ix

    mother = np.random.randint(len(fitness))
    for ix in np.random.randint(0, len(fitness), k - 1):
        # check if better (e.g. perform a tournament)
        if fitness[ix] > fitness[mother]:
            mother = ix
    return father,mother