/*
     Description: 第一阶段的读入线程

     Copyright : All right reserved by ICT

     Author : Zhang Zhonghai
     Date : 2026/05/25
*/

#include "phase_1_read.h"

#include <spdlog/spdlog.h>
#include <stdint.h>
#include <stdio.h>

#include "common_data.h"
#include "const_val.h"
#include "phase_1.h"
#include "sam_io.h"
#include "sort.h"
#include "util/profiling.h"
#include "util/yarn.h"

/* 具体执行读取文件操作 */
/* 将bam文件内容读取到buf，解析buf中的gz block长度信息 */
// static size_t doPhase1ReadFile(Phase1PipelineArg& p, DataBuffer& halfBlock, FILE* fpr) {
size_t ReadBamFile(ReadBuffer& readData, DataBuffer& halfBlock, FILE* fpr) {
    // ReadBuffer& readData = p.readData[p.readOrder % p.READ_BUF_NUM];
    size_t readState = 0;
    size_t curReadPos = 0;
    int blockLen = 0;
    int maxBlockLen = 0;

    readState = fread(readData.dataBuf, 1, readData.readBufSize, fpr);
    if (readState == 0) {
        return 0;
    }

    readData.startAddrArr.clear(); // 先清空上一次的block起始地址信息，后续重新计算并保存当前buf里每个block的起始地址和长度信息

    /* 处理上一个不完整的block */  // 需要一个额外的空间来保存上一个不完整的block数据，因为readbuffer里的data和block都会用来解析，而且解析之后都会清空。
    if (halfBlock.readPos > 0) {                        // 上一轮有剩余
        if (halfBlock.readPos < BLOCK_HEADER_LENGTH) {  // 上一轮剩余数据不满足解析block长度信息
            memcpy(&halfBlock.data[halfBlock.readPos], readData.dataBuf, BLOCK_HEADER_LENGTH - halfBlock.readPos);
            halfBlock.curLen = unpackInt16(&halfBlock.data[16]) + 1;  // 更新一下剩余block的真正长度
            // spdlog::info("last remain, blocklen: {}, last load: {}", halfBlock.curLen, halfBlock.readPos);
        }
        memcpy(readData.blockBuf, halfBlock.data, halfBlock.readPos);
        curReadPos = halfBlock.curLen - halfBlock.readPos;  // curlen保存上一个block的长度，readPos保存上一个block在上一次读取中的长度
        memcpy(&readData.blockBuf[halfBlock.readPos], readData.dataBuf, curReadPos);  // 将不完整的block剩余数据拷贝到curBlock
        readData.startAddrArr.push_back(readData.blockBuf);
    }
    /* 解析读入buf中的文件数据，计算包含的每个block的长度信息和起始地址 */
    while (curReadPos + BLOCK_HEADER_LENGTH <= readState) { /* 确保能解析block长度 */
        blockLen = unpackInt16(&readData.dataBuf[curReadPos + 16]) + 1;
        if (blockLen > maxBlockLen) {
            maxBlockLen = blockLen;
        }
        if (curReadPos + blockLen <= readState) { /* 完整的block数据在buf里 */
            readData.startAddrArr.push_back(&readData.dataBuf[curReadPos]);
            curReadPos += blockLen;
        } else {
            break; /* 当前block数据不完整，一部分在还没读入的file数据里 */
        }
    }
    /* 如果buf中包含不完整的block数据，先保存一下，放到下一轮里去处理 */
    halfBlock.readPos = readState - curReadPos;
    halfBlock.curLen = blockLen;
    if (halfBlock.readPos > 0) {
        memcpy(halfBlock.data, &readData.dataBuf[curReadPos], halfBlock.readPos);  // 将不完整的block拷贝到halfBlock
    }

    return readState;
}

/* phase1ReadFile step-1 读取文件线程 */
void* phase1ReadFile(void* data) {
    Phase1PipelineArg& p = *(Phase1PipelineArg*)data;
    /* 1. set up */
    FILE* fpr = fopen(nsgv::gSortArg.INPUT_FILE.c_str(), "rb");
    parseSamHeader(fpr, nsgv::gInHdr); // 解析header，后续需要用到header里的信息来解析bam数据
    size_t fileSize = 0;
    DataBuffer halfBlock(SINGLE_BLOCK_SIZE);

    /* 2. do the work */
    while (true) {
        // self dependency
        yarn::DEPENDENCY_NOT_TO_BE(p.readSig, p.READ_BUF_NUM);

        PROF_G_BEG(read);
        size_t readState = ReadBamFile(p.readData[p.readOrder % p.READ_BUF_NUM], halfBlock, fpr);
        PROF_G_END(read);

        if (readState == 0) {
            yarn::SIGNAL_FINISH(p.readSig, p.readFinish);
            break;
        }

        // update self status
        yarn::UPDATE_SIG_ORDER(p.readSig, p.readOrder);

        fileSize += readState;
    }

    spdlog::info("read file order: {}, file size: {}", p.readOrder, fileSize);
    /* 3. clean up */
    fclose(fpr);
    return nullptr;
}