/**
 * @file graph.c
 * @brief 图操作模块实现
 *
 * 实现网络拓扑的创建、邻接矩阵打印、文件保存与加载。
 * 第1轮实现：createGraph + printGraph
 */

#define _CRT_SECURE_NO_WARNINGS 1
#include "graph.h"

/* ==================== 全局变量定义 ==================== */
int **parray = NULL;            /* 邻接矩阵（二维动态数组） */
int visited[MAX_DEVICE];        /* 设备访问标记数组 */

/* ==================== 辅助函数：释放图内存 ==================== */

static void freeGraph(ALGraph *graph)
{
    int i;
    CNode *p, *q;

    /* 释放邻接表 */
    for (i = 0; i < graph->deviceNum; i++)
    {
        p = graph->devices[i].first;
        while (p != NULL)
        {
            q = p->next;
            free(p);
            p = q;
        }
        graph->devices[i].first = NULL;
    }

    /* 释放邻接矩阵 */
    if (parray != NULL)
    {
        for (i = 0; i < graph->deviceNum; i++)
        {
            if (parray[i] != NULL)
                free(parray[i]);
        }
        free(parray);
        parray = NULL;
    }

    graph->deviceNum = 0;
    graph->linkNum = 0;
}

/* ==================== createGraph ==================== */

void createGraph(ALGraph *graph)
{
    int i, k;
    int start, end, delay;
    char startName[20], endName[20];
    CNode *node1, *node2;

    /* 如果已有旧图，先释放 */
    if (graph->deviceNum > 0)
        freeGraph(graph);

    /* ====== 8台网络设备 ====== */
    char names[8][20] = {
        "CR", "DR-A", "DR-B", "AR-T",
        "AR-L", "AR-D", "SV", "FW"
    };
    char descs[8][100] = {
        "校园网核心路由器，连接互联网出口",
        "教学区汇聚路由器，承载教学网络流量",
        "生活区汇聚路由器，承载宿舍区网络流量",
        "教学楼接入路由器，连接各教室有线网络",
        "实验楼接入路由器，连接各实验室网络",
        "宿舍区接入路由器，连接各宿舍网络",
        "数据中心服务器集群，托管各类校园服务",
        "防火墙，安全策略控制与入侵检测"
    };
    int ports[8] = { 4, 4, 4, 8, 8, 8, 12, 4 };
    char notes[8][50] = {
        "24h运行", "24h运行", "24h运行", "6:00-22:00运行",
        "6:00-22:00运行", "全天运行", "24h运行", "24h运行"
    };

    /* ====== 10条双向链路：起点 终点 延迟(ms) ====== */
    struct { char from[20]; char to[20]; int w; }
    links[10] = {
        {"CR",   "DR-A",  5},
        {"CR",   "DR-B",  8},
        {"CR",   "FW",    2},
        {"DR-A", "AR-T",  3},
        {"DR-A", "AR-L",  4},
        {"DR-B", "AR-D",  3},
        {"DR-A", "DR-B", 12},
        {"AR-T", "SV",    2},
        {"AR-L", "SV",    3},
        {"AR-D", "SV",   25}
    };

    /* 设置设备数和链路数 */
    graph->deviceNum = 8;
    graph->linkNum = 10;

    /* 初始化顶点信息 */
    for (i = 0; i < graph->deviceNum; i++)
    {
        strcpy(graph->devices[i].name, names[i]);
        strcpy(graph->devices[i].desc, descs[i]);
        graph->devices[i].portCount = ports[i];
        strcpy(graph->devices[i].note, notes[i]);
        graph->devices[i].first = NULL;
    }

    /* 逐条建立双向链路（头插法） */
    for (k = 0; k < graph->linkNum; k++)
    {
        strcpy(startName, links[k].from);
        strcpy(endName, links[k].to);
        delay = links[k].w;

        start = locate(*graph, startName);
        end   = locate(*graph, endName);

        /* 防御性检查：正常情况下不会触发 */
        if (start == -1 || end == -1)
        {
            printf("⚠ 警告：链路 %s-%s 中存在未知设备，已跳过！\n",
                   startName, endName);
            continue;
        }

        /* 创建正向边：start → end */
        node1 = (CNode *)malloc(sizeof(CNode));
        node1->index = end;
        node1->delay = delay;
        node1->next = graph->devices[start].first;
        graph->devices[start].first = node1;

        /* 创建反向边：end → start（无向图双向存储） */
        node2 = (CNode *)malloc(sizeof(CNode));
        node2->index = start;
        node2->delay = delay;
        node2->next = graph->devices[end].first;
        graph->devices[end].first = node2;
    }

    /* 输出创建结果 */
    printf("\n========== 网络拓扑创建成功！==========\n");
    printf("共 %d 台设备，%d 条链路\n\n", graph->deviceNum, graph->linkNum);

    printf("设备列表：\n");
    for (i = 0; i < graph->deviceNum; i++)
    {
        printf("  [%d] %-5s — %s\n", i, graph->devices[i].name, graph->devices[i].desc);
    }

    printf("\n链路列表（双向）：\n");
    printf("  ");
    for (k = 0; k < graph->linkNum; k++)
    {
        printf("%s──%s(%dms)", links[k].from, links[k].to, links[k].w);
        if (k < graph->linkNum - 1)
            printf("    ");
    }
    printf("\n");
    printf("========================================\n\n");
}

/* ==================== printGraph ==================== */

void printGraph(ALGraph *graph)
{
    int i, j;
    CNode *p;

    if (graph->deviceNum <= 0)
    {
        printf("✘ 请先创建网络拓扑图（选项1）！\n");
        return;
    }

    /* 释放旧邻接矩阵（如有） */
    if (parray != NULL)
    {
        for (i = 0; i < graph->deviceNum; i++)
        {
            if (parray[i] != NULL)
                free(parray[i]);
        }
        free(parray);
        parray = NULL;
    }

    /* 分配邻接矩阵 */
    parray = (int **)malloc(sizeof(int *) * graph->deviceNum);
    for (i = 0; i < graph->deviceNum; i++)
    {
        parray[i] = (int *)malloc(sizeof(int) * graph->deviceNum);
    }

    /* 初始化：对角线为0，其他为INF */
    for (i = 0; i < graph->deviceNum; i++)
    {
        for (j = 0; j < graph->deviceNum; j++)
        {
            if (i == j)
                parray[i][j] = 0;
            else
                parray[i][j] = INF;
        }
    }

    /* 从邻接表填充邻接矩阵 */
    for (i = 0; i < graph->deviceNum; i++)
    {
        for (p = graph->devices[i].first; p != NULL; p = p->next)
        {
            j = p->index;
            parray[i][j] = p->delay;
        }
    }

    /* 打印邻接矩阵 */
    printf("\n========== 网络路由表（邻接矩阵，单位：ms）==========\n");
    printf("\t");
    for (i = 0; i < graph->deviceNum; i++)
    {
        printf("%s\t", graph->devices[i].name);
    }
    printf("\n");

    for (i = 0; i < graph->deviceNum; i++)
    {
        printf("%s\t", graph->devices[i].name);
        for (j = 0; j < graph->deviceNum; j++)
        {
            printf("%d\t", parray[i][j]);
        }
        printf("\n");
    }
    printf("=====================================================\n");
    printf("注：32767 表示两设备之间无直连链路（INF）\n\n");
}

/* ==================== saveGraphToFile ==================== */

void saveGraphToFile(ALGraph *graph, const char *filename)
{
    FILE *fp;
    int i, j;
    CNode *p;
    int edgeCount = 0;

    if (graph->deviceNum <= 0)
    {
        printf("✘ 没有拓扑数据可保存！请先创建网络拓扑图。\n");
        return;
    }

    fp = fopen(filename, "w");
    if (fp == NULL)
    {
        printf("✘ 无法打开文件 %s 进行写入！\n", filename);
        return;
    }

    /* 写入设备数和链路数 */
    fprintf(fp, "%d %d\n", graph->deviceNum, graph->linkNum);

    /* 写入每台设备信息：代号 描述 端口数 备注 */
    for (i = 0; i < graph->deviceNum; i++)
    {
        fprintf(fp, "%s %s %d %s\n",
                graph->devices[i].name,
                graph->devices[i].desc,
                graph->devices[i].portCount,
                graph->devices[i].note);
    }

    /* 写入每条链路信息（i < p->index 避免重复） */
    for (i = 0; i < graph->deviceNum; i++)
    {
        for (p = graph->devices[i].first; p != NULL; p = p->next)
        {
            j = p->index;
            if (i < j)
            {
                fprintf(fp, "%s %s %d\n",
                        graph->devices[i].name,
                        graph->devices[j].name,
                        p->delay);
                edgeCount++;
            }
        }
    }

    fclose(fp);
    printf("✔ 拓扑已成功保存到文件：%s（共%d台设备，%d条链路）\n\n",
           filename, graph->deviceNum, edgeCount);
}

/* ==================== loadGraphFromFile ==================== */

void loadGraphFromFile(ALGraph *graph, const char *filename)
{
    FILE *fp;
    int n, m, i, k;
    int start, end, delay;
    char startName[20], endName[20];
    CNode *node1, *node2;

    fp = fopen(filename, "r");
    if (fp == NULL)
    {
        printf("✘ 无法打开文件 %s 进行读取！\n", filename);
        return;
    }

    /* 读取设备数和链路数 */
    if (fscanf(fp, "%d %d", &n, &m) != 2)
    {
        printf("✘ 文件格式错误：无法读取设备数和链路数！\n");
        fclose(fp);
        return;
    }

    /* 校验数据合法性 */
    if (n <= 0 || n > MAX_DEVICE || m < 0 || m > n * (n - 1) / 2)
    {
        printf("✘ 文件数据不合法：设备数=%d，链路数=%d\n", n, m);
        fclose(fp);
        return;
    }

    /* 释放旧图 */
    if (graph->deviceNum > 0)
        freeGraph(graph);

    graph->deviceNum = n;
    graph->linkNum = m;

    /* 读取n行设备信息 */
    for (i = 0; i < n; i++)
    {
        if (fscanf(fp, "%s %s %d %s",
                   graph->devices[i].name,
                   graph->devices[i].desc,
                   &graph->devices[i].portCount,
                   graph->devices[i].note) != 4)
        {
            printf("✘ 文件格式错误：第%d台设备信息读取失败！\n", i + 1);
            fclose(fp);
            graph->deviceNum = 0;
            graph->linkNum = 0;
            return;
        }
        graph->devices[i].first = NULL;
    }

    /* 读取m行链路信息 */
    for (k = 0; k < m; k++)
    {
        if (fscanf(fp, "%s %s %d", startName, endName, &delay) != 3)
        {
            printf("✘ 文件格式错误：第%d条链路信息读取失败！\n", k + 1);
            fclose(fp);
            freeGraph(graph);
            return;
        }

        start = locate(*graph, startName);
        end   = locate(*graph, endName);

        if (start == -1 || end == -1)
        {
            printf("✘ 文件数据错误：链路 %s-%s 中存在未知设备！\n", startName, endName);
            fclose(fp);
            freeGraph(graph);
            return;
        }

        /* 建立双向链路 */
        node1 = (CNode *)malloc(sizeof(CNode));
        node1->index = end;
        node1->delay = delay;
        node1->next = graph->devices[start].first;
        graph->devices[start].first = node1;

        node2 = (CNode *)malloc(sizeof(CNode));
        node2->index = start;
        node2->delay = delay;
        node2->next = graph->devices[end].first;
        graph->devices[end].first = node2;
    }

    fclose(fp);
    printf("✔ 从文件 %s 加载成功！共%d台设备，%d条链路。\n", filename, n, m);
    printf("提示：请选择选项2查看邻接矩阵后再使用路径查询功能。\n\n");
}
