/****************************************************************************
*                                                                           *
*   Nite 1.3 - Single Control Sample                                        *
*                                                                           *
*   Author:     Oz Magal                                                    *
*                                                                           *
****************************************************************************/

/****************************************************************************
*                                                                           *
*   Nite 1.3                                                                *
*   Copyright (C) 2006 PrimeSense Ltd. All Rights Reserved.                 *
*                                                                           *
*   This file has been provided pursuant to a License Agreement containing  *
*   restrictions on its use. This data contains valuable trade secrets      *
*   and proprietary information of PrimeSense Ltd. and is protected by law. *
*                                                                           *
****************************************************************************/

//-----------------------------------------------------------------------------
// Headers
//-----------------------------------------------------------------------------
// General headers
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

// OpenNI headers
#include <XnOpenNI.h>
 
// NITE headers
#include <XnVSessionManager.h>
#include "XnVMultiProcessFlowClient.h"
#include <XnVWaveDetector.h>
#include "kbhit.h"
#include "signal_catch.h"

// xml to initialize OpenNI
#define SAMPLE_XML_FILE "../../Data/Sample-Tracking.xml"
static int uinp_fd = -1; 
struct uinput_user_dev uinp; // uInput device structure 
struct input_event event; // Input device structure 

XnBool g_bQuit = false;

int setup_uinput_device() 
{ 
    int i=0; 
    uinp_fd = open("/dev/uinput", O_WRONLY | O_NDELAY); 
    if (!uinp_fd) 
    { 
      printf("Unable to open /dev/uinput\n"); 
        return -1; 
    } 
    memset(&uinp,0,sizeof(uinp)); // Intialize the uInput device to NULL 
    strncpy(uinp.name, "Kinect Mouse", UINPUT_MAX_NAME_SIZE); 
    uinp.id.version = 4; 
    uinp.id.bustype = BUS_USB; 
    ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY); 
    ioctl(uinp_fd, UI_SET_EVBIT, EV_REL); 
    ioctl(uinp_fd, UI_SET_RELBIT, REL_X); 
    ioctl(uinp_fd, UI_SET_RELBIT, REL_Y); 
    ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MOUSE); 
    ioctl(uinp_fd, UI_SET_KEYBIT, BTN_TOUCH); 
    ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MOUSE); 
    ioctl(uinp_fd, UI_SET_KEYBIT, BTN_LEFT); 
    ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MIDDLE); 
    ioctl(uinp_fd, UI_SET_KEYBIT, BTN_RIGHT); 
    ioctl(uinp_fd, UI_SET_KEYBIT, BTN_FORWARD); 
    ioctl(uinp_fd, UI_SET_KEYBIT, BTN_BACK); 
    write(uinp_fd, &uinp, sizeof(uinp)); 
    if (ioctl(uinp_fd, UI_DEV_CREATE)) 
    { 
        printf("Unable to create UINPUT device."); 
        return -1; 
    } 
    return 1; 
} 
void move_cursor(int x, int y ) 
{ 
    memset(&event, 0, sizeof(event)); 
    gettimeofday(&event.time, NULL); 
    event.type = EV_REL; 
    event.code = REL_X; 
    event.value = x; 
    write(uinp_fd, &event, sizeof(event)); 
    event.type = EV_REL; 
    event.code = REL_Y; 
    event.value = y; 
    write(uinp_fd, &event, sizeof(event)); 
    event.type = EV_SYN; 
    event.code = SYN_REPORT; 
    event.value = 0; 
    write(uinp_fd, &event, sizeof(event)); 
}
//-----------------------------------------------------------------------------
// Callbacks
//-----------------------------------------------------------------------------

// Callback for when the focus is in progress
void XN_CALLBACK_TYPE SessionProgress(const XnChar* strFocus, const XnPoint3D& ptFocusPoint, XnFloat fProgress, void* UserCxt)
{
        printf("Session progress (%6.2f,%6.2f,%6.2f) - %6.2f [%s]\n", ptFocusPoint.X, ptFocusPoint.Y, ptFocusPoint.Z, fProgress,  strFocus);
}
// callback for session start
void XN_CALLBACK_TYPE SessionStart(const XnPoint3D& ptFocusPoint, void* UserCxt)
{
        printf("Session started. Please wave (%6.2f,%6.2f,%6.2f)...\n", ptFocusPoint.X, ptFocusPoint.Y, ptFocusPoint.Z);
}
// Callback for session end
void XN_CALLBACK_TYPE SessionEnd(void* UserCxt)
{
        printf("Session ended. Please perform focus gesture to start session\n");
}
// Callback for wave detection
void XN_CALLBACK_TYPE OnWaveCB(void* cxt)
{
        printf("Wave!\n");
}
// callback for a new position of any hand
void XN_CALLBACK_TYPE OnPointUpdate(const XnVHandPointContext* pContext, void* cxt)
{
        //printf("%d: (%f,%f,%f) [%f]\n", pContext->nID, pContext->ptPosition.X, pContext->ptPosition.Y, pContext->ptPosition.Z, pContext->f
Time);
        //printf("%f,%f,%f\n",  pContext->ptPosition.X, pContext->ptPosition.Y, pContext->ptPosition.Z);
    move_cursor((int)(pContext->ptPosition.X/4),(int) -(pContext->ptPosition.Y/4));
}

//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------

// this sample can run either as a regular sample, or as a client for multi-process (remote mode)
int main(int argc, char** argv)
{
        xn::Context context;
        XnVSessionGenerator* pSessionGenerator;
        XnBool bRemoting = FALSE;

        if (argc > 1)
        {
                // remote mode
                context.Init();
                printf("Running in 'Remoting' mode (Section name: %s)\n", argv[1]);
                bRemoting = TRUE;

                // Create multi-process client
                pSessionGenerator = new XnVMultiProcessFlowClient(argv[1]);

                XnStatus rc = ((XnVMultiProcessFlowClient*)pSessionGenerator)->Initialize();
                if (rc != XN_STATUS_OK)
                {
                        printf("Initialize failed: %s\n", xnGetStatusString(rc));
                        delete pSessionGenerator;
                        return 1;
                }
        }
        else
        {
                // Local mode
                // Create context
                XnStatus rc = context.InitFromXmlFile(SAMPLE_XML_FILE);
                if (rc != XN_STATUS_OK)
                {
                        printf("Couldn't initialize: %s\n", xnGetStatusString(rc));
                        return 1;
                }
  // Create the Session Manager
                pSessionGenerator = new XnVSessionManager();
                rc = ((XnVSessionManager*)pSessionGenerator)->Initialize(&context, "Click", "RaiseHand");
                if (rc != XN_STATUS_OK)
                {
                        printf("Session Manager couldn't initialize: %s\n", xnGetStatusString(rc));
                        delete pSessionGenerator;
                        return 1;
                }

                // Initialization done. Start generating
                context.StartGeneratingAll();
        }

        // Register session callbacks
        pSessionGenerator->RegisterSession(NULL, &SessionStart, &SessionEnd, &SessionProgress);

        // Start catching signals for quit indications
        CatchSignals(&g_bQuit);

        // init & register wave control
        XnVWaveDetector wc;
        wc.RegisterWave(NULL, OnWaveCB);
        wc.RegisterPointUpdate(NULL, OnPointUpdate);
        pSessionGenerator->AddListener(&wc);

        printf("Please perform focus gesture to start session\n");
        printf("Hit any key to exit\n");
    setup_uinput_device();
        // Main loop
        while ((!_kbhit()) && (!g_bQuit))
        {
                if (bRemoting)
                {
                        ((XnVMultiProcessFlowClient*)pSessionGenerator)->ReadState();
                }
                else
                {
                        context.WaitAndUpdateAll();
                        ((XnVSessionManager*)pSessionGenerator)->Update(&context);
                }
        }

        delete pSessionGenerator;

        context.Shutdown();
    ioctl(uinp_fd, UI_DEV_DESTROY); 
    close(uinp_fd); 

        return 0;
}