import math
import Users
import numpy as np
import random
from pprint import pprint
import constants as C
import tool
import ObjectiveFuction as OF

# 定义混沌映射（Logistic映射）
def logistic_map(x, r):
    return r * x * (1 - x)
class CPSO:
    def __init__(self,pre_uavs,users,r_c,r_p,k,N=50):
        #之后解析JSON，从JSON中读参数
        self.N=N
        self.k=k
        self.pre_uavs=pre_uavs
        self.users=users
        self.d = 3  # 可行解维数
        self.limit = [C.min_range,C.max_range]  # 边界位置限制
        self.vlimit = [-50, 50]  # 边界速度限制
        self.r_c = r_c
        self.r_p=r_p

        # self.height = 100
        self.min_height = C.min_height
        self.max_height = C.max_height
        self.wmax = 0.9  # 惯性权重
        self.wmin = 0.4
        self.c1 = 1.5  # 自我学习因子
        self.c2 = 1.5  # 群体学习因子
        # self.c1 = 0.5  # 自我学习因子
        # self.c2 = 0.5  # 群体学习因子
        self.maxgen = 200
        # self.users = Users.getUsers("./data.csv")
        self.users = users
        self.r=3.8
        # self.c_association = c_association
        # self.p_association = p_association
        # self.p_all = p_all
        # self.b_all = b_all

        # self.init_alloc()


    # #todo：防止无人机位置碰撞
    # def punish_function(self,uavs):
    #     # user_arr = [1, 2, 5]
    #     punish=0
    #     for i in range(len(uavs)):
    #         for j in range(i+1,len(uavs)):
    #             dis=self.cal_tool.getDistance(uavs[i],uavs[j])
    #             if dis<200:
    #                 punish+=100
    #
    #     return punish
    def init_alloc(self,):
        c_association, p_association, p_all, b_all=OF.objective_resource(self.pre_uavs,self.users,self.r_c)
        self.c_association=c_association
        self.p_association = p_association
        self.p_all=p_all
        self.b_all=b_all


    def re_init(self,pre_uavs,users,r_c,r_p,c_association, p_association, p_all, b_all,):
        self.pre_uavs=pre_uavs
        self.user=users
        self.r_c=r_c
        self.r_p=r_p
        self.c_association = c_association
        self.p_association = p_association
        self.p_all = p_all
        self.b_all = b_all

    def punish_f(self,uavs):
        punish=0
        max_dis=(C.time_slot-C.run_time)*C.speed
        for i in range(len(uavs)):
            dis=tool.calculate_3d_distance_uavs(uavs[i],self.pre_uavs[i])
            if dis>max_dis:
                punish=float('-inf')
        return punish

    # def init_r_c(self):
    #     r_c={}
    #     for i in range(len(self.users)):
    #         r_c[i] = random.randint(1,4)
    #     return r_c
    # 初始化粒子群，k是无人机数量
    def initParticle(self, users):
        X = []
        # users=Users.generate(100)#用户位置
        V = np.random.uniform(self.vlimit[0], self.vlimit[1], (self.N, self.k, 3))
        for i in range(self.N):
            uavs=[]
            uavs_k=tool.getUavInitialPos(self.k,self.users,)
            # tool.getUavInitialPos(self.k, users)
            # uavs_k=self.pre_uavs
            if i==0:
                uavs=tool.getUavInitialPos_fcm(self.k,self.users)
            elif i==1:
                uavs = tool.getUavInitialPos(self.k, self.users)
            else:
                for index in range(self.k):
                    # x,y=generate_random_particles(uavs_k[index],30,1)
                    x, y = generate_random_particles(uavs_k[index],C.v*5, 1)
                    # x=np.random.uniform(self.limit[0],self.limit[1])
                    # y=np.random.uniform(self.limit[0],self.limit[1])
                    # z=np.random.uniform(self.min_height,self.max_height)
                    z=self.min_height
                    uavs.append([x[0],y[0],z])

            X.append(uavs)
        X = np.array(X)
        return X, V

    # 初始化个体和全局最佳历史位置和最佳适应度
    def initBest(self, X,):
        p_pos = X.copy()  # 每个个体的历史最佳位置
        global_pos = np.zeros((self.k, 3))  # 种群的历史最佳位置5*3
        p_best = np.full((self.N, 1), float('-inf'))  # 每个个体的历史最佳适应度
        global_best = float('-inf')  # 种群历史最佳适应度
        return p_pos, global_pos, p_best, global_best


    def pso(self, X, V, update_w,get_fitness,):
        # X, V, users = initParticlePos()
        p_pos, global_pos, p_best, global_best = self.initBest(X)
        result = np.zeros(self.maxgen)

        for iter in range(self.maxgen):
            # fitness = self.getfitness(X, users, k,P)  # 计算所有粒子适应度
            fitness = np.zeros(self.N)
            for i in range(len(X)):
                # f,_,_=get_fitness(self.pre_uavs,X[i],self.users,self.r_c,self.r_p)
                punish=self.punish_f(X[i])
                if punish==float('-inf'):
                    fitness[i]=punish
                else:
                    f, _, _ = get_fitness(self.pre_uavs,X[i],self.users,self.r_c,self.r_p,self.p_all,self.b_all,self.c_association,self.p_association)
                    fitness[i]= f+self.punish_f(X[i])
            for i in range(self.N):
                if fitness[i] > p_best[i]:
                    p_best[i] = fitness[i]  # 更新个体历史最佳适应度
                    p_pos[i] = X[i].copy()  # 更新个体历史最佳位置(取值)
            if max(p_best) > global_best:
                global_best = max(p_best)  # 更新群体历史最佳适应度
                max_index = p_best.argmax()
                global_pos = X[max_index].copy()  # 更新群体历史最佳位置


            # 权重更新
            w = self.wmax - iter * (self.wmax - self.wmin) / self.maxgen
            # w = update_w(self.wmin, self.wmax, iter, self.maxgen, fitness)
            for i in range(self.N):
                # V[i] = self.r * logistic_map(V[i], self.r)
                # w=update_w(self.wmin,self.wmax,iter,self.maxgen,i,fitness)
                # c1=update_c1(iter,self.maxgen)
                # c2=update_c2(iter,self.maxgen)
                V[i] = V[i] * w + self.c1 * random.random() * (p_pos[i] - X[i]) + self.c2 * random.random() * (
                        global_pos - X[i])
                # print(V[i])
                for j in range(len(V[i])):
                    for p in range(len(V[i][j])):
                        if V[i][j][p] > self.vlimit[1]:
                            V[i][j][p] = self.vlimit[1]
                            # V[i][j][p] = self.vlimit[0]+np.random.rand(1)*(self.vlimit[1]-self.vlimit[0])
                        elif V[i][j][p] < self.vlimit[0]:
                            V[i][j][p] = self.vlimit[0]
                # print(V[i])

            # 位置更新
            X = X + V
            # 边界位置处理
            for i in range(self.N):
                for j in range(len(X[i])):
                    for p in range(2):
                        if X[i][j][p] > self.limit[1]:
                            X[i][j][p] = self.limit[1]
                        elif X[i][j][p] < self.limit[0]:
                            X[i][j][p] = self.limit[0]
                    if X[i][j][2] > self.max_height:
                        X[i][j][2] = self.max_height
                    elif X[i][j][2] < self.min_height:
                        X[i][j][2] = self.min_height

            result[iter] = global_best
            print(iter,global_pos,global_best)

        # print(global_best)
        # print(global_pos)

        return global_pos,global_best,result


# import matplotlib.pyplot as plt
#
# def logistic_chaotic_model(x0, r, num_steps):
#     """
#     实现Logistic混沌模型
#
#     参数:
#     - x0: 初始条件
#     - r: 控制参数
#     - num_steps: 模拟的时间步数
#
#     返回:
#     - states: 模拟的状态序列
#     """
#     states = [x0]
#     for _ in range(num_steps - 1):
#         x_next = r * states[-1] * (1 - states[-1])
#         states.append(x_next)
#     return states
#
# def generate_logistic_chaotic_points(num_points):
#     """
#     生成 Logistic 混沌模型的点集
#
#     参数:
#     - num_points: 要生成的点的数量
#
#     返回:
#     - points: 生成的点集 (x, y)
#     """
#     x0 = np.random.random()  # 随机选择初始条件
#     r = 4  # 控制参数，可以根据需要进行调整
#     num_steps = 1000  # 模拟的时间步数
#
#     x_states = logistic_chaotic_model(x0, r, num_steps)
#     y_states = logistic_chaotic_model(x0 + 0.1, r, num_steps)  # 使用略微不同的初始条件
#
#     # 线性映射到[0, 1000]范围
#     x_min, x_max = min(x_states), max(x_states)
#     x_states_mapped = [((x - x_min) / (x_max - x_min)) * 1000 for x in x_states]
#
#     y_min, y_max = min(y_states), max(y_states)
#     y_states_mapped = [((y - y_min) / (y_max - y_min)) * 1000 for y in y_states]
#
#     # 从映射后的状态序列中选择一些点
#     indices = np.linspace(0, num_steps - 1, num_points, dtype=int)
#     points = np.array([(x_states_mapped[i], y_states_mapped[i]) for i in indices])
#
#     return points
#
# def random_points(num):
#     points=[]
#     for i in range(num):
#         x=random.randint(0,1000)
#         y=random.randint(0,1000)
#         points.append(np.array([x,y]))
#     return np.array(points)
#
# # 生成 100 个点
# num_points = 100
# generated_points = generate_logistic_chaotic_points(num_points)
# points2=random_points(num_points)
# # 绘制生成的点
# plt.figure(figsize=(12, 5))
#
# plt.subplot(1, 2, 1)
# plt.scatter(generated_points[:, 0], generated_points[:, 1], color='b', marker='o')
# plt.title('Logistic model')
# plt.xlabel('x')
# plt.ylabel('y')
# # plt.xlim(0, 1000)
# # plt.ylim(0, 1000)
# plt.subplot(1, 2, 2)
# plt.scatter(points2[:, 0], points2[:, 1], color='r', marker='o')
# plt.title('随机生成的100个点')
# plt.xlabel('x')
# plt.ylabel('y')
# plt.xlim(0, 1000)
# plt.ylim(0, 1000)
# plt.show()

import matplotlib.pyplot as plt


def generate_random_particles(center, outer_radius, num_particles):
    # Generate random angles
    angles = np.random.uniform(0, 2 * np.pi, num_particles)

    # Generate random distances within the outer circle
    distances = np.sqrt(np.random.uniform(0, outer_radius ** 2, num_particles))

    # Convert polar coordinates to Cartesian coordinates
    x = center[0] + distances * np.cos(angles)
    y = center[1] + distances * np.sin(angles)

    return x, y


def plot_particles(x, y, center, outer_radius):
    circle = plt.Circle(center, outer_radius, color='r', alpha=0.2, label='Outer Circle')

    plt.scatter(x, y, label='Random Particles', color='blue')
    plt.scatter(center[0], center[1], label='Center', color='red', marker='x')
    plt.gca().add_patch(circle)

    plt.title('Random Particles within Outer Circle')
    plt.xlabel('X-axis')
    plt.ylabel('Y-axis')
    plt.legend()
    plt.show()


# Example usage
# center_point = (0, 0)
# outer_circle_radius = 5
# num_particles = 100
#
# random_x, random_y = generate_random_particles(center_point, outer_circle_radius, num_particles)
# plot_particles(random_x, random_y, center_point, outer_circle_radius)



if __name__ == '__main__':
    uavs=[[868.34535881,315.18340086,50.        ],
          [292.96443022,729.7612657,77.75450997],
 [732.25467955,927.62861821,50.        ],
 [303.40107629,240.31208079,150.        ]]
    # 示例






