/* ============================================================
 *  图书管理系统（链表 + 结构体）
 *  功能：增加 / 按书名查询 / 按出版社查询 / 按书名删除 /
 *        按出版社删除 / 显示全部 / 借还登记
 *  编译：gcc book.c -o book
 * ============================================================ */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* ---------- 状态枚举 ---------- */
typedef enum {
    STATUS_RETURNED = 0,   // 已归还（在馆）
    STATUS_BORROWED = 1    // 已借出
} BookStatus;

/* ---------- 图书结构体（链表节点） ---------- */
typedef struct Book {
    char title[50];        // 书名
    char publisher[50];    // 出版社
    char borrow_date[20];  // 借出时间
    char return_date[20];  // 归还时间
    BookStatus status;     // 当前状态
    struct Book *next;     // 下一节点
} Book;

Book *head = NULL;         // 链表头指针

/* ============================================================
 *  输入工具：读一整行并去掉换行
 * ============================================================ */
void read_line(char *buf, size_t size) {
    if (fgets(buf, (int)size, stdin) == NULL) {
        buf[0] = '\0';
        return;
    }
    if (strchr(buf, '\n') == NULL) {           // 输入超长，清掉残留
        int c;
        while ((c = getchar()) != '\n' && c != EOF) { }
    } else {
        buf[strcspn(buf, "\n")] = '\0';        // 去掉换行
    }
}

/* 状态转中文 */
const char *status_text(BookStatus s) {
    return (s == STATUS_BORROWED) ? "借出" : "归还";
}

/* 表头 */
void print_header(void) {
    printf("%-16s %-18s %-12s %-12s %-4s\n",
           "书名", "出版社", "借出时间", "归还时间", "状态");
    printf("--------------------------------------------------------------------\n");
}

/* 打印一行图书信息 */
void print_book(Book *b) {
    printf("%-16s %-18s %-12s %-12s %-4s\n",
           b->title, b->publisher,
           b->borrow_date, b->return_date,
           status_text(b->status));
}

/* ============================================================
 *  1. 增加图书（尾插，保持添加顺序）
 * ============================================================ */
void add_book(void) {
    Book *b = (Book *)malloc(sizeof(Book));
    if (b == NULL) {
        printf("内存分配失败！\n");
        return;
    }

    printf("书名: ");
    read_line(b->title, sizeof(b->title));
    if (b->title[0] == '\0') {
        printf("书名不能为空！\n");
        free(b);
        return;
    }

    printf("出版社: ");
    read_line(b->publisher, sizeof(b->publisher));

    printf("借出时间(如 2024-05-01，可为 -): ");
    read_line(b->borrow_date, sizeof(b->borrow_date));

    printf("归还时间(如 2024-06-01，可为 -): ");
    read_line(b->return_date, sizeof(b->return_date));

    char line[16];
    printf("状态(0=归还/在馆, 1=借出): ");
    read_line(line, sizeof(line));
    b->status = (atoi(line) == 1) ? STATUS_BORROWED : STATUS_RETURNED;

    /* 尾插 */
    b->next = NULL;
    if (head == NULL) {
        head = b;
    } else {
        Book *p = head;
        while (p->next != NULL) p = p->next;
        p->next = b;
    }
    printf("添加成功！\n");
}

/* ============================================================
 *  2. 按书名查询（模糊匹配）
 * ============================================================ */
void search_by_title(void) {
    char key[50];
    printf("输入书名关键字: ");
    read_line(key, sizeof(key));

    int count = 0;
    print_header();
    for (Book *p = head; p != NULL; p = p->next) {
        if (strstr(p->title, key) != NULL) {   // 子串匹配
            print_book(p);
            count++;
        }
    }
    printf("共找到 %d 本图书\n", count);
}

/* ============================================================
 *  3. 按出版社查询（模糊匹配）
 * ============================================================ */
void search_by_publisher(void) {
    char key[50];
    printf("输入出版社关键字: ");
    read_line(key, sizeof(key));

    int count = 0;
    print_header();
    for (Book *p = head; p != NULL; p = p->next) {
        if (strstr(p->publisher, key) != NULL) {
            print_book(p);
            count++;
        }
    }
    printf("共找到 %d 本图书\n", count);
}

/* ============================================================
 *  4. 删除：通用逻辑 + 匹配函数
 *     删除所有匹配节点，返回删除数量
 * ============================================================ */

/* 按书名精确匹配 */
int match_title(Book *b, const char *key) {
    return strcmp(b->title, key) == 0;
}

/* 按出版社精确匹配 */
int match_publisher(Book *b, const char *key) {
    return strcmp(b->publisher, key) == 0;
}

/* 通用删除：遍历链表删除所有匹配项
 * match 是函数指针，决定按书名还是按出版社匹配 */
int delete_books(int (*match)(Book *, const char *), const char *key) {
    Book *prev = NULL;      // 前驱
    Book *cur  = head;      // 当前
    int   count = 0;

    while (cur != NULL) {
        if (match(cur, key)) {
            Book *del = cur;
            if (prev == NULL) {           // 删头节点
                head = cur->next;
            } else {                      // 删中间/尾节点
                prev->next = cur->next;
            }
            cur = cur->next;              // 前进
            free(del);                    // 释放
            count++;
        } else {                          // 不匹配才移动 prev
            prev = cur;
            cur  = cur->next;
        }
    }
    return count;
}

/* 按书名删除 */
void delete_by_title(void) {
    char key[50];
    printf("输入要删除的书名(需完整): ");
    read_line(key, sizeof(key));

    int n = delete_books(match_title, key);
    if (n > 0) printf("已删除 %d 本图书\n", n);
    else       printf("未找到该书\n");
}

/* 按出版社删除 */
void delete_by_publisher(void) {
    char key[50];
    printf("输入要删除的出版社(需完整): ");
    read_line(key, sizeof(key));

    int n = delete_books(match_publisher, key);
    if (n > 0) printf("已删除 %d 本图书\n", n);
    else       printf("未找到该出版社的图书\n");
}

/* ============================================================
 *  5. 显示全部
 * ============================================================ */
void list_books(void) {
    if (head == NULL) {
        printf("暂无图书\n");
        return;
    }
    print_header();
    for (Book *p = head; p != NULL; p = p->next) {
        print_book(p);
    }
}

/* ============================================================
 *  6. 借出 / 归还登记
 * ============================================================ */
void change_status(void) {
    char title[50];
    printf("输入书名: ");
    read_line(title, sizeof(title));

    /* 按书名精确查找 */
    Book *p = head;
    while (p != NULL && strcmp(p->title, title) != 0) p = p->next;
    if (p == NULL) {
        printf("未找到该书\n");
        return;
    }

    char line[16];
    printf("1=借出 0=归还: ");
    read_line(line, sizeof(line));

    if (atoi(line) == 1) {
        p->status = STATUS_BORROWED;
        printf("借出时间: ");
        read_line(p->borrow_date, sizeof(p->borrow_date));
    } else {
        p->status = STATUS_RETURNED;
        printf("归还时间: ");
        read_line(p->return_date, sizeof(p->return_date));
    }
    printf("状态已更新\n");
}

/* ============================================================
 *  7. 释放链表
 * ============================================================ */
void free_all(void) {
    Book *p = head;
    while (p != NULL) {
        Book *next = p->next;   // 先存 next，再 free
        free(p);
        p = next;
    }
    head = NULL;
}

/* ============================================================
 *  主菜单 & main
 * ============================================================ */
void menu(void) {
    printf("\n========== 图书管理系统 ==========\n");
    printf(" 1. 增加图书\n");
    printf(" 2. 按书名查询\n");
    printf(" 3. 按出版社查询\n");
    printf(" 4. 按书名删除\n");
    printf(" 5. 按出版社删除\n");
    printf(" 6. 显示全部\n");
    printf(" 7. 借出/归还登记\n");
    printf(" 0. 退出\n");
    printf("==================================\n");
    printf("请选择: ");
}

int main(void) {
    char line[32];
    while (1) {
        menu();
        read_line(line, sizeof(line));      // 统一按行读
        int choice = atoi(line);

        switch (choice) {
            case 1: add_book();            break;
            case 2: search_by_title();     break;
            case 3: search_by_publisher(); break;
            case 4: delete_by_title();     break;
            case 5: delete_by_publisher(); break;
            case 6: list_books();          break;
            case 7: change_status();       break;
            case 0:
                free_all();
                printf("再见！\n");
                return 0;
            default:
                printf("无效选项，请重新输入\n");
        }
    }
    return 0;
}