/****************************************************************************/
/* Copyright 2012 MBARI.                                                    */
/* Monterey Bay Aquarium Research Institute Proprietary Information.        */
/* All rights reserved.                                                     */
/****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <windows.h>
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <vfw.h>

/* Sensoray includes */
#include "s2255.h"
#include "s2255f.mingw.h"

/* OpenCV 2.4.2 includes: */
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <opencv2/nonfree/nonfree.hpp>


/* prototypes and defines */
FILE* logFile = NULL;
int logMsg(char* msg);

#define SOCK_BUFF_SZ    1024

int main(int argc, char* argv[])
{
    int result;
    
    /* process loop vars */
    int end_loop = 0;
    int image_cnt = 1;
    char image_name[512];

    /* openCV vars */
    IplImage* bmp_frame;
    IplImage* frame;

    /* openCV SIFT vars */
    cv::Mat in_img;
    cv::SIFT sift;
    cv::vector<cv::KeyPoint> key_points;
    cv::Mat descriptors;
    cv::Mat out_img;
    cv::FileStorage fs;

    /* Sensoray vars */
    HANDLE hdev;
    int board = 0;
    int channel = 1;
    int im_size;
    BITMAPINFO bmi;
    MODE2255 mymode = {DEF_MODE_NTSC_CONT};

    /* windows socket vars */
    WSADATA wsa_data;

    SOCKET sock;
    SOCKADDR_IN recv_addr;
    SOCKADDR_IN send_addr;
    
    int send_addr_size = sizeof(send_addr);
    char sock_buff[SOCK_BUFF_SZ];
    
    /* log file vars */
    char log_buff[1024];

/****************************************************************************/
/*                        startup and usage banners                         */
/****************************************************************************/
    
    printf("imageGrabber built on %s at %s\n\n", __DATE__, __TIME__);

    if (argc != 2) 
    { 
        printf("Usage: imageGrabber.exe [port]\n");
        return -1;
    }

/****************************************************************************/
/*                          init openCV components                          */
/****************************************************************************/
    
    frame = cvCreateImage(cvSize(640,480), IPL_DEPTH_8U, 3);
    cvZero(frame);
    
    bmp_frame = cvCreateImage(cvSize(640,480), IPL_DEPTH_8U, 3);
    cvZero(bmp_frame);

    /* the Sensoray captures RGB frames as window style bitmaps */
    /* 1 - bottom-left origin (Windows bitmaps style) */
    /* frame->origin = 1; */
    
    /********************************************************/
    /* note: frame->origin was not being picked up by       */
    /* cv::cvarrToMat, so I'm now flipping by using two     */
    /* frames and cvConvertImage(src, dst, CV_CVTIMG_FLIP); */
    /********************************************************/

/****************************************************************************/
/*                          init Sensoray board                             */
/****************************************************************************/
    
    result = S2255_DeviceOpen(board, &hdev);
    
    if ( result != 0 )
    {
        printf("Error opening Device Error Num %d\n", result);

        /* clean up and exit with error */
        cvReleaseImage(&frame);
        cvReleaseImage(&bmp_frame);
        return -1;
    }

    /* setup capture mode, assume RGB color */
    mymode.color = COLOR_RGB;
    mymode.scale = SCALE_4CIFSI;

    result = S2255_SetMode(hdev, channel, &mymode);
    if ( result != 0 )
    {
        printf("Failed to set Sensoray mode Error Num %d\n", result);
        
        /* clean up and exit with error */
        cvReleaseImage(&frame);
        cvReleaseImage(&bmp_frame);
        S2255_DeviceClose(hdev);
        return -1;
    }

    /* find out what size of buffer we need */
    im_size = S2255_get_image_size(&mymode);

    if (bmp_frame->imageSize != im_size)
    {
        printf("Error: Image frame size mismatch\n");
        printf("OpenCV frame   = %d bytes\n", bmp_frame->imageSize);
        printf("Sensoray frame = %d bytes\n", im_size);

        /* clean up and exit with error */
        cvReleaseImage(&frame);
        cvReleaseImage(&bmp_frame);
        S2255_DeviceClose(hdev);
        return -1;
    }

/****************************************************************************/
/*                          init windows sockets                            */
/****************************************************************************/
    
    /* starup winsock winsock */
    result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
    
    if ( result != NO_ERROR ) 
    {
        printf("WSAStartup failed with error %d\n", result);
        
        /* clean up and exit with error */
        cvReleaseImage(&frame);
        cvReleaseImage(&bmp_frame);
        S2255_DeviceClose(hdev);
        return -1;
    }

    /* open the datagram socket */
    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if (sock == INVALID_SOCKET) 
    {
        printf("socket failed with error %d\n", WSAGetLastError());
        
        /* clean up and exit with error */
        cvReleaseImage(&frame);
        cvReleaseImage(&bmp_frame);
        S2255_DeviceClose(hdev);
        WSACleanup();
        return -1;
    }

    /* bind the socket to the port you want to listen on */
    recv_addr.sin_family = AF_INET;
    recv_addr.sin_port = htons(atoi(argv[1]));
    recv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    result = bind(sock, (SOCKADDR*)&recv_addr, sizeof(recv_addr));

    if ( result != 0 ) 
    {
        printf("bind failed with error %d\n", WSAGetLastError());
        
        /* clean up and exit with error */
        cvReleaseImage(&frame);
        cvReleaseImage(&bmp_frame);
        S2255_DeviceClose(hdev);
        closesocket(sock);
        WSACleanup();
        return -1;
    }

/****************************************************************************/
/*                               init logfile                               */
/****************************************************************************/

    logFile = fopen("dataLog.txt", "a+");

/****************************************************************************/
/*                              processing loop                             */
/****************************************************************************/

    /* open a display window named "image" */
    cvNamedWindow("image", CV_WINDOW_AUTOSIZE);

    while ( !end_loop )
    {
        printf("<----------- waiting for image%04d ----------->\n", image_cnt);

        result = recvfrom(sock, sock_buff, SOCK_BUFF_SZ, 0, 
                          (SOCKADDR*)&send_addr, &send_addr_size);

        if ( result == SOCKET_ERROR ) 
        {
            printf("recvfrom failed with error %d\n", WSAGetLastError());
        }

        /* terminate received message */
        printf("received datagram from %s\n", inet_ntoa(send_addr.sin_addr));

        if ( result > 0 )
            sock_buff[result] = '\0';

        printf("datagram payload [%s]\n", sock_buff);

        /* send ACK back to sender */
        printf("sending ACK to the remote computer\n");

        result = sendto(sock, "ACK", strlen("ACK"), 0, 
                        (SOCKADDR*)&send_addr, sizeof(send_addr));

        if ( result == SOCKET_ERROR )
        {
            printf("sendto failed with error: %d\n", WSAGetLastError());

            /* clean up and exit with error */
            cvReleaseImage(&frame);
            cvReleaseImage(&bmp_frame);
            S2255_DeviceClose(hdev);
            fclose(logFile);
            closesocket(sock);
            WSACleanup();
            return -1;
        }

        /* make log entry from datagram payload */

        /* log the contents of the datagram to the logfile */
        sprintf(log_buff, "%04d\t%s", image_cnt, sock_buff);   
        logMsg(log_buff);

        /* grab a frame from the Sensoray */
        result = S2255_GrabFrame(hdev, channel, 
                                 (unsigned char*)bmp_frame->imageData, 
                                 bmp_frame->imageSize, 1000, &bmi);

        if ( result != 0 )
            printf("S2255_GrabFrame(...) failed with erro %d\n", result);

        /* flip the image here so it can be used with cv::Mat */
        cvConvertImage(bmp_frame, frame, CV_CVTIMG_FLIP);

        /* convert the frame to a cv:Mat image */
        in_img = cv::cvarrToMat(frame);

        /* peform the SIFT algorithm */
        sift(in_img, cv::Mat(), key_points, descriptors);
        
        /* draw the keypoints on an output image */
        drawKeypoints(in_img, key_points, out_img);
        cv::imshow("image", out_img);

        /* save key_points and descriptors as yaml files */
        sprintf(image_name, "keypoints%04d.yml", image_cnt);
        fs.open(image_name, cv::FileStorage::WRITE);
        fs << "key_points" << key_points;
        fs.release();

        sprintf(image_name, "descriptors%04d.yml", image_cnt);
        fs.open(image_name, cv::FileStorage::WRITE);
        fs << "descriptors" << descriptors;
        fs.release();

        /* cvShowImage doesn't seem to display without a small pause */
        cvWaitKey(33);

        /* save the image */
        sprintf(image_name, "image%04d.jpg", image_cnt);
        cvSaveImage(image_name, frame, 0);
        printf("image %d saved\n", image_cnt++);

        /* check for exit request */    
        if ( strnicmp(sock_buff, "EXIT", 4) == 0 )
        {
            printf("recieved EXIT request, terminating acquisition loop\n");
            end_loop = 1;
        }
    }

    /* clean up and exit */
    cvDestroyAllWindows();
    cvReleaseImage(&frame);
    cvReleaseImage(&bmp_frame);
    S2255_DeviceClose(hdev);
    fclose(logFile);
    closesocket(sock);
    WSACleanup();

    return 0;
}

int logMsg(char* msg)
{
    time_t time_now;
    char time_str[32];
    struct tm *tmp;

    if ( logFile == (FILE*)NULL )
        return -1;

    time(&time_now);
    tmp = localtime(&time_now);
    strftime(time_str, 32, "%b %d %X", tmp);

/*    fprintf(logFile, "%s, %s\n", time_str, msg);*/
    fprintf(logFile, "%s\n", msg);

    fflush(logFile);

    return 0;
}


