#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 *prev;    //该节点的指针域 -- 保存上一个节点
    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;
    pnew->prev = NULL;
    //3.把新的节点添加到链表中去 -- 中间插入
    //3.1 是否为空链表
    if(phead == NULL){
        phead = pnew;//直接放到第一个节点位置
        return 0;
    }
    //3.2 查找位置,进行插入
    while(p1 != NULL){
        if(pnew->student.no < phead->student.no)//上来比第一个节点数据小
        {
            phead->prev = pnew;
            pnew->pnext = phead;
            phead = pnew;
            return 0;
        }
        if(pnew->student.no > p1->student.no){//中间
            if(p1->pnext == NULL)//链表末尾
            {
                pnew->prev = p1;
                p1->pnext = pnew;
            }else if(pnew->student.no < p1->pnext->student.no)//中间
            {   //修改插入节点的指针域
                pnew->prev = p1;
                pnew->pnext = p1->pnext;
                //修改下一个节点(prev)
                p1->pnext->prev = pnew;
                //修改上一个节点(pnext)
                p1->pnext = 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;//保存要删除节点的首地址
    while(p1 != NULL){
        if(p1->student.no == temp.no)
        {
            pdel = p1;//pdel保存要删除节点的首地址
            if(pdel->prev == NULL && pdel->prev == NULL)
            {
                phead = NULL;
                free(pdel);
                return 0;
            }
            //前一个节点的pnext变化
            if(pdel->prev != NULL)//中间
                pdel->prev->pnext = pdel->pnext;
            else//代表头节点
            {
                pdel->pnext->prev = NULL;//下一个节点的prev为空
                phead = pdel->pnext;     //下一个节点作为了链表头
            }
            //后一个节点的prev变化
            if(pdel->pnext != NULL)
                pdel->pnext->prev = pdel->prev;
            else{
                //上一给节点pnext变为NULL -- 作为链表尾
                pdel->prev->pnext = NULL;
            }
            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);
    }
}