#include "misc/mapped_file/mapped_file.h"

#include <stdexcept>
#include <windows.h>

class mapped_file_win : public mapped_file {
public:
    mapped_file_win();

    ~mapped_file_win() override;

    // 禁用拷贝
    mapped_file_win(const mapped_file_win&) = delete;
    mapped_file_win& operator=(const mapped_file_win&) = delete;

    // 允许移动
    mapped_file_win(mapped_file_win&& other) noexcept;
    mapped_file_win& operator=(mapped_file_win&& other) noexcept;

    bool map_file(const std::filesystem::path& filename) override;
    void unmap() override;

    [[nodiscard]] const void* get_data() const override { return data_; }
    [[nodiscard]] void*       get_data() override { return data_; }
    [[nodiscard]] size_t      get_size() const override { return size_; }
    [[nodiscard]] bool        is_mapped() const override { return data_ != nullptr; }
private:
    void cleanup();

    void* data_;
    size_t size_;
    HANDLE file_handle_;
    HANDLE mapping_handle_;
};

mapped_file_win::mapped_file_win() :
	mapped_file(),
	data_(nullptr),
	size_(0),
	file_handle_(INVALID_HANDLE_VALUE),
	mapping_handle_(nullptr) {}

mapped_file_win::~mapped_file_win() {
    cleanup();
}

mapped_file_win::mapped_file_win(mapped_file_win&& other) noexcept
    : data_(other.data_), size_(other.size_), file_handle_(other.file_handle_), mapping_handle_(other.mapping_handle_) {
    other.data_ = nullptr;
    other.size_ = 0;
    other.file_handle_ = INVALID_HANDLE_VALUE;
    other.mapping_handle_ = nullptr;
}

mapped_file_win& mapped_file_win::operator=(mapped_file_win&& other) noexcept {
    if (this == &other)
        return *this;
    cleanup();
    data_ = other.data_;
    size_ = other.size_;
    file_handle_ = other.file_handle_;
    mapping_handle_ = other.mapping_handle_;

    other.data_ = nullptr;
    other.size_ = 0;
    other.file_handle_ = INVALID_HANDLE_VALUE;
    other.mapping_handle_ = nullptr;
    return *this;
}

bool mapped_file_win::map_file(const std::filesystem::path& filename) {
    cleanup();

    file_handle_ = CreateFileW(
        filename.c_str(),
        GENERIC_READ,
        FILE_SHARE_READ,
        nullptr,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        nullptr
    );

    if (file_handle_ == INVALID_HANDLE_VALUE) {
    	// 无法打开文件
        throw std::runtime_error("Failed to open file");
    }

    LARGE_INTEGER file_size;
    if (!GetFileSizeEx(file_handle_, &file_size)) {
        cleanup();
    	// 无法获取文件大小
        throw std::runtime_error("Failed to get file size");
    }
    size_ = static_cast<size_t>(file_size.QuadPart);

    mapping_handle_ = CreateFileMappingW(
        file_handle_,
        nullptr,
        PAGE_READONLY,
        0,
        0,
        nullptr
    );

    if (mapping_handle_ == nullptr) {
        cleanup();
    	// 无法创建文件映射
        throw std::runtime_error("Failed to create file mapping");
    }

    data_ = MapViewOfFile(
        mapping_handle_,
        FILE_MAP_READ,
        0,
        0,
        0
    );

    if (data_ == nullptr) {
        cleanup();
    	// 无法映射文件视图
        throw std::runtime_error("Failed to map view of file");
    }

    return true;
}

void mapped_file_win::cleanup() {
    if (data_) {
        UnmapViewOfFile(data_);
        data_ = nullptr;
    }
    if (mapping_handle_) {
        CloseHandle(mapping_handle_);
        mapping_handle_ = nullptr;
    }
    if (file_handle_ != INVALID_HANDLE_VALUE) {
        CloseHandle(file_handle_);
        file_handle_ = INVALID_HANDLE_VALUE;
    }
    size_ = 0;
}

void mapped_file_win::unmap() {
    cleanup();
}

std::unique_ptr<mapped_file> mapped_file::create() {
	return std::make_unique<mapped_file_win>();
}
