from django.contrib import auth
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework.authentication import BasicAuthentication, TokenAuthentication
from .serializers import UserRegisterSerializer

from django.middleware.csrf import get_token
from django.contrib.auth import login

from api.utils import *
from api.models import Result, Graph, GraphToken
from random import randint

import json, csv

class ViewGraphByToken(APIView):
    # 通过验证码看图——供VR使用
    authentication_classes = []
    permission_classes = []
    def get(self, request):
        print(request.GET)
        requestToken = request.GET.get('token')
        print(requestToken)
        try:
            token = GraphToken.objects.get(token=str(requestToken))
        except GraphToken.DoesNotExist:
            return failed(message="Token不存在")
        if token.checkExpire():
            return failed(message="Token已过期")
        graph = token.graph
        return success(data={
            'nodes': graph.nodes,
            'edges': graph.edges,
        })


class GenerateGraph(APIView):
    # 生成图数据
    def get(self, request):
        user = request.user
        method = request.GET.get('method') # 以何种视图查看
        resultId = request.GET.get('result')
        # 测试用，固定result值，省略计算
        resultId = 57
        # 检查result的传递值是否正确
        try:
            if type(resultId) == int:
                result = Result.objects.get(id=resultId)
            elif type(resultId) == str:
                result = Result.objects.get(id=int(resultId))
            elif type(resultId) == dict:
                if 'id' in resultId:
                    result = Result.objects.get(id=resultId['id'])
                else:
                    return failed(message="输入信息错误")
        except Exception as error:
            return failed(message="输入信息错误", data=error)

        # 图表显示不生成图，仅做统计计算
        if method == 'chart':
            nodeJson = result.nodeFile.toJson()
            edgeJson = result.edgeFile.toJson()
            def isOriginEdge(edge):
                for meta in edge['meta']:
                    if meta.get('optimize') == 'new' or meta.get('predict') == 'new':
                        return False
                return True
            # 需要完善图表视图统计数据，根据具体图表类型决定
            # 通用图数据统计，包括SDI节点数量、边数量，各类型节点度数分布
            # 平均链长度，聚类系数即D节点间连接紧密程度
            # 分别统计SDI节点的数量和度数:sNodes,dNodes,iNodes 
            sDegree = dDegree = iDegree = [0 for i in range(11)] # 度的取值范围0~10,共11个元素
            sNodes = [node for node in nodeJson if node['type'] == 'S']
            for s in sNodes:
                degree = len([edge for edge in edgeJson if edge['from'] == s['id'] or edge['to'] == s['id']])
                if degree <= 10:
                    sDegree[degree] += 1
                else:
                    sDegree[10] += 1
            dNodes = [node for node in nodeJson if node['type'] == 'D']
            for d in dNodes:
                degree = len([edge for edge in edgeJson if edge['from'] == d['id'] or edge['to'] == d['id']])
                if degree <= 10:
                    dDegree[degree] += 1
                else:
                    dDegree[10] += 1
            iNodes = [node for node in nodeJson if node['type'] == 'I']
            for i in iNodes:
                degree = len([edge for edge in edgeJson if edge['from'] == i['id'] or edge['to'] == i['id']])
                if degree <= 10:
                    iDegree[degree] += 1
                else:
                    iDegree[10] += 1
            # 查找所有OODA链路:chains
            chains = []
            for s in sNodes:
                stack = [(s, [s])]  # (当前节点, 路径, 已访问节点)
                while stack:
                    current, path = stack.pop()
                    # 必须限制链路长度，否则无法结束，除限制长度外还可尝试将不同顺序节点的链路视为同一条，从而减少链路数量
                    if len(path) > 5:
                        continue
                    # 终止条件：到达I节点
                    if current['type'] == 'I':
                        chains.append(path)
                        continue
                    
                    # 遍历邻居,注意通用计算只统计原始图
                    neighbors = [edge['to'] for edge in edgeJson if (isOriginEdge(edge) and edge['from'] == current['id'])] + [edge['from'] for edge in edgeJson if (isOriginEdge(edge) and edge['to'] == current['id'])]
                    for neighborId in neighbors:
                        neighbor = [node for node in nodeJson if node.get('id') == neighborId]
                        if len(neighbor) != 1:
                            return failed(message="查找OODA链路时出错:不存在的邻居节点")
                        neighbor = neighbor[0]
                        if neighbor in path: continue  # 避免环路
                        # 不能连线的几种情况
                        if current['type'] == 'S' and neighbor['type'] == 'I':
                            continue
                        if current['type'] == 'D' and neighbor['type'] == 'S':
                            continue
                        stack.append((neighbor, path + [neighbor]))
            chainsNum = len(chains)
            chainsNumByLength = [0 for i in range(5)]
            for chain in chains:
                length = len(chain)
                if length >= 5:
                    length = 5
                chainsNumByLength[length-1] += 1
            averageChainsLength = round(sum([len(chain) for chain in chains]) / chainsNum, 2)
            data = {
                'sNodesNum': len(sNodes),
                'dNodesNum': len(dNodes),
                'iNodesNum': len(iNodes),
                'sDegree': sDegree,
                'dDegree': dDegree,
                'iDegree': iDegree,
                'chains': {
                    'num': chainsNum,
                    'averageLength': averageChainsLength,
                    'numByLength': chainsNumByLength,
                },
            }
            # 根据计算类型的不同分别计算特殊数据
            if result.plan.algorithm.type in ['optimize', 'predict'] :
                # 拓扑优化算法应该统计原有图的数据与优化后图的数据，优化应该仅优化边
                newChains = []
                for s in sNodes:
                    stack = [(s, [s])]  # (当前节点, 路径, 已访问节点)
                    while stack:
                        current, path = stack.pop() 
                        
                        # 终止条件：到达I节点
                        if current['type'] == 'I':
                            newChains.append(path)
                            continue
                        
                        # 遍历邻居,注意优化计算只统计新图
                        neighbors = [edge['to'] for edge in edgeJson if (not isOriginEdge(edge) and edge['from'] == current['id'])] + [edge['from'] for edge in edgeJson if (not isOriginEdge(edge) and edge['to'] == current['id'])]
                        for neighborId in neighbors:
                            neighbor = [node for node in nodeJson if node.get('id') == neighborId]
                            if len(neighbor) != 1:
                                return failed(message="查找OODA链路时出错:不存在的邻居节点")
                            neighbor = neighbor[0]
                            if neighbor in path: continue  # 避免环路
                            # 不能连线的几种情况
                            if current['type'] == 'S' and neighbor['type'] == 'I':
                                continue
                            if current['type'] == 'D' and neighbor['type'] == 'S':
                                continue
                            stack.append((neighbor, path + [neighbor]))
                newChainsNum = len(newChains)
                newAverageChainsLength = 0
                if newChainsNum != 0:
                    newAverageChainsLength = round(sum([len(chain) for chain in newChains]) / newChainsNum, 2)
                data['newChains'] = {
                        'num': newChainsNum,
                        'averageLength': newAverageChainsLength,
                    }                        
            return success(data=data)

        # 如果已经生成过图数据，则直接生成新的验证码
        if result.own_graphs.exists():
            graph = result.own_graphs.first()
            if method == 'web' or method == '3D' or method == '3d':
                return success(data={
                    'nodes': graph.nodes,
                    'edges': graph.edges,
                })
            elif method == 'VR':
                token = graph.generateToken()
                return success(data={
                    'token': token,
                })
        else:
            nodeJson = result.nodeFile.toJson()
            edgeJson = result.edgeFile.toJson()


######测试用，添加几个标签
            for node in nodeJson:
                node['meta'].append({'optimize': 'new'})
                node['meta'].append({'group': randint(1,5)})
                node['meta'].append({'predict': 'new'})
            for edge in edgeJson:
                edge['meta'].append({'optimize': 'new'})
                edge['meta'].append({'predict': 'new'})
########################


            # 两种3D视图的数据结构应该是一样的
            if result.plan.algorithm.type == 'optimize':
                # 拓扑优化算法生成的结果，只有网络结构数据
                # 检查合法性
                for node in nodeJson:
                    if not 'id' in node or not 'type' in node:
                        return failed(message="拓扑优化结果的节点文件存在问题")
                for edge in edgeJson:
                    if not 'from' in edge or not 'to' in edge:
                        return failed(message="边文件存在问题")
                    # 对于优化算法，边的属性中应该有新旧的区分
                    missingLabel = True
                    for meta in edge['meta']:
                        if 'optimize' in meta and meta['optimize'] in ['new', 'old']:
                            missingLabel = False
                    if missingLabel:
                        return failed(message="无拓扑优化标签")
            elif result.plan.algorithm.type == 'group':
                # 功能体探测算法生成的结果
                # 检查合法性
                for node in nodeJson:
                    if not 'id' in node or not 'type' in node:
                        return failed(message="节点文件存在问题")
                    # 对于功能体探测算法，节点的属性中应有功能体的编号
                    missingLabel = True
                    for meta in node['meta']:
                        if 'group' in meta and type(meta['group']) == int:
                            missingLabel = False
                    if missingLabel:
                        return failed(message="无功能体标签")
                for edge in edgeJson:
                    if not 'from' in edge or not 'to' in edge:
                        return failed(message="边文件存在问题")
            elif result.plan.algorithm.type == 'predict':
                # 链路演化预测算法生成的结果
                # 检查合法性
                for node in nodeJson:
                    if not 'id' in node or not 'type' in node:
                        return failed(message="节点文件存在问题")
                    # 对于演化预测算法，节点的属性中应有新旧区分
                    missingLabel = True
                    for meta in node['meta']:
                        if 'predict' in meta and meta['predict'] in ['new', 'old']:
                            missingLabel = False
                    if missingLabel:
                        return failed(message="无演化预测标签")
                for edge in edgeJson:                 
                    if not 'from' in edge or not 'to' in edge:
                        return failed(message="边文件存在问题")
                    # 对于演化预测算法，边的属性中应有新旧区分
                    missingLabel = True
                    for meta in edge['meta']:
                        if 'predict' in meta and meta['predict'] in ['new', 'old']:
                            missingLabel = False
                    if missingLabel:
                        return failed(message="无演化预测标签")
            graph = Graph.objects.createFromResult(result)
            graph.save()
            if method == 'web':
                return success(data={
                    'nodes': graph.nodes,
                    'edges': graph.edges,
                })
            if method == 'VR':
                token = graph.generateToken()
                return success(data={
                    'token': token,
                })

