#include <QDebug>
#include <QDateTime>

//#include "cameraLog.h"
#include "kylinlogger_cpp.h"
#include <sys/ioctl.h>
#include <errno.h>

#include "VideoCapture.h"
#include "VideoDecoder.h"
#include "VideoEncoder.h"
#include "V4l2Device.h"
#include "V4l2Capture.h"
#include "CCycleBuffer.h"

VideoCapture::VideoCapture(CCycleBuffer *buf, KylinCameraInfo *device) {
    (void) buf;
    V4l2Access::IoType ioTypeIn = V4l2Access::IOTYPE_MMAP;

    //yuv_fp = fopen("test.yuv", "wb");
    V4L2DeviceParameters param(device->devname, device->format, device->width, device->height, device->fps);
    int iRetryTimes = 2;
    while (iRetryTimes--)
    {
        v4l2Capture = V4l2Capture::create(param, ioTypeIn);
        if (false && v4l2Capture != NULL)
        {
            sleep(1);
            timeval tv;
            tv.tv_sec = 1;
            tv.tv_usec = 0;
            if (v4l2Capture->isReadable(&tv) == 0)
            {
                delete v4l2Capture;
                v4l2Capture = NULL;
                printf("** %s: dev  not readable retry \n", __func__);
            }
            else
                break;
        }
        else
            break;
    }
    if (v4l2Capture == NULL) {
        qDebug() << "Cannot create V4L2 capture interface for device: %s" << device->devname;
        return;
    }

    m_capture_buf = v4l2Capture->m_capture_buf;

    m_width = v4l2Capture->getWidth();
    m_height = v4l2Capture->getHeight();
    m_format = v4l2Capture->getFormat();
    m_fps = device->fps;
    m_buf_size = v4l2Capture->getBufferSize();
    m_buf = (uint8_t *) new char[m_buf_size];
    m_start_flag = false;
    m_stopped = false;
    m_run_stopped = false;
    m_readable = 0;
}

VideoCapture::~VideoCapture() {
    requestInterruption();
    quit();
    wait();
    if (v4l2Capture != NULL)
        delete v4l2Capture;

    if (m_buf != NULL)
        delete []m_buf;
}

int VideoCapture::capture_init(const char *devname, unsigned int format, unsigned int width, unsigned int height,
                               int fps) {
    V4l2Access::IoType ioTypeIn = V4l2Access::IOTYPE_MMAP;
    V4L2DeviceParameters param(devname, format, width, height, fps);
    v4l2Capture = V4l2Capture::create(param, ioTypeIn);
    if (v4l2Capture == NULL) {
        LOG(CAMERA_LOG, NOTICE) << "Cannot create V4L2 capture interface for device: %s" << devname;
        return -1;
    }

    m_width = v4l2Capture->getWidth();
    m_height = v4l2Capture->getHeight();
    m_format = v4l2Capture->getFormat();
    m_fps = fps;
    memcpy(m_devname, devname, strlen(devname) + 1);
    m_buf_size = v4l2Capture->getBufferSize();
    m_buf = (uint8_t *) new char[m_buf_size];
    m_start_flag = false;

    return 0;
}

int VideoCapture::capture_set_ctrl(int id, int value) {
    return v4l2Capture->setControl(id, value);
}

int VideoCapture::capture_get_ctrl(int id, int *pvalue) {
    return v4l2Capture->getControl(id, pvalue);
}

int VideoCapture::capture_query_ctrl(int id, struct v4l2_queryctrl *pvalue) {
    return v4l2Capture->queryControl(id, pvalue);
}

int VideoCapture::capture_deinit() {
    while (m_run_stopped == false)
        usleep(20);
    m_run_stopped = false;

    if (m_buf != NULL)
        delete []m_buf;

    if (v4l2Capture != NULL)
        delete v4l2Capture;

    return 0;
}

void VideoCapture::run() {
    timeval tv, tv_start, tv_stop;

    gettimeofday(&tv_start, 0);
    tv_stop = tv_start;

    while (!isInterruptionRequested()) {
        m_mutex.lock();
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        auto start = std::chrono::high_resolution_clock::now();
        int ret = v4l2Capture->isReadable(&tv);


        m_readable = ret;
        //LOG(CAMERA_LOG,NOTICE) << "v4l2 readable"<< m_readable;
        if (ret <= 0) {
            auto end = std::chrono::high_resolution_clock::now();
            std::chrono::duration<double, std::milli> elapsed = end - start;
            LOG(CAMERA_LOG, NOTICE) <<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:MM:ss:zzz")<<" "<< __func__ <<": dev  not readable waittime="<< elapsed.count()/1000.0 ;

        }
        if (ret == 1) {
            int rsize = 0;

            m_capture_buf = v4l2Capture->m_capture_buf;
            if (m_capture_buf->GetLength() < 50 * 1024 * 1024) {
                gettimeofday(&tv_stop, 0);
                m_real_time = (double) tv_stop.tv_sec - (double) tv_start.tv_sec +\
                              (double) tv_stop.tv_usec * 0.000001 - (double) tv_start.tv_usec * 0.000001;
                m_real_time *= 1000;
                tv_start = tv_stop;
                //qDebug("frame time %10.2f ms\n", m_real_time);
                rsize = v4l2Capture->read((char *) m_buf, m_buf_size);
            } else {
                LOG(CAMERA_LOG, NOTICE) <<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:MM:ss:zzz")<<" "<< __func__ <<": m_capture_buf->GetLength() < 50*1024*1024";
            }

            if (rsize == -1) {
                m_readable = -1;
                emit readout();
                LOG(CAMERA_LOG, NOTICE) <<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:MM:ss:zzz")<<" "<< __func__ <<": v4l2 read error";
            } else {
                m_readable = 1;
                //m_capture_buf->Write(m_buf, m_buf_size);
                //fwrite(m_buf, 1, m_buf_size, yuv_fp);
                m_start_flag = true;
            }
        }
        if (m_stopped == false)
            m_mutex.unlock();
        else
            m_run_stopped = true;
    }
}
