/*
 * @Author: Student author@example.com
 * @Date: 2025-01-02 12:20:41
 * @LastEditors: Student author@example.com
 * @LastEditTime: 2025-01-03 19:04:34
 * @FilePath: \LibraryManageSystem\src\Book.cpp
 * @Description: Coding with UTF-8
 *
 * Copyright (c) 2025 by Student, All Rights Reserved.
 */
#include "../include/Book.h"
#include <ctime>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>

/**
 * @brief Book类构造函数
 * @param id 图书唯一标识符
 * @param title 图书标题
 * @param author 作者
 * @param library 所在图书馆
 * @param category 图书分类
 * @param desc 图书描述
 * @param stock 初始库存数量
 */
Book::Book(const std::string &id, const std::string &title, const std::string &author,
           const std::string &library, BookCategory category, const std::string &desc,
           int stock)
    : bookId(id), title(title), author(author), library(library), // 初始化基本信息
      category(category), description(desc), currentStock(stock), // 初始化分类和库存
      totalStock(stock), averageRating(0)                         // 初始化总库存和评分
{
    // 构造函数实现
}

/**
 * @brief 图书借阅处理
 * @param userPhone 借阅者手机号
 * @return 借阅成功返回true，失败返回false
 * @note 会检查库存和用户是否重复借阅
 */
bool Book::borrow(const std::string &userPhone)
{
    if (currentStock > 0)
    {
        // 检查该用户是否已经借阅过这本书
        for (int i = 1; i <= borrowerPhones.length(); i++)
        {
            std::string storedPhone = std::to_string(borrowerPhones.getPointer(i)->data);
            if (storedPhone == userPhone)
            {
                return false; // 已经借过这本书
            }
        }

        currentStock--;
        // 存储借阅者手机号（转换为整数存储）
        try
        {
            borrowerPhones.addNodeToEnd(std::stoll(userPhone));
        }
        catch (...)
        {
            currentStock++; // 恢复库存
            return false;
        }
        // 存储借阅时间（使用时间戳）
        borrowDates.addNodeToEnd(std::time(nullptr));
        return true;
    }
    return false;
}
/**
 * @brief 图书归还处理
 * @param userPhone 借阅者手机号
 * @return 归还成功返回true，失败返回false
 */
bool Book::returnBook(const std::string &userPhone)
{
    for (int i = 1; i <= borrowerPhones.length(); i++) // 遍历借阅者列表
    {
        if (std::to_string(borrowerPhones.getPointer(i)->data) == userPhone) // 找到借阅记录
        {
            borrowerPhones.deleteNode(i); // 删除借阅者记录
            borrowDates.deleteNode(i);    // 删除借阅时间记录
            currentStock++;               // 增加库存
            return true;
        }
    }
    return false;
}

/**
 * @brief 检查图书是否逾期
 * @param userPhone 借阅者手机号
 * @param maxDays 最大借阅天数
 * @return 已逾期返回true，未逾期返回false
 */
bool Book::isOverdue(const std::string &userPhone, int maxDays) const
{
    for (int i = 1; i <= borrowerPhones.length(); i++) // 遍历借阅者列表
    {
        if (std::to_string(borrowerPhones.getPointer(i)->data) == userPhone) // 找到借阅记录
        {
            time_t borrowDate = borrowDates.getPointer(i)->data; // 获取借阅时间
            time_t now = std::time(nullptr);                     // 获取当前时间
            return (now - borrowDate) > (maxDays * 24 * 3600);   // 检查是否超期
        }
    }
    return false; // 未找到借阅记录
}
/**
 * @brief 检查图书是否可借阅
 * @return 如果当前库存大于0返回true，否则返回false
 */
bool Book::isAvailable() const
{
    return currentStock > 0;
}

/**
 * @brief 更新图书信息
 * @param newTitle 新的书名
 * @param newAuthor 新的作者
 * @param newLibrary 新的所在图书馆
 * @param newDesc 新的图书描述
 * @note 如果参数为空字符串则保持原值不变
 */
void Book::updateInfo(const std::string &newTitle, const std::string &newAuthor,
                      const std::string &newLibrary, const std::string &newDesc)
{
    if (!newTitle.empty())
        title = newTitle;
    if (!newAuthor.empty())
        author = newAuthor;
    if (!newLibrary.empty())
        library = newLibrary;
    if (!newDesc.empty())
        description = newDesc;
}

/**
 * @brief 更新图书库存数量
 * @param newStock 新的库存数量
 * @note 库存数量不能为负数
 */
void Book::updateStock(int newStock)
{
    if (newStock >= 0)
    {
        currentStock = newStock;
    }
}
/**
 * @brief 添加图书评分
 * @param rating 评分值(1-5)
 * @note 评分必须在1-5之间，添加后会自动重新计算平均分
 */
void Book::addRating(int rating)
{
    if (rating < 1 || rating > 5) // 验证评分范围
        return;

    ratings.addNodeToEnd(rating); // 添加新评分
    calculateAverageRating();     // 重新计算平均分
}

/**
 * @brief 计算图书平均评分
 * @note 如果没有评分记录则平均分为0
 */
void Book::calculateAverageRating()
{
    if (ratings.length() == 0) // 检查是否有评分
    {
        averageRating = 0;
        return;
    }

    double sum = 0;
    for (int i = 1; i <= ratings.length(); i++) // 计算评分总和
    {
        sum += ratings.getPointer(i)->data;
    }
    averageRating = sum / ratings.length(); // 计算平均分
}

/**
 * @brief BookManager类构造函数
 * @param filename 数据文件路径
 * @note 初始化时会自动从文件加载图书数据
 */
BookManager::BookManager(const std::string &filename) : dataFile(filename)
{
    loadFromFile();
}

BookManager::~BookManager()
{
    saveToFile();
}

bool BookManager::addBook(Book *book)
{
    if (!findBook(book->getBookId()))
    {
        books.addNodeToEnd(reinterpret_cast<intptr_t>(book));
        return true;
    }
    return false;
}

/**
 * @brief 根据图书ID查找图书
 * @param bookId 要查找的图书ID
 * @return 如果找到返回Book指针,否则返回nullptr
 * @note 遍历books链表进行查找
 */
Book *BookManager::findBook(const std::string &bookId) const
{
    for (int i = 1; i <= books.length(); i++)
    {
        Book *book = reinterpret_cast<Book *>(books.getPointer(i)->data);
        if (book->getBookId() == bookId)
        {
            return book;
        }
    }
    return nullptr;
}

void BookManager::displayAllBooks() const
{
    for (int i = 1; i <= books.length(); i++)
    {
        Book *book = reinterpret_cast<Book *>(books.getPointer(i)->data);
        std::cout << "书号: " << book->getBookId()
                  << ", 书名: " << book->getTitle()
                  << ", 作者: " << book->getAuthor()
                  << ", 馆藏地: " << book->getLibrary()
                  << ", 在库数量: " << book->getCurrentStock()
                  << "/" << book->getTotalStock() << std::endl;
    }
}

/**
 * @brief 将图书数据保存到文件
 * @return 保存成功返回true，失败返回false
 * @note 使用CSV格式存储，每本书占一行
 */
bool BookManager::saveToFile() const
{
    // 先清空文件，然后重新写入所有数据
    std::ofstream file(dataFile, std::ios::trunc); // 使用 trunc 模式打开文件
    if (!file.is_open())
        return false;

    // 写入当前内存中的所有图书数据
    for (int i = 1; i <= books.length(); i++)
    {
        Book *book = reinterpret_cast<Book *>(books.getPointer(i)->data);
        file << book->getBookId() << ","
             << book->getTitle() << ","
             << book->getAuthor() << ","
             << book->getLibrary() << ","
             << book->getDescription() << ","
             << book->getTotalStock() << ","
             << book->getCurrentStock() << ","
             << book->getBorrowCount() << ","
             << book->getAverageRating() << ","
             << book->getRatingCount() << "\n";
    }
    return true;
}

/**
 * @brief 从文件加载图书数据
 * @return 加载成功返回true，失败返回false
 * @note 会清空当前内存中的数据，完全以文件内容为准
 */
bool BookManager::loadFromFile()
{
    std::ifstream file(dataFile);
    if (!file.is_open())
        return false;

    std::string line;
    while (std::getline(file, line))
    {
        std::stringstream ss(line);
        std::string id, title, author, library, desc;
        std::string totalStock, currentStock, borrowCount, avgRating, ratingCount;

        std::getline(ss, id, ',');
        std::getline(ss, title, ',');
        std::getline(ss, author, ',');
        std::getline(ss, library, ',');
        std::getline(ss, desc, ',');
        std::getline(ss, totalStock, ',');
        std::getline(ss, currentStock, ',');
        std::getline(ss, borrowCount, ',');
        std::getline(ss, avgRating, ',');
        std::getline(ss, ratingCount);

        Book *book = new Book(id, title, author, library, BookCategory::OTHER, desc, std::stoi(totalStock));
        book->updateStock(std::stoi(currentStock));
        books.addNodeToEnd(reinterpret_cast<intptr_t>(book));
    }
    return true;
}

/**
 * @brief 从系统中移除指定图书
 * @param bookId 要移除的图书ID
 * @return 移除成功返回true，图书不存在返回false
 * @note 会释放图书对象占用的内存并从链表中删除对应节点
 */
bool BookManager::removeBook(const std::string &bookId)
{
    for (int i = 1; i <= books.length(); i++)
    {
        Book *book = reinterpret_cast<Book *>(books.getPointer(i)->data);
        if (book->getBookId() == bookId)
        {
            delete book;
            books.deleteNode(i);
            return true;
        }
    }
    return false;
}

/**
 * @brief 搜索图书
 * @param keyword 搜索关键词
 * @return 包含匹配图书的链表
 * @note 支持按书名和作者名搜索
 */
LinkList BookManager::searchBooks(const std::string &keyword) const
{
    LinkList results;
    for (int i = 1; i <= books.length(); i++)
    {
        Book *book = reinterpret_cast<Book *>(books.getPointer(i)->data);
        if (book->getTitle().find(keyword) != std::string::npos ||
            book->getAuthor().find(keyword) != std::string::npos)
        {
            results.addNodeToEnd(reinterpret_cast<intptr_t>(book));
        }
    }
    return results;
}

/**
 * @brief 处理图书借阅请求
 * @param bookId 要借阅的图书ID
 * @param userPhone 借阅者手机号
 * @return 借阅成功返回true，失败返回false
 * @note 借阅成功后会自动保存更新到文件
 */
bool BookManager::borrowBook(const std::string &bookId, const std::string &userPhone)
{
    Book *book = findBook(bookId);
    if (book && book->borrow(userPhone))
    {
        saveToFile(); // 确保保存时只更新 currentStock
        return true;
    }
    return false;
}

/**
 * @brief 处理图书归还请求
 * @param bookId 要归还的图书ID
 * @param userPhone 归还者手机号
 * @return 归还成功返回true，失败返回false
 * @note 归还成功后会自动保存更新到文件
 */
bool BookManager::returnBook(const std::string &bookId, const std::string &userPhone)
{
    Book *book = findBook(bookId);
    if (book && book->returnBook(userPhone))
    {
        saveToFile(); // 确保保存时只更新 currentStock
        return true;
    }
    return false;
}

/**
 * @brief 使用二分查找来查找图书
 * @param bookId 要查找的图书ID
 * @return 如果找到返回Book指针,否则返回nullptr
 * @note 这是一个冗余实现，需要先对books按ID排序
 */
Book *BookManager::findBookBinary(const std::string &bookId) const
{
    // 创建一个临时数组来存储排序后的图书指针
    // 创建临时数组存储排序后的图书指针
    int length = books.length();
    Book **sortedBooks = new Book *[length];
    for (int i = 1; i <= length; i++)
    {
        sortedBooks[i - 1] = reinterpret_cast<Book *>(books.getPointer(i)->data);
    }

    // 冒泡排序按bookId排序
    for (int i = 0; i < length - 1; i++)
    {
        for (int j = 0; j < length - i - 1; j++)
        {
            if (sortedBooks[j]->getBookId() > sortedBooks[j + 1]->getBookId())
            {
                Book *temp = sortedBooks[j];
                sortedBooks[j] = sortedBooks[j + 1];
                sortedBooks[j + 1] = temp;
            }
        }
    }

    // 二分查找
    int left = 0;
    int right = length - 1;
    Book *result = nullptr;

    while (left <= right)
    {
        int mid = left + (right - left) / 2;
        std::string midId = sortedBooks[mid]->getBookId();

        if (midId == bookId)
        {
            result = sortedBooks[mid];
            break;
        }

        if (midId < bookId)
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }

    // 释放临时数组
    delete[] sortedBooks;
    return result;
}

/**
 * @brief 计算字符串的BKDR哈希值
 * @param str 要计算哈希值的字符串
 * @return 哈希值
 * @note BKDR哈希算法是一种经典的字符串哈希算法,具有以下特点:
 *       1. 计算速度快,只需要遍历一次字符串
 *       2. 冲突概率较低,通过选择合适的seed值可以进一步降低冲突
 *       3. 分布均匀,生成的哈希值在整个取值范围内分布相对均匀
 *       4. 雪崩效应好,输入的微小变化会导致输出的显著变化
 */
unsigned int BookManager::BKDRHash(const std::string &str) const
{
    // 种子数,可选用31,131,1313等质数,质数作为种子可以让哈希值分布更均匀
    unsigned int seed = 131;
    // 初始化哈希值
    unsigned int hash = 0;

    // 对字符串中的每个字符进行处理
    for (char c : str)
    {
        // 哈希计算公式: hash = hash * seed + 当前字符
        // 这样可以保证字符串中每个位置的字符都对最终哈希值有贡献
        hash = hash * seed + c;
    }

    return hash;
}

/**
 * @brief 使用哈希表查找图书
 * @param title 书名
 * @return 如果找到返回Book指针,否则返回nullptr
 * @note 这是一个基于哈希的冗余实现,使用拉链法处理哈希冲突
 *       时间复杂度:
 *       - 平均情况: O(1)
 *       - 最坏情况: O(n) (当所有元素都哈希到同一个桶时)
 *       空间复杂度: O(n)
 */
Book *BookManager::findBookByTitleHash(const std::string &title) const
{
    // 定义哈希表大小为1024,这是一个权衡值:
    // - 太小会增加冲突概率
    // - 太大会浪费内存空间
    const int TABLE_SIZE = 1024;
    Book **hashTable[TABLE_SIZE];         // 哈希表,每个位置存储一个动态数组(桶)
    int hashSizes[TABLE_SIZE] = {0};      // 记录每个桶当前存储的元素数量
    int hashCapacities[TABLE_SIZE] = {0}; // 记录每个桶的当前容量

    // 初始化哈希表,将所有桶指针设为nullptr
    for (int i = 0; i < TABLE_SIZE; i++)
    {
        hashTable[i] = nullptr;
    }

    // 将所有图书插入哈希表
    for (int i = 1; i <= books.length(); i++)
    {
        // 获取当前图书指针
        Book *book = reinterpret_cast<Book *>(books.getPointer(i)->data);
        // 计算哈希值并取模,确保在表大小范围内
        unsigned int hash = BKDRHash(book->getTitle()) % TABLE_SIZE;

        // 如果当前桶已满或未分配,需要分配或扩展空间
        if (hashSizes[hash] >= hashCapacities[hash])
        {
            // 使用倍增策略扩展容量,初始容量为4
            int newCapacity = hashCapacities[hash] == 0 ? 4 : hashCapacities[hash] * 2;
            Book **newBucket = new Book *[newCapacity];

            // 将原有数据复制到新空间
            for (int j = 0; j < hashSizes[hash]; j++)
            {
                newBucket[j] = hashTable[hash][j];
            }

            // 释放旧空间并更新指针和容量
            delete[] hashTable[hash];
            hashTable[hash] = newBucket;
            hashCapacities[hash] = newCapacity;
        }

        // 将新元素添加到桶中
        hashTable[hash][hashSizes[hash]++] = book;
    }

    // 计算要查找的书名的哈希值
    unsigned int hash = BKDRHash(title) % TABLE_SIZE;
    Book *result = nullptr;

    // 在对应的桶中查找匹配的图书
    if (hashTable[hash] != nullptr)
    {
        for (int i = 0; i < hashSizes[hash]; i++)
        {
            // 找到完全匹配的书名
            if (hashTable[hash][i]->getTitle() == title)
            {
                result = hashTable[hash][i];
                break;
            }
        }
    }

    // 清理所有动态分配的内存,防止内存泄漏
    for (int i = 0; i < TABLE_SIZE; i++)
    {
        delete[] hashTable[i];
    }

    return result;
}
