/************************************************************************************************/
// CaptureByInput.cpp : Defines the entry point for the console application.

// MAGEWELL PROPRIETARY INFORMATION

// The following license only applies to head files and library within Magewell’s SDK
// and not to Magewell’s SDK as a whole.

// Copyrights © Nanjing Magewell Electronics Co., Ltd. (“Magewell”) All rights reserved.

// Magewell grands to any person who obtains the copy of Magewell’s head files and library
// the rights,including without limitation, to use, modify, publish, sublicense, distribute
// the Software on the conditions that all the following terms are met:
// - The above copyright notice shall be retained in any circumstances.
// -The following disclaimer shall be included in the software and documentation and/or
// other materials provided for the purpose of publish, distribution or sublicense.

// THE SOFTWARE IS PROVIDED BY MAGEWELL “AS IS” AND ANY EXPRESS, INCLUDING BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL MAGEWELL BE LIABLE

// FOR ANY CLAIM, DIRECT OR INDIRECT DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT,
// TORT OR OTHERWISE, ARISING IN ANY WAY OF USING THE SOFTWARE.

// CONTACT INFORMATION:
// SDK@magewell.net
// http://www.magewell.com/
//
/************************************************************************************************/

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "MWFOURCC.h"
#include "LibMWCapture/MWCapture.h"

#include "bitmap.h"

#define NUM_CAPTURE 100

void print_version_and_useage();
bool check_file();
int get_id(char c);
HCHANNEL open_channel(int argc, char* argv[]);

int g_need_stop = 0;
DWORD g_enable_bits = MWCAP_NOTIFY_VIDEO_FRAME_BUFFERED;
int g_i_frame = MWCAP_VIDEO_FRAME_ID_NEWEST_BUFFERED;

void stop(int signo){
    g_need_stop = 1;
}
int main(int argc,char* argv[])
{
    // version
    print_version_and_useage();
    
    // permission
    if(!check_file()){
        return 0;
    }
    signal(SIGINT, stop);
    if(!MWCaptureInitInstance()){
        printf("have InitilizeFailed");
    }
    
    HCHANNEL hChannel = NULL;
//    HNOTIFY hNotify = 0;
    MWCAP_PTR hNotifyEvent = 0;
    MWCAP_PTR hCaptureEvent = 0;
    
    HANDLE64 pbImage = 0;
    
    LONGLONG llTotalTime = 0LL;
    
    int nRet;
    
    do {
        MWRefreshDevice();
        hChannel = open_channel(argc, argv);
        if(NULL == hChannel){
            break;
        }
        MWCAP_CHANNEL_INFO videoInfo = { 0 };
        if (MW_SUCCEEDED != MWGetChannelInfo(hChannel, &videoInfo)) {
            printf("ERROR: Can't get channel info!\n");
            break;
        }
        
        printf("Open channel - BoardIndex = %X, ChannelIndex = %d.\n", videoInfo.byBoardIndex, videoInfo.byChannelIndex);
        printf("Product Name: %s\n", videoInfo.szProductName);
        printf("Board SerialNo: %s\n\n", videoInfo.szBoardSerialNo);

        // Capture frames on input signal frequency
        hCaptureEvent = MWCreateEvent();
        if(hCaptureEvent==0){
            printf("Create capture event error\n");
            break;
        }
        
        hNotifyEvent = MWCreateEvent();
        if(hNotifyEvent==0){
            printf("Create notify event error\n");
            break;
        }

        MW_RESULT xr;
        xr = MWStartVideoCapture(hChannel, hCaptureEvent);
        if (xr != MW_SUCCEEDED) {
            printf("ERROR: Start Video Capture error!\n");
            break;
        }

        MWCAP_VIDEO_SIGNAL_STATUS videoSignalStatus;
        MWGetVideoSignalStatus(hChannel, &videoSignalStatus);
        
        switch (videoSignalStatus.state) {
            case MWCAP_VIDEO_SIGNAL_NONE:
                printf("ERRPR: Input signal status: NONE\n");
                break;
            case MWCAP_VIDEO_SIGNAL_UNSUPPORTED:
                printf("ERRPR: Input signal status: Unsupported\n");
                break;
            case MWCAP_VIDEO_SIGNAL_LOCKING:
                printf("ERRPR: Input signal status: Locking\n");
                break;
            case MWCAP_VIDEO_SIGNAL_LOCKED:
                printf("Input signal status: Locked\n");
                break;
        }
        
        if (videoSignalStatus.state != MWCAP_VIDEO_SIGNAL_LOCKED) {
            MWStopVideoCapture(hChannel);
            break;
        }
        
        MWCAP_VIDEO_BUFFER_INFO videoBufferInfo;
        xr = MWGetVideoBufferInfo(hChannel, &videoBufferInfo);
        
        MWCAP_VIDEO_FRAME_INFO videoFrameInfo;
        xr = MWGetVideoFrameInfo(hChannel, videoBufferInfo.iNewestBufferedFullFrame, &videoFrameInfo);
        if (xr != MW_SUCCEEDED) {
            printf("ERROR: Get Video Frame Info error!\n");
            break;
        }
        
        int cx = videoSignalStatus.cx;
        int cy = videoSignalStatus.cy;
        DWORD dwFourcc = MWFOURCC_BGR24;
        DWORD dwMinStride = FOURCC_CalcMinStride(dwFourcc,cx,4);
        DWORD dwImageSize = FOURCC_CalcImageSize(dwFourcc,cx,cy,dwMinStride);
        pbImage = (HANDLE64)(unsigned long)malloc(dwImageSize);
        
        
        printf("Input signal resolution: %d x %d\n", videoSignalStatus.cx, videoSignalStatus.cy);
        double fps = (videoSignalStatus.bInterlaced == TRUE) ? (double)20000000LL / videoSignalStatus.dwFrameDuration : (double)10000000LL / videoSignalStatus.dwFrameDuration;
        printf("Input signal fps: %.2f\n", fps);
        printf("Input signal interlaced: %d\n", videoSignalStatus.bInterlaced);
        printf("Input signal frame segmented: %d\n", videoSignalStatus.bSegmentedFrame);
        
        
        HNOTIFY hNotify = MWRegisterNotify(hChannel, hNotifyEvent, g_enable_bits);
        if (hNotify == 0) {
            MWStopVideoCapture(hChannel);
            printf("ERROR: Register Notify error.\n");
            break;
        }
        printf("Begin to capture %d frames by %.2f fps...\n", NUM_CAPTURE, fps);
        fflush(stdout);
        LONGLONG llBegin = 0LL;
        xr = MWGetDeviceTime(hChannel, &llBegin);
        int i;
        LONGLONG llTime = 0, llTime_pre=0;
        for (i = 0; i < NUM_CAPTURE; i++) {
            if(g_need_stop){
                break;
            }
            
//            if (MWTryWaitEvent(hNotifyEvent) != MW_SUCCEEDED) {
//                continue;
//            }
            
            nRet = MWWaitEvent(hNotifyEvent,5000);
            if(nRet<0) {
                printf("Error:wait notify error or timeout, hNotify: %p-0x%llx\n", (void *)hNotifyEvent, hNotifyEvent);
                break;
            }

            ULONGLONG ullStatusBits = 0;
            xr = MWGetNotifyStatus(hChannel, hNotify, &ullStatusBits);
            if (xr != MW_SUCCEEDED)
                continue;
            
            if ((ullStatusBits & g_enable_bits) == 0) {
                continue;
            }

//            xr = MWGetVideoBufferInfo(hChannel, &videoBufferInfo);
//            if (xr != MW_SUCCEEDED)
//                continue;
//
//            xr = MWGetVideoFrameInfo(hChannel, videoBufferInfo.iNewestBufferedFullFrame, &videoFrameInfo);
//            if (xr != MW_SUCCEEDED)
//                continue;
            
            if (MWTryWaitEvent(hCaptureEvent) != MW_SUCCEEDED) {
                continue;
            }
            
            xr = MWCaptureVideoFrameToVirtualAddressEx(hChannel,
                                                       g_i_frame,(unsigned char *)pbImage,dwImageSize,dwMinStride,
                                                       1,0,dwFourcc,cx,cy,0,0,0,0,0,100,0,100,0,MWCAP_VIDEO_DEINTERLACE_BLEND,
                                                       MWCAP_VIDEO_ASPECT_RATIO_CROPPING,0,0,0,0,MWCAP_VIDEO_COLOR_FORMAT_UNKNOWN,
                                                       MWCAP_VIDEO_QUANTIZATION_UNKNOWN,MWCAP_VIDEO_SATURATION_UNKNOWN);
            
            if(xr != MW_SUCCEEDED)
                continue;

            nRet = MWWaitEvent(hCaptureEvent,-1);
            if(nRet < 0) {
                printf("Error:wait capture event error or timeout\n");
                break;
            }

            MWCAP_VIDEO_CAPTURE_STATUS captureStatus;
            xr = MWGetVideoCaptureStatus(hChannel, &captureStatus);
            
            MWGetDeviceTime(hChannel, &llTime);
            printf("[%d]: %lld, %lld\n", i, llTime/10000, llTime_pre == 0 ? 0 : (llTime-llTime_pre)/10000);
            llTime_pre = llTime;
            
//            LONGLONG llCurrent = 0LL;
//            xr = MWGetDeviceTime(hChannel, &llCurrent);
//            llTotalTime += (llCurrent - (videoSignalStatus.bInterlaced ? videoFrameInfo.allFieldBufferedTimes[1] : videoFrameInfo.allFieldBufferedTimes[0]));
//            
            char szBitmapName[64];
            sprintf(szBitmapName,"./video_%d.BMP",NUM_CAPTURE);
            if( i== NUM_CAPTURE-1)
                create_bitmap(szBitmapName,pbImage,dwImageSize,cx,cy);
        }
        LONGLONG llEnd = 0LL;
        xr = MWGetDeviceTime(hChannel, &llEnd);
        xr = MWUnregisterNotify(hChannel, hNotify);
        hNotify = 0;
        xr = MWStopVideoCapture(hChannel);
        
        if(i >= NUM_CAPTURE){
            printf("\nStop capture\n");
            printf("Each frame average duration is %d (100 ns).\n", (LONG)((llEnd - llBegin) / NUM_CAPTURE));
//            printf("\nAverage frame duration is %lld ms.\n", (LONGLONG)(llTotalTime / (NUM_CAPTURE * 10000)));
            printf("Write 100th frame to file Video_%d.BMP\n", NUM_CAPTURE);
            
        }
    } while (FALSE);
    
    if (hChannel != NULL){
        MWCloseChannel(hChannel);
        hChannel=NULL;
    }
    
    if(hNotifyEvent!=0){
        MWCloseEvent(hNotifyEvent);
        hNotifyEvent=0;
    }
    
    if(hCaptureEvent!=0){
        MWCloseEvent(hCaptureEvent);
        hCaptureEvent=0;
    }
    
    if(pbImage!=0){
        free((void *)(unsigned long)pbImage);
        pbImage=0;
    }
    
    MWCaptureExitInstance();
    
    printf("\nPress 'Enter' to exit!\n");
    getchar();
    
    return 0;
}

void print_version_and_useage()
{
    
    BYTE byMaj, byMin;
    WORD wBuild;
    MWGetVersion(&byMaj, &byMin, &wBuild);
    printf("Magewell MWCapture SDK V%d.%d.%d - CaptureByInput\n",byMaj,byMin,wBuild);
    printf("Only Pro Capture Series Devices are supported\n");
    printf("Usage:\n");
    printf("CaptureByInput <buffered/buffering> <channel index>\n");
    printf("CaptureByInput <buffered/buffering> <board id>:<channel id>\n\n");
    
}

bool check_file()
{
    FILE* wavFile = NULL;
    wavFile=fopen("temp.bmp","wb");
    if (NULL == wavFile){
        printf("ERROR: can't create file on now dir!\n");
        printf("\nPress ENTER to exit...\n");
        getchar();
        return false;
    }
    else{
        fclose(wavFile);
        wavFile = NULL;
        remove("temp.bmp");
    }
    return true;
}

int get_id(char c)
{
    if(c >= '0' && c <= '9')
        return (int)(c - '0');
    if(c >= 'a' && c <= 'f')
        return (int)(c - 'a' + 10);
    if(c >= 'A' && c <= 'F')
        return (int)(c - 'F' + 10);
    return 0;
}

HCHANNEL open_channel(int argc, char* argv[]){
    HCHANNEL hChannel = NULL;
    int nChannelCount = MWGetChannelCount();
    
    if (0 == nChannelCount) {
        printf("ERROR: Can't find channels!\n");
        return NULL;
    }
    printf("Find %d channels!\n",nChannelCount);
    int nProDevCount = 0;
    int nProDevChannel[32] = {-1};
    for (int i = 0; i < nChannelCount; i++){
        MWCAP_CHANNEL_INFO info;
        MW_RESULT mr = MWGetChannelInfoByIndex(i, &info);
        if ((strcmp(info.szFamilyName, "Pro Capture") == 0)){
            nProDevChannel[nProDevCount] = i;
            nProDevCount++;
        }
    }
    if (nProDevCount <= 0){
        printf("\nERROR: Can't find pro channels!\n");
        return NULL;
    }
    
    printf("Find %d pro channels.\n", nProDevCount);
    
    // Get <board id > <channel id> or <channel index>
    int byBoardId = -1;
    int byChannelId = -1;
    int nDevIndex = 0;
    BOOL bIndex = FALSE;
    
    MWCAP_CHANNEL_INFO videoInfo = { 0 };
    if (argc >= 2) {
        if (NULL != strstr(argv[1], "buffering")) {
            g_enable_bits = MWCAP_NOTIFY_VIDEO_FRAME_BUFFERING;
            g_i_frame = MWCAP_VIDEO_FRAME_ID_NEWEST_BUFFERING;
        } else if (NULL != strstr(argv[1], "buffered")){
            g_enable_bits = MWCAP_NOTIFY_VIDEO_FRAME_BUFFERED;
            g_i_frame = MWCAP_VIDEO_FRAME_ID_NEWEST_BUFFERED;
        } else {
            printf("\nERROR: Invalid params!\n");
            return NULL;
        }
        
    }
    if (argc <= 2) {
        if (MW_SUCCEEDED != MWGetChannelInfoByIndex(nProDevChannel[3], &videoInfo)) {
            printf("ERROR: Can't get channel info!\n");
            return NULL;
        }
        
        bIndex = TRUE;
        nDevIndex = 3;
    }
    else if (NULL == strstr(argv[2], ":")){
        bIndex = TRUE;
        if ((strlen(argv[2]) > 2) || (argv[2][0] > '9') || argv[2][0] < '0') {
            printf("\nERROR: Invalid params!\n");
            return NULL;
        }
        nDevIndex = atoi(argv[2]);
        if(nDevIndex >= nProDevCount){
            printf("ERROR: just have %d channel!\n",nProDevCount);
            return NULL;
        }
    }
    else{
        bIndex = FALSE;
        if (strlen(argv[2]) == 3){
            if ((argv[2][0] >= '0' && argv[2][0] <= '9') || (argv[2][0] >= 'a' && argv[2][0] <= 'f') || (argv[2][0] >= 'A' && argv[2][0] <= 'F')){
                byBoardId = get_id(argv[2][0]);//atoi(argv[1]);
            }
            
            if ((argv[2][2] >= '0' && argv[2][2] <= '3')){
                byChannelId = get_id(argv[2][2]);//atoi(&argv[1][2]);
            }
        }
        
        if (-1 == byBoardId || -1 == byChannelId) {
            printf("\nERROR: Invalid params!\n");
            return NULL;
        }
    }
    // Open channel
    if (bIndex == TRUE){
        char path[128] = {0};
        MWGetDevicePath(nDevIndex, path);
        hChannel = MWOpenChannelByPath(path);
        if (hChannel == NULL) {
            printf("ERROR: Open channel %d error!\n", nDevIndex);
            return NULL;
        }
    }
    else{
        hChannel = MWOpenChannel(byBoardId, byChannelId);
        if (hChannel == NULL) {
            printf("ERROR: Open channel %X:%d error!\n", byBoardId, byChannelId);
            return NULL;
        }
    }
    return hChannel;
}

