#include "KylinCamera.h"

#include <QDebug>
#include <unistd.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include "VideoDisplay.h"
#include "VideoDisplayGL.h"
#include "VideoCapture.h"
#include "AudioCapture.h"
#include "VideoDecoder.h"
#include "VideoEncoder.h"
#include "V4l2Capture.h"
//#include "cameraLog.h"
#include "kylinlogger_cpp.h"

#define LIB_PUBLIC __attribute__((visibility("default")))

LIB_PUBLIC KylinCamera::KylinCamera(QObject *parent) : QObject(parent)
{
    m_rate = 16000;
    m_channels = 1;
    m_bit_size = 16;
}

LIB_PUBLIC KylinCamera::~KylinCamera()
{
    if(videoWiget->m_rgb_buf != NULL)
        delete videoWiget;
}




LIB_PUBLIC VideoDisplayGL* KylinCamera::create(QWidget *parent, KylinCameraInfo *device)
{
    videoWiget = new VideoDisplayGL(parent, device);

    //if(videoWiget->videoDecoder==NULL)
        //return NULL;

    if(videoWiget->videoCapture->v4l2Capture == NULL)
        return NULL;
    else
    {
        connect(videoWiget->videoDecoder,SIGNAL(runingStop()),this, SLOT(camera_decoder_reinit()));
        connect(videoWiget->videoDecoder,SIGNAL(photoError()),this, SIGNAL(camera_photo_error()));
        return videoWiget;
    }
}


LIB_PUBLIC int KylinCamera::fourcc(unsigned int format)
{
    char formatArray[] = { (char)(format&0xff), (char)((format>>8)&0xff), (char)((format>>16)&0xff), (char)((format>>24)&0xff), 0 };
    //qDebug() << "pixel format: " << formatArray;
    return 0;
}

int KylinCamera::enum_frame_intervals(int fd, TypePixFormats *pixfmt, TypeFramesizes *fsize)
{
    int i;
    fsize->frm_ivals_len = 0;
    for(i = 0; i < 10; i++) {
        int ret;
        struct v4l2_frmivalenum *fivals = &fsize->frm_ivals[i];
        memset(fivals, 0, sizeof(struct v4l2_frmivalenum));
        fivals->index = i;
        fivals->pixel_format = pixfmt->fmt.pixelformat;
        fivals->width = fsize->frm_size.discrete.width;
        fivals->height = fsize->frm_size.discrete.height;

        ret = ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, fivals);
        if(ret == 0) {
            if (fivals->type == V4L2_FRMIVAL_TYPE_DISCRETE) {
                fsize->frm_ivals_len++;
                LOG(CAMERA_LOG,NOTICE) << fivals->discrete.numerator << "/" << fivals->discrete.denominator;
            } else if (fivals->type == V4L2_FRMIVAL_TYPE_CONTINUOUS) {
                LOG(CAMERA_LOG,NOTICE) << "{min { " << fivals->stepwise.min.numerator << "/" << fivals->stepwise.min.numerator
                              << " } .. max { " << fivals->stepwise.max.denominator << "/" << fivals->stepwise.max.denominator
                              << " } }, ";
                break;
            } else if (fivals->type == V4L2_FRMIVAL_TYPE_STEPWISE) {
                LOG(CAMERA_LOG,NOTICE) << "{min { " << fivals->stepwise.min.numerator << "/" << fivals->stepwise.min.denominator
                              << " } .. max { " << fivals->stepwise.max.numerator << "/" << fivals->stepwise.max.denominator
                              << " } /" << " }, stepsize { " << fivals->stepwise.step.numerator << "/" << fivals->stepwise.step.denominator
                              << "} }, ";
                break;
            }
        } else if(EINVAL == errno) {
            break;
        } else {
            LOG(CAMERA_LOG,ERROR) << "VIDIOC_ENUM_FRAMEINTERVALS error " ;
            return -1;
        }
    }
    return 0;
}

int KylinCamera::enum_frame_sizes(int fd, TypePixFormats *pixfmt)
{
    int i;
    pixfmt->frm_sizes_len = 0;
    for(i = 0; i < 50; i++) {
        int ret;
        TypeFramesizes *fsize = &pixfmt->frm_sizes[i];
        memset(fsize, 0, sizeof(TypeFramesizes));
        fsize->frm_size.index = i;
        fsize->frm_size.pixel_format = pixfmt->fmt.pixelformat;
        printf("%s: ioctl-VIDIOC_ENUM_FRAMESIZES index=%d pixel_format=%d \n"  , __FUNCTION__ , i , pixfmt->fmt.pixelformat);
        ret = ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsize->frm_size);
        if(ret == 0) {
            if(fsize->frm_size.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
                pixfmt->frm_sizes_len++;
                LOG(CAMERA_LOG,NOTICE) << "{ discrete: width = "<< fsize->frm_size.discrete.width
                              << ", height = " << fsize->frm_size.discrete.height << "}";
                ret = enum_frame_intervals(fd, pixfmt, fsize);
                if(ret !=0)
                    LOG(CAMERA_LOG,NOTICE) << "Unable to enumerate frame sizes.\n";
            }else if(fsize->frm_size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
                LOG(CAMERA_LOG,NOTICE) << "{ continuous: min { width = " << fsize->frm_size.stepwise.min_width << ","
                              << "height = " << fsize->frm_size.stepwise.min_height << " } .. "
                              << "max { width = " << fsize->frm_size.stepwise.max_width
                              << ", height =" << fsize->frm_size.stepwise.max_width <<"} }\n";
                LOG(CAMERA_LOG,NOTICE) << "Refusing to enumerate frame intervals.\n";
                break;
            }else if(fsize->frm_size.type == V4L2_FRMSIZE_TYPE_STEPWISE){
                LOG(CAMERA_LOG,NOTICE) << "{ stepwise: min { width = " << fsize->frm_size.stepwise.min_width
                              << ", height = " << fsize->frm_size.stepwise.min_height << "} .. "
                              << "max { width = " << fsize->frm_size.stepwise.max_width
                              << ", height = " << fsize->frm_size.stepwise.max_width << "} }\n";
                LOG(CAMERA_LOG,NOTICE) << "Refusing to enumerate frame intervals.\n";
                break;
            }
        }else if(EINVAL == errno) {
            break;
        }else {
            return -1;
        }
    }
    return 0;
}

LIB_PUBLIC int KylinCamera::enum_device(V4l2DeviceInfo *device)
{
    int fd, i;

    fd = open(device->dev_path, O_RDWR, 0);
    if(fd < 0) {
        LOG(CAMERA_LOG,NOTICE) << "Cannot open ' " << device->dev_path << " ': "
                      << errno << ", " << strerror(errno) << "\n";
        close(fd);
        return -1;
    }
    device->fmt_size = 0;
    for(i=0;i<10;i++){ //enum pixel format
         TypePixFormats *pixfmt = &device->fmt_supported[i];
         memset(pixfmt,0,sizeof(TypePixFormats));
         pixfmt->fmt.index = i;
         pixfmt->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         printf("%s: ioctl-VIDIOC_ENUM_FMT index=%d type=%d \n" ,
                  __FUNCTION__ , pixfmt->fmt.index , pixfmt->fmt.type);
         if(0 == ioctl(fd,VIDIOC_ENUM_FMT,&pixfmt->fmt)){
             fourcc(pixfmt->fmt.pixelformat);
             device->fmt_size++;  //increase the supported format size
             int ret = enum_frame_sizes(fd,pixfmt);  //enum supported frame size
             if (ret != 0)
                 LOG(CAMERA_LOG,NOTICE) << "  Unable to enumerate frame sizes.\n";
         }else if(errno == EINVAL){
             LOG(CAMERA_LOG,NOTICE) << "Device " << device->dev_path << "format enumerate finished\n";
             break;
         }else{
             return -1;
         }
     }
     //close the device
     close(fd);
     return 0;
}

LIB_PUBLIC void KylinCamera::camera_capture_pause()
{
    videoWiget->videoCapture->m_stopped = true;
    videoWiget->videoDecoder->m_stopped = true;
    videoWiget->videoEncoder->m_stopped = true;
}

LIB_PUBLIC void KylinCamera::camera_capture_restore()
{
    videoWiget->videoDecoder->m_stopped = false;
    videoWiget->videoDecoder->m_mutex.unlock();
    videoWiget->videoEncoder->m_stopped = false;
    videoWiget->videoEncoder->m_mutex.unlock();
    videoWiget->videoCapture->m_stopped = false;
    videoWiget->videoCapture->m_mutex.unlock();
}

void KylinCamera::camera_decoder_reinit()
{
    videoWiget->videoCapture->capture_deinit();
    videoWiget->videoCapture->capture_init(next_dinfo->devname, next_dinfo->format, next_dinfo->width, next_dinfo->height, next_dinfo->fps);
    
    videoWiget->videoDecoder->decoder_deinit();
    int size = videoWiget->videoCapture->m_buf_size;
    videoWiget->videoDecoder->decoder_init(videoWiget->videoCapture->v4l2Capture->m_capture_buf ,next_dinfo->format, next_dinfo->width, next_dinfo->height, size);

    videoWiget->videoEncoder->encoder_deinit();
    int fps = videoWiget->videoCapture->m_fps;
    videoWiget->videoEncoder->encoder_init(next_dinfo->width, next_dinfo->height, next_dinfo->fps);

    videoWiget->set_src_ratio(videoWiget->videoCapture->m_width,videoWiget->videoCapture->m_height);
    camera_capture_restore();
}

LIB_PUBLIC int KylinCamera::camera_set_param(KylinCameraInfo *device)
{
    int ret = 0;
    if(device == NULL)
        return -1;
    camera_capture_pause();
    videoWiget->videoDecoder->m_run_stopped = false;
    next_dinfo = device;
    return ret;
}

LIB_PUBLIC int KylinCamera::camera_get_param(KylinCameraInfo *device)
{
    int ret = 0;
    if(device == NULL)
        return -1;
    char *devname = videoWiget->videoCapture->m_devname;
    int iLen = strnlen(videoWiget->videoCapture->m_devname , sizeof(videoWiget->videoCapture->m_devname));
    memcpy(device->devname, devname, iLen);
    device->format = videoWiget->videoCapture->m_format;
    device->fps = videoWiget->videoCapture->m_fps;
    device->height = videoWiget->videoCapture->m_height;
    device->width = videoWiget->videoCapture->m_width;

    return ret;
}

LIB_PUBLIC int KylinCamera::camera_get_mode()
{
    return videoWiget->m_camera_mode;
}

LIB_PUBLIC int KylinCamera::camera_take_photo(char *photopath)
{
    if(!photopath || strlen(photopath)<=0)
        return false;

    videoWiget->m_camera_mode = photo;
    memcpy(videoWiget->videoDecoder->m_photo_name, photopath, strlen(photopath)+1);
    videoWiget->videoDecoder->m_photo_clicked = true;

    return true;
}

LIB_PUBLIC int KylinCamera::camera_start_record(char *videopath, bool hwenc)
{
    // if (videoWiget->videoEncoder->recordStart(videopath, hwenc, m_rate, m_channels, m_bit_size) == false)
    //     return false;
    // videoWiget->videoEncoder->m_stopped = false;
    // videoWiget->videoEncoder->m_started = true;
    // videoWiget->videoEncoder->m_mutex.unlock();
    // videoWiget->videoDecoder->m_video_clicked = true;
    // videoWiget->audioCapture->m_audio_click = true;
    // return true;

    return camera_start_record(videopath, -1, hwenc);
}


LIB_PUBLIC int KylinCamera::camera_start_record(char *videopath, int enclevel, bool hwenc)
{
    if (videoWiget->videoEncoder->recordStart(videopath, hwenc, m_rate, m_channels, m_bit_size, enclevel) == false)
        return false;
    videoWiget->videoEncoder->m_stopped = false;
    videoWiget->videoEncoder->m_started = true;
    videoWiget->videoEncoder->m_mutex.unlock();
    videoWiget->videoDecoder->m_video_clicked = true;
    videoWiget->audioCapture->m_audio_click = true;

    return true;
}

LIB_PUBLIC int KylinCamera::camera_stop_record()
{
    videoWiget->videoDecoder->m_video_clicked = false;
    videoWiget->audioCapture->m_audio_click = false;
    videoWiget->videoEncoder->m_stopped = true;
    videoWiget->videoEncoder->m_fps = videoWiget->get_fps();
    //videoWiget->videoEncoder->recordStop();
    return 0;
}

LIB_PUBLIC int KylinCamera::camera_record_pause(bool on)
{
    struct timeval timeNow;

    gettimeofday(&timeNow, NULL);
    if(on == true) {
        videoWiget->videoEncoder->m_pause_time = timeNow.tv_sec * 1000 + timeNow.tv_usec / 1000
                                                - videoWiget->videoEncoder->m_start_time;
        videoWiget->videoDecoder->m_video_clicked = false;
        videoWiget->audioCapture->m_audio_click = false;
    }
    else {
        videoWiget->videoEncoder->m_time += timeNow.tv_sec * 1000 + timeNow.tv_usec / 1000
                                           - videoWiget->videoEncoder->m_start_time
                                           - videoWiget->videoEncoder->m_pause_time;
        videoWiget->videoEncoder->m_pause_time = 0;
        videoWiget->videoDecoder->m_video_clicked = true;
        videoWiget->audioCapture->m_audio_click = true;
    }
    return 0;
}

LIB_PUBLIC long long KylinCamera::camera_get_record_time()
{
    return videoWiget->videoEncoder->m_record_time;
}

LIB_PUBLIC int KylinCamera::camera_set_contrl(int id, int value)
{
    return videoWiget->videoCapture->capture_set_ctrl(id, value);
}

LIB_PUBLIC int KylinCamera::camera_get_contrl(int id, int *pvalue)
{
    return videoWiget->videoCapture->capture_get_ctrl(id, pvalue);
}

LIB_PUBLIC int KylinCamera::camera_query_contrl(int id, struct v4l2_queryctrl *pvalue)
{
    return videoWiget->videoCapture->capture_query_ctrl(id, pvalue);
}

LIB_PUBLIC int KylinCamera::camera_audio_init(const QAudioDeviceInfo &deviceInfo)
{
    videoWiget->audioCapture->m_audio_buf->Empty();
    videoWiget->audioCapture->capture_init(deviceInfo, m_rate, m_channels, m_bit_size);
    return 0;
}

LIB_PUBLIC void KylinCamera::camera_audio_volume_set(qreal volume)
{
    videoWiget->audioCapture->set_volume(volume);
}

LIB_PUBLIC qreal KylinCamera::camera_audio_volume()
{
    return videoWiget->audioCapture->volume();
}

#if 0
LIB_PUBLIC int KylinCamera::camera_video_enable(bool enable)
{
    return 0;
}
#endif

LIB_PUBLIC int KylinCamera::camera_audio_disable()
{
    videoWiget->audioCapture->m_audio_buf->Empty();
    videoWiget->audioCapture->capture_deinit();

    return 0;
}

LIB_PUBLIC enum CameraState KylinCamera::camera_get_state(enum CameraOptions opt)
{
    enum CameraState state = CAMERA_STATE_NONE;
    switch (opt) {
    case CAMERA_CAPTURE:
        if(videoWiget->videoCapture->m_readable)
            state = CAMERA_CAPTURE_READ;
        else
            state = CAMERA_CAPTURE_READ_ERR;
        break;
    case CAMERA_DECODE:
        if(videoWiget->videoDecoder->m_state)
            state = CAMERA_DECODE_ERR;
        else
            state = CAMERA_DECODE_OK;
        break;
    case CAMERA_ENCODE:
        if(videoWiget->videoEncoder->m_started)
            state = CAMERA_ENCODE_START;
        else if( !videoWiget->videoEncoder->m_started )
            state = CAMERA_ENCODE_STOP;
		break;
	case CAMERA_PHOTO:
        if(videoWiget->videoDecoder->m_photo_clicked)
            state = CAMERA_PHOTO_BUZY;
        else
            state = CAMERA_PHOTO_FREE;
        break;
    default:
        break;
    }
    return state;
}

LIB_PUBLIC void KylinCamera::camera_display_mirror(bool on)
{
    videoWiget->videoDecoder->m_display_mirror = on;
}

LIB_PUBLIC void KylinCamera::camera_record_mirror(bool on)
{
    camera_display_mirror(on);
}

LIB_PUBLIC void KylinCamera::camera_photo_mirror(bool on)
{
    camera_display_mirror(on);
}

LIB_PUBLIC int KylinCamera::camera_log_init(int verbose)
{
    CaminitLogger(NULL,verbose);
    int err = logger_init(NULL,verbose);
    if (err) {
        logger_deinit();
        //return false;
    }
    return err;
}

LIB_PUBLIC void KylinCamera::camera_set_background(QColor color)
{
    return videoWiget->set_background_color(color);
}

 LIB_PUBLIC void KylinCamera::camera_set_cover(float cover)
 {
     return videoWiget->set_cover(cover);
 }
