/*
     Description: 排序过程的第二阶段
        * 第二阶段（phase-2）：
        * 1. 打开所有中间文件，并关联上解压线程池
        * 2. phase-2-stage-1：解压中间文件，并进行归并排序，放入buf
        * 3. phase-2-stage-2：多线程进行压缩，放入buf
        * 4. phase-2-stage-3：将buf写入最终的bam文件
        * 5. 要注意计算index并写入index文件
     Copyright : All right reserved by ICT

     Author : Zhang Zhonghai
     Date : 2026/02/08
*/

#pragma once
#include <spdlog/spdlog.h>
#include <stdio.h>

#include <string>

#include "phase_1.h"
#include "sam_io.h"
#include "sort.h"
using std::string;

extern uint64_t block_id;
extern uint64_t read_bam_id;
extern uint64_t copy_bam_id;
extern uint64_t write_bam_id;

// 循环缓冲区
struct CircularBuffer {
    uint8_t* data = nullptr;
    size_t readIdx = 0;  // 可以读取的开始位置
    size_t writeIdx = 0;  // 可以写入的开始位置
    size_t valueSize = 0; // 有效字节
    size_t bufSize = 0;  // 缓冲区空间
    size_t skipped = 0; // 跳过的字节数

    CircularBuffer() {}
    CircularBuffer(size_t initSize) {
        bufSize = initSize;
        data = (uint8_t*)malloc(bufSize);
    }
    ~CircularBuffer() {
        if (data) {
            free(data);
            data = nullptr;
        }
        readIdx = 0;
        writeIdx = 0;
        valueSize = 0;
        bufSize = 0;
        skipped = 0;
    }
    void AllocMem(size_t memSize) { ReAllocMem(memSize); }

    void ReAllocMem(size_t memSize) {
        if (memSize > bufSize) {
            bufSize = memSize;
            data = (uint8_t*)realloc(data, bufSize);
        }
    }

    size_t Write(uint8_t* src, size_t len) {
        if (len == 0) return 0;
        size_t freeSpace = bufSize - valueSize;
        size_t toWrite = MIN(len, freeSpace);
        if (toWrite == 0) return 0;

        size_t firstPart = MIN(toWrite, bufSize - writeIdx);
        // 第一段：从 write_index_ 到末尾
        memcpy(data + writeIdx, src, firstPart);
        // 第二段：如果超过了末尾，则从头部继续
        if (toWrite > firstPart) {
            memcpy(data, src + firstPart, toWrite - firstPart);
        }
        writeIdx = (writeIdx + toWrite) % bufSize;
        valueSize += toWrite;
        return toWrite;
    }

    size_t Read(uint8_t* out, size_t len) {
        if (len == 0) return 0;
        size_t toRead = MIN(len, valueSize);
        if (toRead == 0) return 0;

        size_t firstPart = MIN(toRead, bufSize - readIdx);
        memcpy(out, data + readIdx, firstPart);
        if (toRead > firstPart) {
            std::memcpy(out + firstPart, data, toRead - firstPart);
        }
        readIdx = (readIdx + toRead) % bufSize;
        valueSize -= toRead;
        return toRead;
    }

    // 返回第一个连续空间的大小
    size_t FirstPartWriteSize() {
        size_t freeSpace = bufSize - valueSize - skipped;
        size_t firstPart = MIN(freeSpace, bufSize - writeIdx);
        return firstPart;
    }

    // 如果空间不连续，那么返回第二个连续空间的内存大小
    size_t SecondPartWriteSize() {
        size_t freeSpace = bufSize - valueSize - skipped;
        size_t firstPart = MIN(freeSpace, bufSize - writeIdx);
        if (firstPart == freeSpace)
            return 0;
        return freeSpace - firstPart;
    }

    // 跳过不能完整保存一个bam的空间
    void SkipWrite(size_t skipBytes) {
        // valueSize += skipBytes;
        skipped += skipBytes;
        writeIdx = (writeIdx + skipBytes) % bufSize;
    }

    void SkipRead(size_t from, size_t skipBytes) {
        if (valueSize <= skipBytes) { // 直接清空
            readIdx = 0;      // 可以读取的开始位置
            writeIdx = 0;     // 可以写入的开始位置
            valueSize = 0;    // 有效字节
            skipped = 0;      // 跳过的字节数
        } else {
            if (from == readIdx) {
                readIdx = (readIdx + skipBytes) % bufSize;
                valueSize -= skipBytes;
            } else {
                valueSize -= skipBytes;
                readIdx = skipBytes;
            }
        }
        //if (valueSize > 1024 * 1024 * 1024) {
         //   spdlog::info("debug valuesize");
        //}
    }

    // 退回一个bam
    void RevertRead(size_t from, size_t revertBytes) {
        readIdx = from - revertBytes;
        valueSize += revertBytes;
    }

    // 读取一个bam
    size_t ReadBam(uint8_t* out, size_t start, size_t len) {
        memcpy(out, data + start, len);
        valueSize -= len;
        readIdx = start + len;
        return len;
    }

    // 是否为空
    bool Empty() const { return valueSize == 0; }
    // 是否已满
    bool Full() const { return valueSize == bufSize; }
    // 当前数据量（字节）
    size_t Size() const { return valueSize; }
    // 剩余可写空间
    size_t Free() const { return bufSize - valueSize; }
    // 总容量
    size_t Capacity() const { return bufSize; }

    void Clear() {
        readIdx = 0;    // 可以读取的开始位置
        writeIdx = 0;   // 可以写入的开始位置
        valueSize = 0;  // 有效字节
        skipped = 0;    // 跳过的字节数
    }
};

// 循环数组
template<class T>
struct CircularArray {
    std::vector<T> arr;
    size_t readIdx = 0;    // 可以读取的开始位置
    size_t writeIdx = 0;   // 可以写入的开始位置
    size_t valueSize = 0;  // 有效字节
    size_t bufSize = 0;    // 缓冲区空间

    CircularArray() {}
    CircularArray(size_t initSize) {
        bufSize = initSize;
        arr.resize(initSize);
    }
    void AllocArr(size_t arrSize) { ReAllocArr(arrSize); }

    void ReAllocArr(size_t arrSize) {
        if (arrSize > bufSize) {
            bufSize = arrSize;
            arr.resize(arrSize);
        }
    }

    inline T& operator[](size_t idx) {
        return arr[(idx + readIdx) % bufSize];
    }

    // 推入元素（自动扩容）
    void Push(const T& value) {
        if (Full()) {
            ReAllocArr(bufSize * 1.5); // 这是bug的来源，不能扩展，或者把原来的数据搬运到后面的空间，否则writeIdx对应的位置和ReadIdx冲突
        }
        arr[writeIdx] = value;
        writeIdx = (writeIdx + 1) % bufSize;
        ++valueSize;
    }

    void Push(T&& value) {
        if (Full()) {
            ReAllocArr(bufSize * 1.5);
        }
        arr[writeIdx] = std::move(value);
        writeIdx = (writeIdx + 1) % bufSize;
        ++valueSize;
    }

    // 弹出头部元素（如果为空则返回 nullopt）
    void Pop() {
        if (Empty())
            return;
        --valueSize;
        readIdx = (readIdx + 1) % bufSize;
    }

    void Revert() {
        ++valueSize;
        readIdx = (readIdx + bufSize - 1) % bufSize;
    }

    // 查看头部/尾部（不弹出）
    T* Front() {
        if (Empty())
            return nullptr;
        return &arr[readIdx];
    }

    T* Back() {
        if (Empty())
            return nullptr;
        return &arr[(writeIdx + bufSize - 1) % bufSize];
    }

    // 是否为空
    bool Empty() const { return valueSize == 0; }
    // 是否已满
    bool Full() const { return valueSize == bufSize; }
    // 当前数据量（字节）
    size_t Size() const { return valueSize; }
    // 剩余可写空间
    size_t Free() const { return bufSize - valueSize; }
    // 总容量
    size_t Capacity() const { return bufSize; }


    void Clear() {
        readIdx = 0;    // 可以读取的开始位置
        writeIdx = 0;   // 可以写入的开始位置
        valueSize = 0;  // 有效字节
    }
};

// 合并过程需要的缓冲区
struct Phase2MergeBuffer {
    CircularBuffer data;
    CircularArray<OneBam> bams;
    bool initialized = false;
    size_t numProcessed = 0;

    OneBam* Front() { return bams.Front(); }
    void Pop() {
        OneBam* bp = Front();
        data.SkipRead(bp->offset, bp->wholeBamLen);
        bams.Pop();
    }

    void Revert() {
        bams.Revert();
        OneBam* b = bams.Front();
        data.RevertRead(b->offset, b->wholeBamLen);
    }

    size_t Size() { return bams.Size(); }

    bool hasSpace(size_t bamLen) { return data.FirstPartWriteSize() >= bamLen; }

    // 从一个解压后的block缓冲区拷贝多个bam到循环缓冲区
    // 返回实际拷贝的bam数量
    size_t CopyBams(DataBuffer &blockBuf, BamArr &arr, int start, size_t copyOrder, int fid) {
        if (start >= arr.Size()) return 0;
        int origStart = start;
        size_t numCopied = 0;
        int bamArrFree = bams.Free();
        if (bamArrFree == 0)
            return 0;

        int stop = arr.Size();
        if (stop - start > bamArrFree) {
            stop = start + bamArrFree;
        }
        OneBam* b1 = &arr.Get(start);
        OneBam* b2 = &arr.Get(stop - 1);

        size_t firstPartSize = data.FirstPartWriteSize();
        size_t secondPartSize = data.SecondPartWriteSize();
        size_t needSize = b2->offset - b1->offset + b2->wholeBamLen;

        #if 0
        if (copyOrder == 2 && fid == 4) {
            spdlog::info("debug");
        }
        if (fid == 4) {
            spdlog::info("debug");
        }
        if (data.valueSize > 1024 * 1024 * 1024) {
            spdlog::info("debug");
        }
#endif
        if (needSize <= firstPartSize) {  // 在第一个连续空间里就能放下
            // 每个bam的offset需要加上diff，以对应新的buf
            int64_t diff = (int64_t)data.writeIdx - b1->offset;
            data.Write(blockBuf.data + b1->offset, needSize);
            for (int i = start; i < stop; ++i) {
                bams.Push(arr.Get(i));
                bams.Back()->offset += diff;
                bams.Back()->addr = data.data;
                // spdlog::info("1");
                #if 0
                auto bam = bams.Back();
                if (!CheckBam(bam->addr + bam->offset, bam->wholeBamLen)) {
                    spdlog::info("1");
                    exit(0);
                }
                #endif
                // auto bam = &arr.Get(i);
                // CheckBam(blockBuf.data + bam->offset, bam->wholeBamLen);
            }
        } else {
            int finalStop = stop;
            stop = start;
            size_t firstNeedSize = 0;
            while (arr.Get(stop).wholeBamLen + firstNeedSize < firstPartSize) {
                firstNeedSize += arr.Get(stop).wholeBamLen;
                stop += 1;
            }
            int64_t diff = (int64_t)data.writeIdx - b1->offset;
            data.Write(blockBuf.data + b1->offset, firstNeedSize);
            for (int i = start; i < stop; ++i) {
                bams.Push(arr.Get(i));
                bams.Back()->offset += diff;
                bams.Back()->addr = data.data;
                // spdlog::info("2");
#if 0
                auto bam = bams.Back();
                if (!CheckBam(bam->addr + bam->offset, bam->wholeBamLen)) {
                    spdlog::info("2");
                    exit(0);
                }
                #endif
            }
            // 跳过first part不能放下完整bam的部分
            data.SkipWrite(firstPartSize - firstNeedSize);
            b1 = &arr.Get(stop);
            start = stop;
            // 拷贝第二段
            size_t secondNeedSize = needSize - firstNeedSize;
            if (secondNeedSize <= secondPartSize) {
                stop = finalStop;
            } else {
                secondNeedSize = 0;
                while (arr.Get(stop).wholeBamLen + secondNeedSize < secondPartSize) {
                    secondNeedSize += arr.Get(stop).wholeBamLen;
                    stop += 1;
                }
            }
            diff = (int64_t)data.writeIdx - b1->offset;
            data.Write(blockBuf.data + b1->offset, secondNeedSize);
            for (int i = start; i < stop; ++i) {
                bams.Push(arr.Get(i));
                bams.Back()->offset += diff;
                bams.Back()->addr = data.data;
#if 0
                auto bam = bams.Back();
                //auto bam = &arr.Get(i);
                // CheckBam(blockBuf.data + bam->offset, bam->wholeBamLen);
                if (!CheckBam(bam->addr + bam->offset, bam->wholeBamLen)) {
                // if (!CheckBam(blockBuf.data + bam->offset, bam->wholeBamLen)) {
                    spdlog::info("3-{}-{}", i, copyOrder);
                    exit(0);
                }
                #endif
            }
        }
        numCopied = stop - origStart;

        return numCopied;
    }

    void InitSize(size_t _bufsize, size_t _bamsize) {
        data.AllocMem(_bufsize);
        bams.AllocArr(_bamsize);
        initialized = true;
    }
};

struct BamDataBuffer {
    uint64_t offset = 0;
    int bamOffset = 0;
    DataBuffer block;
    BamArr bams;
    void Clear() {
        offset = 0;
        block.Clear();
        bams.Clear();
    }
};

// 读取和解压需要的buffer
struct Phase2ReadBuffer : ReadBuffer {
    FastVector<BamDataBuffer> uncompressBlocks;
    int bamNum = 0;
};

// 解析
struct UncompressBuffer {
    DataBuffer blockBuf;  // 解压的block放在这里
    BamArr bamArr;        // 解析后的bam数据放在这里
    int startIdx = 0;

    size_t Size() { return bamArr.Size() - startIdx; }

    OneBam *Front() {
        if (Size() == 0)
            return nullptr;
        return &bamArr.Get(startIdx);
    }

    void Clear() {
        startIdx = 0;
        blockBuf.Clear();
        bamArr.Clear();
    }
};

struct Phase2File {
    // static constexpr int MID_FILE_BUF_SIZE = 8 * SINGLE_BLOCK_SIZE;
    static constexpr int READ_BUF_NUM = 2;         // 读入的buf数量
    static constexpr int UNCOMPRESSS_BUF_NUM = 2;  // 解析的buf数量
    static constexpr int COPY_BUF_NUM = 1; // 拷贝到merge缓冲区数量

    // 参数
    size_t readBufSize = 0;
    string fileName;

    FILE* fp = nullptr;
    int fid = 0; // for debug
    Phase2ReadBuffer readData[READ_BUF_NUM];
    UncompressBuffer uncompressBuf[UNCOMPRESSS_BUF_NUM];
    DataBuffer halfBlock;  // 剩余不完整的压缩的block数据

    // 读
    volatile int readyReadBufNum = 0;  // 有几个buffer已经填满可用了, 类似lock_t的作用
    uint64_t readOrder = 0;         // 当前读取的buf索引

    // 解压
    volatile int readyUncompressBufNum = 0;
    uint64_t uncompressOrder = 0;         // 写入的buf索引，类似readOrder
    bool needUncompress = false;

    // 拷贝到归并buf
    Phase2MergeBuffer mergeData;  // 归并时候用到的数据
    uint64_t copyOrder = 0;
    volatile int readyMergeBufNum = 0;

    volatile int finish = 0; // 读入结束

    void Init() {
        for (int i = 0; i < READ_BUF_NUM; ++i) {
            readData[i].Resize(readBufSize);
        }
        halfBlock.AllocMem(SINGLE_BLOCK_SIZE);
        // 打开中间文件
        // spdlog::info("file: {}", fileName);
        fp = fopen(fileName.c_str(), "rb");
    }

    void Init(const string& _fileName, size_t _readBufSize) {
        readBufSize = _readBufSize;
        fileName = _fileName;
        Init();
    }

    // 缓冲区大小应该根据中间文件的数量进行调整

    Phase2File() {}
    Phase2File(const string &_fileName, size_t _readBufSize) {
        readBufSize = _readBufSize;
        fileName = _fileName;
    }

    ~Phase2File() {
        if (fp) {
            fclose(fp);
        }
    }
};

// 用于合并压缩的数据结构
struct Phase2MergeData {
    vector<DataBuffer> blockDataArr;     // 待压缩的数据
    vector<DataBuffer> compressDataArr;  // 压缩后的数据

    void Resize(int blockNum) {
        blockDataArr.resize(blockNum);
        compressDataArr.resize(blockNum);
    }
};

/* 第二阶段的多线程流水线参数 */
struct Phase2PipelineArg {
    // common parameters
    static const int MERGE_BUF_NUM = 2;  // 压缩的buf数量
    static const int COMPRESS_BUF_NUM = 2;  // 压缩的buf数量

    int compressBlocksThreshold = 0;  // 进行归并排序的，单次block数量阈值，参数
    int numThread = 0;  // 线程数
    int compressLevel = 3;  // 压缩级别，参数
    uint64_t numBam = 0;

    // 从phase1转移来的数据
    UncompressBlockBuffer& uncompressData;  // 所有线程共用一个，串行往这里添加解压后的block数据
    BamArr& allBams;                        // 所有线程共用一个，串行往这里添加解析后的bam数据

    // 读入中间文件
    vector<Phase2File> midFiles;
    yarn::lock_t* readSig;
    uint64_t readOrder = 0;
    volatile int readFinish = 0;

    // 解压读入的blocks
    yarn::lock_t* uncompressSig;
    uint64_t uncompressOrder = 0;
    volatile int uncompressFinish = 0;
    int curBlockNum = 0;

    // 拷贝到merge缓冲区
    yarn::lock_t* copyMerge;
    uint64_t copyMergeOrder = 0;
    int uncompressReadyNum = 0;
    volatile int copyMergeFinish = 0;

    // 将解压的数据拷贝进merge的缓冲区，归并后放入，以便后续压缩用到
    MergeCompressData mergeData[MERGE_BUF_NUM];
    yarn::lock_t* mergeSig;
    yarn::lock_t* copyCompressSig;
    uint64_t mergeOrder = 0; // 前半段
    uint64_t copyCompressOrder = 0; // 后半段，需要压缩
    volatile int mergeFinish = 0;
    volatile int copyCompressFinish = 0;

    // 压缩
    DataBuffer compressBuf[COMPRESS_BUF_NUM];  // 压缩用到的buf
    yarn::lock_t* compressSig;
    uint64_t compressOrder = 0;
    volatile int compressFinish = 0;

    // 写入文件
    FILE* outFilePtr = nullptr;  // 文件指针
    uint64_t writeOrder = 0;

    void SetThreadNum(int nThread) {
        numThread = nThread;
        // const int kThres = 1024;
        const int kThres = 2048;
        int threadBlocksThres = numThread * 32;
        compressBlocksThreshold = threadBlocksThres < kThres ? threadBlocksThres : kThres;  // 大概64M的解压缩后的数据
        for (int i = 0; i < COMPRESS_BUF_NUM; ++i) {
            mergeData[i].Resize(compressBlocksThreshold);
        }
    }

    Phase2PipelineArg(UncompressBlockBuffer& lastBuf, BamArr& lastBams, string &midFilePrefix, int midFileNum, int threadNum) : uncompressData(lastBuf), allBams(lastBams){
        SetThreadNum(threadNum);

        int allFileBufSize = 64L * 1024 * 1024;
        // int bufBlocks = (numThread * 4 + midFileNum) / (midFileNum + 1);
        // size_t kFileBufSize = bufBlocks * SINGLE_BLOCK_SIZE;
        size_t kFileBufSize = allFileBufSize  / (midFileNum + 1);

        spdlog::info("mid file buf size: {}", kFileBufSize);

        midFiles.resize(midFileNum);
        for (int i = 0; i < midFileNum; ++i) {
            midFiles[i].Init(midFilePrefix + std::to_string(i), kFileBufSize);
            midFiles[i].fid = i;
        }

        readSig = yarn::NEW_LOCK(0);
        uncompressSig = yarn::NEW_LOCK(0);
        copyMerge = yarn::NEW_LOCK(0);
        mergeSig = yarn::NEW_LOCK(0);
        copyCompressSig = yarn::NEW_LOCK(0);
        compressSig = yarn::NEW_LOCK(0);


    }
};

void phase2Pipeline(Phase2PipelineArg &p);