/************************************************************************************************/
// InputSignalNotify.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 <string.h>
#include <stdio.h>
#include <pthread.h>
#include "LibMWCapture/MWCapture.h"

void GetVideoColorName(MWCAP_VIDEO_COLOR_FORMAT color, char* pszName, int nSize);

void print_version_and_useage();
void getVideoSignal(HCHANNEL channel);
void getAudioSignal(HCHANNEL channel);

int get_id(char c);

HCHANNEL g_hChannel = NULL;
MWCAP_PTR g_existEvent = 0;

static void *audio_notify_thread_proc(void *data)
{
    MWCAP_PTR hAudioInputSignalEvent = 0;
    HNOTIFY hAudioNotify = NULL;
    do {
        hAudioInputSignalEvent = MWCreateEvent();
        if (0 == hAudioInputSignalEvent) {
            printf("ERROR:alloc event failed\n");
            break;
        }

        hAudioNotify = MWRegisterNotify(g_hChannel, hAudioInputSignalEvent, MWCAP_NOTIFY_AUDIO_SIGNAL_CHANGE);
        if (hAudioNotify == 0) {
            printf("ERROR:register audio signal change notify failed\n");
            break;
        }
        do {
            MWCAP_PTR event[2] = {g_existEvent, hAudioInputSignalEvent};
            DWORD dwRet = MWMultiWaitEvent(event, 2, -1);
            if (dwRet == 0 || dwRet&0x01) {
                break;;
            } else if (dwRet&0x02) {
                printf("\n\n--- Audio signal changed ---\n");
                getAudioSignal(g_hChannel);
                printf("--- Audio signal changed end ---\n");
            }
        } while (true);
        break;
    } while (true);
    
    if(hAudioNotify) {
        MWUnregisterNotify(g_hChannel, hAudioNotify);
        hAudioNotify = NULL;
    }

    if(hAudioInputSignalEvent != 0) {
        MWCloseEvent(hAudioInputSignalEvent);
        hAudioInputSignalEvent = 0;
    }
    printf("\nAudio signal thread exist\n");
    return (void *)0;
}

static void *video_notify_thread_proc(void *data)
{
    MWCAP_PTR hVideoInputSignalEvent = 0;
    HNOTIFY hVideoNotify = NULL;
    do {
        hVideoInputSignalEvent = MWCreateEvent();
        if (0 == hVideoInputSignalEvent) {
            printf("ERROR:alloc event failed\n");
            break;
        }

        hVideoNotify = MWRegisterNotify(g_hChannel, hVideoInputSignalEvent, MWCAP_NOTIFY_VIDEO_SIGNAL_CHANGE);
        if (hVideoNotify == 0) {
            printf("ERROR:register vidoe signal change notify failed\n");
            break;
        }
        
        do {
            MWCAP_PTR event[2] = {g_existEvent, hVideoInputSignalEvent};
            DWORD dwRet = MWMultiWaitEvent(event, 2, -1);
            if (dwRet == 0 || dwRet&0x01) {
                break;;
            } else if ((dwRet&0x02)) {
                printf("\n\n--- Video signal changed ---\n");
                getVideoSignal(g_hChannel);
                printf("--- Video signal changed end ---\n");

            }
        } while (true);
        break;
    } while (true);

    
    if(hVideoNotify) {
        MWUnregisterNotify(g_hChannel, hVideoNotify);
        hVideoNotify = NULL;
    }

    if(hVideoInputSignalEvent != 0) {
        MWCloseEvent(hVideoInputSignalEvent);
        hVideoInputSignalEvent = 0;
    }
    printf("\nVudio signal thread exist\n");
    return (void *)0;
}

int main(int argc,char* argv[])
{
    //Version
    print_version_and_useage();
    if (argc > 2){
        printf("ERROR: Invalid params!\n");
        printf("\nPress 'Enter' to exit!\n");
        getchar();
        return 0;
    }

    if(!MWCaptureInitInstance()){
        printf("have InitilizeFailed");
    }

    pthread_t videoThreadID = 0;
    pthread_t audioThreadID = 0;
    do {
        MWRefreshDevice();
        int nChannelCount = MWGetChannelCount();

        if (0 == nChannelCount) {
            printf("ERROR: Can't find channels!\n");
            break;
        }
        printf("Find %d channels.\n", nChannelCount);
        // Get <board id > <channel id> or <channel index>
        int byBoardId = -1;
        int byChannelId = -1;
        int nDevIndex = -1;
        BOOL bIndex = FALSE;
        
        MWCAP_CHANNEL_INFO videoInfo = { 0 };
        if (argc == 1) {
            bIndex = TRUE;
            nDevIndex = 1;
        }
        else {
            if (NULL == strstr(argv[1], ":")){
                bIndex = TRUE;

                if (strlen(argv[1]) > 2)
                    nDevIndex = -1;
                else if (strlen(argv[1]) == 2){
                    if ((argv[1][0] >= '0' && argv[1][0] <= '9') && (argv[1][1] >= '0' && argv[1][1] <= '9'))
                        nDevIndex = atoi(argv[1]);
                    else
                        nDevIndex = -1;
                }
                else if (strlen(argv[1]) == 1)
                    nDevIndex = (argv[1][0] >= '0' && argv[1][0] <= '9') ? atoi(argv[1]) : -1;

                if (nDevIndex < 0){
                    printf("\nERROR: Invalid params!\n");
                    break;
                }
                if(nDevIndex >= nChannelCount){
                    printf("ERROR: just have %d channel!\n",nChannelCount);
                    break;
                }
            }
            else{
                bIndex = FALSE;

                if (strlen(argv[1]) == 3){
                    if ((argv[1][0] >= '0' && argv[1][0] <= '9') || (argv[1][0] >= 'a' && argv[1][0] <= 'f') || (argv[1][0] >= 'A' && argv[1][0] <= 'F'))
                        byBoardId = get_id(argv[1][0]);//atoi(argv[1]);
                    else
                        byBoardId = -1;

                    if ((argv[1][2] >= '0' && argv[1][2] <= '3'))
                        byChannelId = get_id(argv[1][2]);//atoi(&argv[1][2]);
                    else
                        byChannelId = -1;
                }
                else{
                    byBoardId = -1;
                    byChannelId = -1;
                }

                if (-1 == byBoardId || -1 == byChannelId){
                    printf("\nERROR: Invalid params!\n");
                    break;
                }
            }
        }

        // Open channel
        if (bIndex == TRUE){
            char path[128] = {0};
            MWGetDevicePath(nDevIndex, path);
            g_hChannel = MWOpenChannelByPath(path);
            if (g_hChannel == NULL) {
                printf("ERROR: Open channel %d error!\n", nDevIndex);
                break;
            }
        }
        else {
            g_hChannel = MWOpenChannel(byBoardId, byChannelId);
            if (g_hChannel == NULL) {
                printf("ERROR: Open channel %X:%d error!\n", byBoardId, byChannelId);
                break;
            }
        }

        if (MW_SUCCEEDED != MWGetChannelInfo(g_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);
        
        g_existEvent = MWCreateEvent();
        if (0 == g_existEvent) {
            printf("ERROR:alloc event failed\n");
            break;
        }
        
        if(pthread_create(&videoThreadID, NULL, video_notify_thread_proc, g_hChannel)!=0){
            printf("ERROR:create video trhead failed\n");
            break;
        }
        
        if(pthread_create(&audioThreadID, NULL, audio_notify_thread_proc, g_hChannel)!=0){
            printf("ERROR:create audio trhead failed\n");
            break;
        }
        
    } while (FALSE);

    printf("\nPress 'Enter' to exit!\n");
    getchar();
    MWSetEvent(g_existEvent);
    if (videoThreadID != 0) {
        pthread_join(videoThreadID, NULL);
    }
    if (audioThreadID != 0) {
        pthread_join(audioThreadID, NULL);
    }

    if (g_hChannel != NULL)
        MWCloseChannel(g_hChannel);

    MWCaptureExitInstance();

    return 0;
}

void getVideoSignal(HCHANNEL channel)
{
    MWCAP_VIDEO_SIGNAL_STATUS vStatus;
    MW_RESULT xr = MWGetVideoSignalStatus(channel, &vStatus);
    if (xr == MW_SUCCEEDED) {
        switch(vStatus.state)
        {
        case MWCAP_VIDEO_SIGNAL_LOCKED:
        {
            printf("Video Signal status: LOCKED\n");
            break;
        }
        case MWCAP_VIDEO_SIGNAL_LOCKING:
        {
            printf("Video Signal status: LOCKING\n");
            break;
        }
        case MWCAP_VIDEO_SIGNAL_UNSUPPORTED:
        {
            printf("Video Signal status: UNSUPPORTED\n");
            break;
        }
        case MWCAP_VIDEO_SIGNAL_NONE:
        {
            printf("Video Signal status: NONE\n");
            break;
        }
        default:
            break;
        }

        if (vStatus.state == MWCAP_VIDEO_SIGNAL_LOCKED)
        {
            char szColorName[16];
            GetVideoColorName(vStatus.colorFormat, szColorName, 16);

            double dFrameDuration = (vStatus.bInterlaced == TRUE) ? (double)20000000 / vStatus.dwFrameDuration : (double)10000000 / vStatus.dwFrameDuration;

            printf("Video Signal: \n");
            printf("---x, y: (%d, %d)\n", vStatus.x, vStatus.y);
            printf("---cx x cy: (%d x %d)\n", vStatus.cx, vStatus.cy);
            printf("---cxTotal x cyTotal: (%d x %d)\n", vStatus.cxTotal, vStatus.cyTotal);
            printf("---bInterlaced: %d\n", vStatus.bInterlaced);
            printf("---dwFrameDuration: %0.2f\n", dFrameDuration);
            printf("---nAspectX: %d\n", vStatus.nAspectX);
            printf("---nAspectY: %d\n", vStatus.nAspectY);
            printf("---bSegmentedFrame: %d\n", vStatus.bSegmentedFrame);
            printf("---colorFormat: %s\n", szColorName);
        }
    }
}

void getAudioSignal(HCHANNEL channel)
{
    MWCAP_AUDIO_SIGNAL_STATUS aStatus;
    MW_RESULT xr = MWGetAudioSignalStatus(channel, &aStatus);
    if (xr == MW_SUCCEEDED) {
        printf("Audio Signal Valid: %d\n", aStatus.wChannelValid > 0 ? 1 : 0);
        
        if (aStatus.bChannelStatusValid == 1){
            char chSupChannels[128] = {0};
            for (int i = 0; i < 4; i++){
                if (aStatus.wChannelValid & (0x01 << i))
                    sprintf(chSupChannels, "%s %d&%d;", chSupChannels, (i * 2 + 1), (i * 2 + 2));
            }
            
            printf("Audio Signal: \n");
            printf("---wChannelValid: %s\n", chSupChannels);
            printf("---bLPCM: %d\n", aStatus.bLPCM);
            printf("---cBitsPerSample: %d\n", aStatus.cBitsPerSample);
            printf("---dwSampleRate: %d\n", aStatus.dwSampleRate);
        }
    }
}

void GetVideoColorName(MWCAP_VIDEO_COLOR_FORMAT color, char* pszName, int nSize)
{
    switch (color) {
    case MWCAP_VIDEO_COLOR_FORMAT_UNKNOWN:
        strncpy(pszName, "Unknown", nSize);
        break;
    case MWCAP_VIDEO_COLOR_FORMAT_RGB:
        strncpy(pszName,"RGB",nSize);
        break;
    case MWCAP_VIDEO_COLOR_FORMAT_YUV601:
        strncpy(pszName, "YUV BT.601",nSize );
        break;
    case MWCAP_VIDEO_COLOR_FORMAT_YUV709:
        strncpy(pszName, "YUV BT.709", nSize);
        break;
    case MWCAP_VIDEO_COLOR_FORMAT_YUV2020:
        strncpy(pszName, "YUV BT.2020",nSize);
        break;
    case MWCAP_VIDEO_COLOR_FORMAT_YUV2020C:
        strncpy(pszName, "YUV BT.2020C", nSize);
        break;
    default:
        strncpy(pszName, "Unknown",nSize);
        break;
    }
}

void print_version_and_useage()
{
    
    BYTE byMaj, byMin;
    WORD wBuild;
    MWGetVersion(&byMaj, &byMin, &wBuild);
    printf("Magewell MWCapture SDK V%d.%d.%d - InputSignalNotify\n",byMaj,byMin,wBuild);
    printf("Usage:\n");
    printf("InputSignalNotify <channel index>\n");
    printf("InputSignalNotify <board id>:<channel id>\n\n");

}

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 - 'A' + 10);
    return 0;
}
