////////////////////////////////////////////////////////////////
// File - WebCamII.C
//
// This is a diagnostics application for accessing the WebCamII 
// USB Camera.
// It accesses the hardware via WinDriver functions.
//
// Copyrights (c) Jungo Ltd. 2000
// Written by Nir Borenshtein
////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <time.h>
#include "cpia.h"
#include "screen_lib.h"
#include "usb_diag_lib.h"

// Input of command from user
static char line [4096];
DWORD g_unique_id;

typedef BOOL (*GRAB_FRAME_FUNC)(PVOID pBuffer, FRAME_INFO frameInfo, BOOL first_time, BOOL last_time);
typedef BOOL (*CONVERT_YUV2RGB_FUNC)(PVOID pYCrCb_Buffer, BYTE *pBGR_Buffer, FRAME_INFO frameInfo);
typedef BOOL (*DRAW_PIC_FUNC)(PVOID pBuffer, FRAME_INFO frameInfo, BOOL first_time, BOOL last_time);

typedef struct  
{
    GRAB_FRAME_FUNC grab_frame_func;
    CONVERT_YUV2RGB_FUNC convert_yuv2rgb_func;
    DRAW_PIC_FUNC draw_pic_func;
    FRAME_INFO frameInfo;
    BOOL fStopped;
    HANDLE hThread;
} THREAD_DATA;

// Thread functions
void DrawPicturesOnScreen(THREAD_DATA *pContGrab);
DWORD WINAPI DrawPicturesHandler(void *pParam);
void StopDrawing(THREAD_DATA *pContGrab);

int main(int argc, char *argv[])
{
    UINT cmd;
    PVOID pYCrCb_Buffer = NULL;
    BYTE *pBGR_Buffer = NULL;
    FRAME_INFO frameInfo;
    DWORD data_type;
    char path[255];

    do
    {
        printf ("\n");
        printf ("WebCamII Main Menu\n");
        printf ("------------------\n");
        printf ("1. Scan for USB Devices.\n");  
        printf ("2. Grab Frame.\n");
        printf ("3. Store Frame Data on disk.\n");
        printf ("4. Continues Grabing.\n");
        printf ("99. Exit.\n");
        printf ("Enter option: ");
        cmd = 0;
        gets(line);
        sscanf (line, "%d", &cmd);

        switch (cmd)
        {
        case 1:
            USB_Print_device_info(0,0);
            break;
        case 2:
            printf ("Please enter type of frame you want to grab - QCIF/CIF (0/1): ");
            gets(line);
            sscanf (line, "%x", &frameInfo.frameType);
            printf ("\n");
            if ((frameInfo.frameType > 1) || (frameInfo.frameType < 0))
            {
                printf("Invalid Frame Type\n");
                break;
            }
            if (!g_unique_id)
            {
                printf ("Please enter the uniqueId of the camera: ");
                gets(line);
                sscanf (line, "%d", &g_unique_id);
                printf ("\n");
            }

            frameInfo.uniqueId = g_unique_id;
            // Free locked memory
            if (pYCrCb_Buffer)
                free(pYCrCb_Buffer);
            pYCrCb_Buffer = NULL;
            if (pBGR_Buffer)
                free(pBGR_Buffer);
            pBGR_Buffer = NULL;
            
            if (frameInfo.frameType == QCIF)
            {
                frameInfo.frameType = QCIF;
                frameInfo.dwBuffSize = QCIF_BUFFER_SIZE;
                frameInfo.dwBytesInLine = QCIF_BYTES_IN_LINE;
                frameInfo.frameRowNum = QCIF_ROW_NUM;
                frameInfo.frameColNum = QCIF_COL_NUM;
                if(!(pYCrCb_Buffer = malloc(QCIF_BUFFER_SIZE)))
                {
                    printf("Failed allocating QCIF buffer\n");
                    break;
                }
            }
            else
            {
                frameInfo.frameType = CIF;
                frameInfo.dwBuffSize = CIF_BUFFER_SIZE;
                frameInfo.dwBytesInLine = CIF_BYTES_IN_LINE;
                frameInfo.frameRowNum = CIF_ROW_NUM;
                frameInfo.frameColNum = CIF_COL_NUM;
                if(!(pYCrCb_Buffer = malloc(CIF_BUFFER_SIZE)))
                {
                    printf("Failed allocating CIF buffer\n");
                    break;
                }
            }
            frameInfo.subSample = SS_422;
            printf("Grabbing frame, please wait ...\n");
            if (!GrabFrame(pYCrCb_Buffer, frameInfo, TRUE, TRUE))
            {
                printf("Could not grab a frame!\n");
                if (!pYCrCb_Buffer)
                    free(pYCrCb_Buffer);
                pYCrCb_Buffer = NULL;
                g_unique_id = 0;
                break;
            }
            printf("Operation ended succesfuly.\n");
            
            printf("Converting data, please wait ...\n");
            pBGR_Buffer = (BYTE *)malloc(sizeof(DWORD) * frameInfo.frameRowNum * frameInfo.frameColNum);
            if (!pBGR_Buffer)
            {
                printf("Failed allocating BGR buffer\n");
                free(pYCrCb_Buffer);
                pYCrCb_Buffer = NULL;
                break;
            }
            if (!ConvertYUV2RGB(pYCrCb_Buffer, pBGR_Buffer, frameInfo))
            {
                printf("Error was detected while conversion\n");
                if (pBGR_Buffer)
                    free(pBGR_Buffer);
                pBGR_Buffer = NULL;
                if (pYCrCb_Buffer)
                    free(pYCrCb_Buffer);
                pYCrCb_Buffer = NULL;
                break;
            }
            printf("Operation ended succesfuly.\n");

            printf("Printing data, please wait ...\n");
            DrawPicture(pBGR_Buffer, frameInfo, TRUE, TRUE);
            printf("Operation ended succesfuly.\n");
            break;
        case 3:
            BZERO(path);
            printf ("Which kind of data do you want to store - YCrCb/RGB (0/1): ");
            gets(line);
            sscanf (line, "%x", &data_type);
            printf ("\n");
            if ((data_type < 0) || (data_type > 1))
            {
                printf("Invalid data type\n");
                break;
            }
            printf ("Please enter path with file name: ");
            gets(line);
            sscanf (line, "%s", &path);
            printf ("\n");
            if (path)
            {
                if (data_type == 0)
                {
                    if (pYCrCb_Buffer)
                        StoreFrameOnFile(pYCrCb_Buffer, frameInfo.dwBuffSize, path);
                    else
                        printf("buffer is empty\n");
                }
                else
                {
                    if (pBGR_Buffer)
                        StoreFrameOnFile(pBGR_Buffer, frameInfo.dwBuffSize, path);
                    else
                        printf("buffer is empty\n");
                }

            }
            break;
        case 4:
            {
                THREAD_DATA contGrab;
                BYTE c;
                
                BZERO(contGrab);
                if (!g_unique_id)
                {
                    printf ("Please enter the uniqueId of the camera: ");
                    gets(line);
                    sscanf (line, "%d", &g_unique_id);
                    printf ("\n");
                }
                contGrab.frameInfo.uniqueId = g_unique_id;
                contGrab.frameInfo.frameType = QCIF;
                contGrab.frameInfo.dwBuffSize = QCIF_BUFFER_SIZE;
                contGrab.frameInfo.dwBytesInLine = QCIF_BYTES_IN_LINE;
                contGrab.frameInfo.frameRowNum = QCIF_ROW_NUM;
                contGrab.frameInfo.frameColNum = QCIF_COL_NUM;
                contGrab.frameInfo.subSample = SS_422;
                contGrab.grab_frame_func = GrabFrame;
                contGrab.convert_yuv2rgb_func = ConvertYUV2RGB;
                contGrab.draw_pic_func = DrawPicture;

                printf ("Press <Enter> to start drawing. While drawing, press <Enter> to stop\n\n");
                getchar();
                DrawPicturesOnScreen(&contGrab);
                while ((c=getchar()) != 10) {}   // ESC code
                StopDrawing(&contGrab);
                break;
            }
            break;
        }
    } while (cmd!=99);

    if (pYCrCb_Buffer)
        free(pYCrCb_Buffer);
    if (pBGR_Buffer)
        free(pBGR_Buffer);
    return 0;
}

void StopDrawing(THREAD_DATA *pContGrab)
{
    WD_USB_TRANSFER transfer;
    BZERO(transfer);

    if (!pContGrab->hThread)
        return;

    printf("Stop drawing pictures\n");
    pContGrab->fStopped = TRUE;

    WaitForSingleObject(pContGrab->hThread, INFINITE);
    CloseHandle(pContGrab->hThread);
    pContGrab->hThread = NULL;
}

DWORD WINAPI DrawPicturesHandler(void *pParam)
{
    THREAD_DATA *pContGrab = (THREAD_DATA*) pParam;
    PVOID pYCrCb_Buffer = NULL;
    BYTE *pBGR_Buffer = NULL;
    DWORD rc;
    BOOL first = TRUE;
    BOOL last = FALSE;
    DWORD count = 0;
    time_t start_time, end_time;
    DWORD secs;

    if(!(pYCrCb_Buffer = malloc(pContGrab->frameInfo.dwBuffSize)))
    {
        printf("Failed allocating YUV buffer\n");
        rc = 1;
        goto Exit;
    }

    pBGR_Buffer = (BYTE *)malloc(sizeof(DWORD) * pContGrab->frameInfo.frameRowNum * pContGrab->frameInfo.frameColNum);
    if (!pBGR_Buffer)
    {
        printf("Failed allocating BGR buffer\n");
        rc = 1;
        goto Exit;
    }
    time(&start_time);
    for (;!last;)
    {
        count++;
        if(pContGrab->fStopped)
            last = TRUE;
        if (!pContGrab->grab_frame_func(pYCrCb_Buffer, pContGrab->frameInfo, first, last))
        {
            printf("Could not grab a frame!\n");
            g_unique_id = 0;
            goto Exit;
            break;
        }
        if (!pContGrab->convert_yuv2rgb_func(pYCrCb_Buffer, pBGR_Buffer, pContGrab->frameInfo))
        {
            printf("Error was detected while conversion\n");
            goto Exit;
            break;
        }   
        pContGrab->draw_pic_func(pBGR_Buffer, pContGrab->frameInfo, first, last);
        first = FALSE;
    }
    time(&end_time);
    secs = end_time - start_time;
    printf("worked %d seconds, showed %d pictures. %d.%02d pics/seconds\n",
        secs, count, count/secs, ((count*100)/secs % 100));

    rc = 0;

Exit:
    if (pYCrCb_Buffer)
        free(pYCrCb_Buffer);
    if (pBGR_Buffer)
        free(pBGR_Buffer);
    return rc;
}

void DrawPicturesOnScreen(THREAD_DATA *pContGrab)
{
    // Start the running thread
    DWORD threadId;
    pContGrab->fStopped = FALSE;
    printf("Start drawing pictures\n");
    pContGrab->hThread = CreateThread (0, 0x1000, DrawPicturesHandler, 
        (PVOID)pContGrab, 0, &threadId);
}