#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#define RD_NO       (1<<0)
#define RD_NAME     (1<<1)
#define RD_SEX      (1<<2)
#define RD_SCORE    (1<<3)

//描述1个学生的属性
struct STUDENT
{
    int no;
    char name[64];
    char sex;
    float score;
};
//创建链表的节点数据结构
struct _node
{
    struct STUDENT student;//该节点的数据域
    struct _node* pnext;   //该节点的指针域
};
void GetInfo(struct  STUDENT* p, unsigned char flag);
int Menu(void);
int Link_Add(struct _node* p1);
int ShowAll(struct _node* p1);
int Link_Find(struct _node* p1);
int Link_Change(struct _node* p1);
int Link_Del(struct _node* p1);
//指向头节点的指针变量
struct _node* phead = NULL;
int main()
{
    int no;
    while (1) {
        no = Menu();
        switch (no)
        {
        case 1:Link_Add(phead); break;
        case 2:Link_Del(phead); break;
        case 3:Link_Change(phead); break;
        case 4:Link_Find(phead); break;
        case 5:break;
        case 6:ShowAll(phead); break;
        }
    }
}
/*
菜单
参数:无
返回:输入的菜单选项
*/
int Menu(void)
{
    int no;
p1:
    printf("**学生管理系统**\n");
    printf("1 添加学生     \n");
    printf("2 删除学生     \n");
    printf("3 修改学生     \n");
    printf("4 查找学生     \n");
    printf("5 成绩排序     \n");
    printf("6 打印所有信息  \n");
    printf("请输入选项:");
    scanf("%d", &no);
    if (no >= 1 && no <= 6)
        return no;
    printf("输入有误,重新输入\n");
    goto p1;
}
/*
函数功能:给链表中添加新的新节点
参数:p1 -- 保存链表头的地址
返回值:0 -- 成功
      >1 -- 失败
*/
/*
int Link_Add(struct _node *p1)
{
    //1.开辟新的节点,使用pnew指向该新的节点
    struct _node *pnew = malloc(sizeof(struct _node));
    //2.1给节点的数据域赋值
    GetInfo(&pnew->student,RD_NO|RD_NAME|RD_SEX|RD_SCORE);
    //2.2给节点的指针域赋值
    pnew->pnext = NULL;
    //3.把新的节点添加到链表中去
    //3.1 链表为空
    if(phead == NULL){
        phead = pnew;
        return 0;
    }else//尾插法
    {
        //找到链表尾 -- 根据节点的指针域是否为NULL
        while(p1->pnext != NULL){
            p1 = p1->pnext;
        }
        //把新节点添加到链表最后节点的后面(新的节点地址给上一个最后1个节点的指针域)
        p1->pnext = pnew;
        return 0;
    }
}*/
/*
函数功能:给链表中添加新的新节点
参数:p1 -- 保存链表头的地址
返回值:0 -- 成功
      >1 -- 失败
*/
/*
int Link_Add(struct _node *p1)
{
    //1.开辟新的节点,使用pnew指向该新的节点
    struct _node *pnew = malloc(sizeof(struct _node));
    //2.1给节点的数据域赋值
    GetInfo(&pnew->student,RD_NO|RD_NAME|RD_SEX|RD_SCORE);
    //2.2给节点的指针域赋值
    pnew->pnext = NULL;
    //3.把新的节点添加到链表中去 -- 头插法
    //3.1 原来第一个节点的地址给新节点的指针域
    pnew->pnext = phead;
    phead = pnew;
}*/
/*
函数功能:给链表中添加新的新节点
参数:p1 -- 保存链表头的地址
返回值:0 -- 成功
      >1 -- 失败
*/
int Link_Add(struct _node* p1)
{
    //1.开辟新的节点,使用pnew指向该新的节点
    struct _node* pnew = malloc(sizeof(struct _node));
    //2.1给节点的数据域赋值
    GetInfo(&pnew->student, RD_NO | RD_NAME | RD_SEX | RD_SCORE);
    //2.2给节点的指针域赋值
    pnew->pnext = NULL;
    //3.把新的节点添加到链表中去 -- 中间插入
    //3.1 是否为空链表
    if (phead == NULL) {
        phead = pnew;
        return 0;
    }
    //3.2 查找位置,进行插入
    while (p1 != NULL) {
        if (p1->student.no < pnew->student.no)
        {
            //后续节点为空,直接插入后面
            if (p1->pnext == NULL) {
                p1->pnext = pnew;
                return 0;
            }
            else if (p1->pnext->student.no > pnew->student.no) {
                pnew->pnext = p1->pnext;
                p1->pnext = pnew;
                return 0;
            }
        }
        else if (p1->student.no>pnew->student.no) {
            pnew->pnext = phead;
            phead = pnew;
            return 0;
        }
        p1 = p1->pnext;
    }
}
/*
函数功能:打印链表中所有节点信息
参数:链表头传递进来
返回值:0 -- 成功
      >0 -- 失败
*/
int ShowAll(struct _node* p1)
{
    printf("打印所有学生信息操作……\n");
    printf("学号\t姓名\t性别\t成绩\n");
    while (p1 != NULL) {
        printf("%d\t", p1->student.no);
        printf("%s\t", p1->student.name);
        printf("%c\t", p1->student.sex);
        printf("%f\n", p1->student.score);
        p1 = p1->pnext;
    }
}
/*
从链表中查找某个信息
参数:p1 -- 链表头
返回值:0 -- 成功
      >0 -- 失败
*/
int Link_Find(struct _node* p1)
{
    struct STUDENT temp = { 0 };//保存要查找的信息
    GetInfo(&temp, RD_NO | RD_NAME);
    //循环比较学号
    while (p1 != NULL)
    {
        if (p1->student.no == temp.no) {
            if (strcmp(p1->student.name, temp.name) == 0) {
                printf("找到该学生,信息如下:\n");
                printf("%d\t", p1->student.no);
                printf("%s\t", p1->student.name);
                printf("%c\t", p1->student.sex);
                printf("%f\n", p1->student.score);
                return 0;
            }
            else {
                printf("学号正确,姓名不对\n");
            }
        }
        p1 = p1->pnext;
    }
    printf("查无此人\n");
    return 1;
}
/*
修改链表节点的内容
参数:p1 -- 指向链表头
返回值:0 -- 正确
      >0 -- 错误
*/
int Link_Change(struct _node* p1)
{
    struct STUDENT temp = { 0 };
    GetInfo(&temp, RD_NO | RD_NAME);
    while (p1 != NULL) {
        if (p1->student.no == temp.no) {
            if (strcmp(p1->student.name, temp.name) == 0) {
                GetInfo(&temp, RD_NO | RD_NAME | RD_SEX | RD_SCORE);
                p1->student = temp;
                return 0;
            }
        }
        p1 = p1->pnext;
    }
    printf("查无此人\n");
    return 1;
}
/*
删除链表中某个节点
p1 -- 链表头
返回值: 0 -- 成功
      >0 -- 失败
       1 -- 链表为空
       2 -- 链表中查询不到
*/
int Link_Del(struct _node* p1)
{
    if (p1 == NULL) {//空链表
        return 1;//链表为空
    }
    struct STUDENT temp = { 0 };
    GetInfo(&temp, RD_NO);
    struct _node* pdel = NULL;//保存要删除节点的首地址
    //1.判断是否是第一个节点
    if (p1->student.no == temp.no) {
        pdel = p1;
        phead = pdel->pnext;//要删除的节点的指针域给链表头指针
        free(pdel);
        return 0;
    }//第2个节点开始
    while (p1->pnext != NULL) {//代表下一个节点空间是否存在
        if (p1->pnext->student.no == temp.no) {
            pdel = p1->pnext;
            p1->pnext = pdel->pnext;
            free(pdel);
            return 0;
        }
        p1 = p1->pnext;
    }
    return 2;
}
/*
flag = 相应的选项是否允许输入
[0]位 -- no_flag    1--允许输入 0 -- 不允许输入
[1]位 -- name_flag  1--允许输入 0 -- 不允许输入
[2]位 -- sex_flag   1--允许输入 0 -- 不允许输入
[3]位 -- score_flag 1--允许输入 0 -- 不允许输入
*/
void GetInfo(struct  STUDENT* p, unsigned char flag)
{
    if (flag & (1 << 0)) {
        printf("请输入学号:");
        scanf("%d", &p->no);
    }
    if (flag & (1 << 1)) {
        printf("请输入姓名:");
        scanf("%s", p->name);
    }
    if (flag & (1 << 2)) {
        printf("请输入性别:");
        getchar();
        scanf("%c", &p->sex);
    }
    if (flag & (1 << 3)) {
        printf("请输入成绩:");
        scanf("%f", &p->score);
    }
}