#include "DvppDec.h"
#include "DvppSourceManager.h"

#define CHECK_AND_RETURN(ret, message)    \
            if(ret != 0) {cout << "device: " << m_dvpp_deviceId << ", chn: " << m_dvpp_channel << ", ret: " << ret << ", [ERROR] " << message; return ret;}
#define CHECK_NOT_RETURN(ret, message)    \
            if(ret != 0) {cout << "device: " << m_dvpp_deviceId << ", chn: " << m_dvpp_channel << ", ret: " << ret << ", [ERROR] " << message;}
#define CHECK_AND_RETURN_NOVALUE(ret, message)    \
            if(ret != 0) {cout << "device: " << m_dvpp_deviceId << ", chn: " << m_dvpp_channel << ", ret: " << ret << ", [ERROR] " << message; return;}

struct Vdec_CallBack_UserData {
    uint64_t frameId;
    long startTime;
    long sendTime;
    // void* vdecOutputBuf;
	DvppDec* self;
	shared_ptr<MemNode> inBufNode;
    Vdec_CallBack_UserData() {
        frameId = 0;
    }
};

#ifdef TEST_DECODER
static void *vdecHostAddr = nullptr;
#endif

static const int g_pkt_size = 1024 * 1024;

 DvppDec::DvppDec(){
    m_decode_thread = 0;
 }

 DvppDec::~DvppDec(){

 }

 bool DvppDec::init_vdpp(DvppDecConfig cfg){
    cout << "Init device....\n";

    m_dvpp_deviceId = atoi(cfg.dev_id.c_str());
    
    if(cfg.codec_id == 0){
        // 66:Baseline,77:Main,>=100:High
        if(cfg.profile == 77){
            enType = H264_MAIN_LEVEL;
        }else if(cfg.profile < 77){
            enType = H264_BASELINE_LEVEL;
        }else{
            enType = H264_HIGH_LEVEL;
        }
    }else if(cfg.codec_id == 1){
        // h265只有main
        enType = H265_MAIN_LEVEL;
    }else {
        cout << "codec_id is not supported!" << endl;
        return false;
    }

    post_decoded_cbk = cfg.post_decoded_cbk;
    m_pktQueueptr = cfg.pktQueueptr;

	// DvppSourceManager 创建时包含 aclInit,析构时包含 aclFinalize
	DvppSourceManager* pSrcMgr = DvppSourceManager::getInstance();
	m_context = pSrcMgr->getContext(m_dvpp_deviceId);
	m_dvpp_channel = pSrcMgr->getChannel(m_dvpp_deviceId);
	if(m_dvpp_channel < 0){
		cout << "该设备channel已经用完了" << endl;
		return false;
	}

    cout << "devProgram start, device: " << m_dvpp_deviceId << endl;
    int ret = aclrtSetCurrentContext(m_context);
    if (ret != ACL_ERROR_NONE) { 
		cout << "aclrtSetCurrentContext failed" << endl;
		return false;
	}
	
	// queue_size 最小应大于16,否则关键帧之间距离太远的时候会导致回调函数与循环队列卡死
	for (size_t i = 0; i < 20; i++){
		void *vdecInputbuf = nullptr;
		int ret = acldvppMalloc((void **)&vdecInputbuf, g_pkt_size);
		if(ret != ACL_ERROR_NONE){
		    cout << "acldvppMalloc failed" << endl;
			return false;;
		}
		m_vec_vdec.push_back(vdecInputbuf);
    }

	if(!m_vdecQueue.init(m_vec_vdec)){
		return false;
	}

    ret = picConverter.init(m_context);
	if(!ret){
		picConverter.release();
	}

    m_vdec_out_size = cfg.width * cfg.height * 3 / 2;
    m_dec_name = cfg.dec_name;

	cout << "init vdpp success!" << endl;
	return true;
}

bool DvppDec::start(){
	m_bRunning = true;

	pthread_create(&m_decode_thread,0,
        [](void* arg)
        {
            DvppDec* a=(DvppDec*)arg;
            a->decode_thread();
            return (void*)0;
        }
    ,this);

	return true;
}

static void *ReportThd(void *arg)
{
    DvppDec *self = (DvppDec *)arg;
	if(nullptr != self){
		self->doProcessReport();
	}
    return (void *)0;
}

void DvppDec::doProcessReport(){
	// aclrtContext thdContext = nullptr;
    // CHECK_AND_RETURN_NOVALUE(aclrtCreateContext(&thdContext, m_dvpp_deviceId), "aclrtCreateContext failed");

	CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed");
    // 阻塞等待vdec线程开始

    int ret;
    while (!m_bExitReportThd) {
        ret = aclrtProcessReport(1000);
        if (ret != ACL_ERROR_NONE) {
            cout << "device: " << m_dvpp_deviceId << ", chn: " << m_dvpp_channel << ", aclrtProcessReport failed, ret: " << ret << endl;
        }
    }

	// CHECK_AND_RETURN_NOVALUE(aclrtDestroyContext(thdContext), "aclrtDestroyContext failed");
}

static int count_frame = 0;
static long lastts = 0;
static void VdecCallback(acldvppStreamDesc *input, acldvppPicDesc *output, void *pUserData)
{
	cout << "VdecCallback: " << UtilTools::get_cur_time_ms() - lastts << endl;
	lastts = UtilTools::get_cur_time_ms();

	Vdec_CallBack_UserData *userData = (Vdec_CallBack_UserData *) pUserData;
	DvppDec* self = userData->self;
	if(self != nullptr){

		self->doVdppVdecCallBack(input, output);
	}
	
	delete userData;
	userData = nullptr;
}

void DvppDec::doVdppVdecCallBack(acldvppStreamDesc *input, acldvppPicDesc *output){

	CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed");

	void *inputDataDev = acldvppGetStreamDescData(input);
	void *outputDataDev = acldvppGetPicDescData(output);
	uint32_t outputSize = acldvppGetPicDescSize(output);
	uint32_t width = acldvppGetPicDescWidth(output);
	uint32_t height = acldvppGetPicDescHeight(output);
	
	cout << "width = " << width << "  height = " << height << "  data_size:" << outputSize << endl;

	if (!m_bPause)
	{
		DvppRgbMemory* rgbMem = picConverter.convert2bgr(output, width, height, false);
        post_decoded_cbk(m_postDecArg, rgbMem);
#ifdef TEST_DECODER
		if(rgbMem != nullptr){
			// D2H
            if(vdecHostAddr == nullptr){
                CHECK_NOT_RETURN(aclrtMallocHost(&vdecHostAddr, width * height * 3), "aclrtMallocHost failed");
            }
			uint32_t data_size = rgbMem->getSize();
			CHECK_AND_RETURN_NOVALUE(aclrtMemcpy(vdecHostAddr, data_size, rgbMem->getMem(), data_size, ACL_MEMCPY_DEVICE_TO_HOST), "D2H aclrtMemcpy failed");

			// 保存vdec结果
			if(count_frame > 45 && count_frame < 50)
			{
				string file_name = "./yuv_pic/vdec_out"+ m_dec_name +".rgb" ;
				FILE *outputFile = fopen(file_name.c_str(), "a");
				if(outputFile){
					fwrite(vdecHostAddr, data_size, sizeof(char), outputFile);
					fclose(outputFile);
				}
			}
            else if(count_frame > 50 && vdecHostAddr != nullptr){
                CHECK_NOT_RETURN(aclrtFreeHost(vdecHostAddr), "aclrtFreeHost failed");
                vdecHostAddr = nullptr;
            }
			count_frame++;
		}
#endif

	}else{
		std::this_thread::sleep_for(std::chrono::milliseconds(3));
	}

    acldvppFree((uint8_t*)outputDataDev);
    outputDataDev = nullptr;

	m_vdecQueue.addHead();

	CHECK_AND_RETURN_NOVALUE(acldvppDestroyStreamDesc(input), "acldvppDestroyStreamDesc failed");
	CHECK_AND_RETURN_NOVALUE(acldvppDestroyPicDesc(output), "acldvppDestroyPicDesc failed");

	cout << "callback exit." << endl;
}

void DvppDec::close(){
	m_bRunning=false;

	if(m_decode_thread != 0){
		pthread_join(m_decode_thread,0);
	}
}

bool DvppDec::sendVdecEos(aclvdecChannelDesc *vdecChannelDesc){
    // create stream desc
    acldvppStreamDesc *streamInputDesc = acldvppCreateStreamDesc();
    if (streamInputDesc == nullptr) {
        cout << "fail to create input stream desc" << endl;
        return false;
    }
    aclError ret = acldvppSetStreamDescEos(streamInputDesc, 1);
    if (ret != ACL_SUCCESS) {
        cout << "fail to set eos for stream desc, errorCode = " << static_cast<int32_t>(ret) << endl;
        (void)acldvppDestroyStreamDesc(streamInputDesc);
        return false;
    }

    // send vdec eos frame. when all vdec callback are completed, aclvdecSendFrame can be returned.
    cout << "send eos" << endl;
    ret = aclvdecSendFrame(vdecChannelDesc, streamInputDesc, nullptr, nullptr, nullptr);
    if (ret != ACL_SUCCESS) {
        cout << "fail to send eos frame, ret=" << ret << endl;
        (void)acldvppDestroyStreamDesc(streamInputDesc);
        return false;
    }
    (void)acldvppDestroyStreamDesc(streamInputDesc);

    return true;
}

void DvppDec::releaseResource(){

	for(int i = 0; i < m_vec_vdec.size(); i++){
		if(m_vec_vdec[i] != nullptr){
			acldvppFree((uint8_t*)m_vec_vdec[i]);
			m_vec_vdec[i] = nullptr;
		}
	}
	m_vec_vdec.clear();

	DvppSourceManager* pSrcMgr = DvppSourceManager::getInstance();
	pSrcMgr->releaseChannel(m_dvpp_deviceId, m_dvpp_channel);
}

void DvppDec::decode_thread(){

    long startTime = UtilTools::get_cur_time_ms();

	int ret = -1;

    // dvpp解码参数
	CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed");

	pthread_t report_thread;
	ret = pthread_create(&report_thread, nullptr, ReportThd, (void *)this);
	if(ret != 0){
		cout << "pthread_create failed" << endl;
		return;
	}

    // 创建aclvdecChannelDesc类型的数据
    aclvdecChannelDesc *vdecChannelDesc = aclvdecCreateChannelDesc();
    if (vdecChannelDesc == nullptr) { 
		cout << "aclvdecCreateChannelDesc failed"; 
		return;
	}
    // 创建 channel dec结构体
    // 通道ID在dvpp层面为0~31
    CHECK_AND_RETURN_NOVALUE(aclvdecSetChannelDescChannelId(vdecChannelDesc, m_dvpp_channel), "aclvdecSetChannelDescChannelId failed");
    CHECK_AND_RETURN_NOVALUE(aclvdecSetChannelDescThreadId(vdecChannelDesc, report_thread), "aclvdecSetChannelDescThreadId failed");
    CHECK_AND_RETURN_NOVALUE(aclvdecSetChannelDescCallback(vdecChannelDesc, VdecCallback), "aclvdecSetChannelDescCallback failed");
    CHECK_AND_RETURN_NOVALUE(aclvdecSetChannelDescEnType(vdecChannelDesc, enType), "aclvdecSetChannelDescEnType failed");
    CHECK_AND_RETURN_NOVALUE(aclvdecSetChannelDescOutPicFormat(vdecChannelDesc, PIXEL_FORMAT_YUV_SEMIPLANAR_420), "aclvdecSetChannelDescOutPicFormat failed");
    CHECK_AND_RETURN_NOVALUE(aclvdecCreateChannel(vdecChannelDesc), "aclvdecCreateChannel failed");

    uint64_t frame_count = 0;
    bool bBreak = false;
	while (m_bRunning)
	{
        int ret = sentFrame(vdecChannelDesc, frame_count);
        if(ret == 2){
            break;
            bBreak = true;
        }else if(ret == 1){
            continue;
        }

        frame_count++;
	}

    // 尽量保证数据全部解码完成
    // int sum = 0;
    // if(!bBreak){
    //     while(!m_pktQueueptr->isEmpty()){
    //         int ret = sentFrame(vdecChannelDesc, frame_count);
    //         if(ret == 2){
    //             break;
    //         }
    //         sum++;
    //         if(sum > 10){
    //             // 避免卡死
    //             break;
    //         }
    //     }
    // }
    

	sendVdecEos(vdecChannelDesc);

	CHECK_NOT_RETURN(aclvdecDestroyChannel(vdecChannelDesc), "aclvdecDestroyChannel failed");
    CHECK_NOT_RETURN(aclvdecDestroyChannelDesc(vdecChannelDesc), "aclvdecDestroyChannelDesc failed");

	// report_thread 需后于destroy退出
	m_bRunning = false;
    m_bExitReportThd = true;
	CHECK_NOT_RETURN(pthread_join(report_thread, nullptr), "pthread_join failed");

	cout << "decode thread exit." << endl;
}

int DvppDec::sentFrame(aclvdecChannelDesc *vdecChannelDesc, uint64_t frame_count){

    AVPacket * pkt = m_pktQueueptr->getHead();
    if(pkt == nullptr){
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        // cout << "getTail failed" << endl;
        // continue;
        return 1;
    }
    // 解码
    void *vdecInputbuf = m_vdecQueue.getTail();
    if(vdecInputbuf == nullptr){
        std::this_thread::sleep_for(std::chrono::milliseconds(3));
        // cout << "getTail failed" << endl;
        // continue;
        return 1;
    }
    
    int ret = aclrtMemcpy(vdecInputbuf, pkt->size, pkt->data, pkt->size, ACL_MEMCPY_HOST_TO_DEVICE);
    if(ACL_ERROR_NONE != ret){
        cout << "aclrtMemcpy failed" << endl;
        // break;
        return 2;
    }

    void *vdecOutputBuf = nullptr;
    ret = acldvppMalloc((void **)&vdecOutputBuf, m_vdec_out_size);
    if(ret != ACL_ERROR_NONE){
        cout << "acldvppMalloc failed" << endl;
        // break;
        return 2;
    }

    /************ 解码*************/
    acldvppStreamDesc *input_stream_desc = acldvppCreateStreamDesc();
        if (input_stream_desc == nullptr) { cout << "acldvppCreateStreamDesc error" << endl; }
    acldvppPicDesc *output_pic_desc = acldvppCreatePicDesc();
        if (output_pic_desc == nullptr) { cout<< "acldvppCreatePicDesc error" << endl; }
    CHECK_NOT_RETURN(acldvppSetStreamDescData(input_stream_desc, vdecInputbuf), "acldvppSetStreamDescData failed");
    CHECK_NOT_RETURN(acldvppSetStreamDescSize(input_stream_desc, pkt->size), "acldvppSetStreamDescSize failed");
    CHECK_NOT_RETURN(acldvppSetPicDescData(output_pic_desc, vdecOutputBuf), "acldvppSetPicDescData failed");
    CHECK_NOT_RETURN(acldvppSetPicDescSize(output_pic_desc, m_vdec_out_size), "acldvppSetPicDescSize failed");
    
    Vdec_CallBack_UserData *user_data = NULL;
    user_data = new Vdec_CallBack_UserData;
    user_data->frameId = frame_count;
    // user_data->startTime = startTime;
    user_data->sendTime = UtilTools::get_cur_time_ms();
    user_data->self = this;
    // user_data->inBufNode = bufNode;
    cout << "send frame" << endl;
    CHECK_NOT_RETURN(aclvdecSendFrame(vdecChannelDesc, input_stream_desc, output_pic_desc, nullptr, reinterpret_cast<void *>(user_data)),
        "aclvdecSendFrame failed");

    m_vdecQueue.addTail();

    m_pktQueueptr->addHead();
    av_packet_unref(pkt);

    return 0;
}


void DvppDec::setPostDecArg(const void* postDecArg){
	m_postDecArg = postDecArg;
}

void DvppDec::pause(){
    m_bPause = true;
}

void DvppDec::resume(){
    m_bPause = false;
}