/*
Development tools and related technology provided under license from 3Dconnexion.
(c) 1992 - 2012 3Dconnexion. All rights reserved
*/

 /*
Implementation by Torsten Sadowski 2015
with special thanks to marcxs for making the first steps
 */

#include <unistd.h>
#include "GUIApplicationNativeEventAware.h"
#include "SpaceballEvent.h"
#include <QWidget>



UInt16 Gui::GUIApplicationNativeEventAware::tdxClientID = 0;
uint32_t Gui::GUIApplicationNativeEventAware::lastButtons = 0;

  /* ----------------------------------------------------------------------------
     Handler for driver events. This function is able to handle the events in
     different ways: (1) re-package the events as Carbon events, (2) compute
     them directly, (3) write the event info in a shared memory location for
     usage by reader threads.
  */
  void 
  Gui::GUIApplicationNativeEventAware::tdx_drv_handler(io_connect_t connection, 
		  natural_t messageType, 
		  void *messageArgument)
  {
    //printf("tdx_drv_handler\n");
    //printf("connection: %X\n", connection);
    //printf("messageType %c%c%c%c\n", messageType/0x1000000, messageType/0x10000, messageType/0x100, messageType);
    ConnexionDeviceStatePtr msg = (ConnexionDeviceStatePtr)messageArgument;
    
    switch(messageType)
      {
      case kConnexionMsgDeviceState:
	/* Device state messages are broadcast to all clients.  It is up to
	 * the client to figure out if the message is meant for them. This
	 * is done by comparing the "client" id sent in the message to our
	 * assigned id when the connection to the driver was established.
	 */
	//printf("msg->client: %d, tdxClientID: %d\n", msg->client, tdxClientID);	 
	if (msg->client == tdxClientID)
	  {
	    Gui::GUIApplicationNativeEventAware* qapp = static_cast<Gui::GUIApplicationNativeEventAware *>(Gui::GUIApplicationNativeEventAware::instance());
	    if (!qapp) return;
	    switch (msg->command)
	      {
	      case kConnexionCmdHandleAxis:
		{
		  //printf("TX: %d\n", msg->axis[0]);
		  //printf("TY: %d\n", msg->axis[1]);
		  //printf("TZ: %d\n", msg->axis[2]);
		  //printf("RX: %d\n", msg->axis[3]);
		  //printf("RY: %d\n", msg->axis[4]);
		  //printf("RZ: %d\n", msg->axis[5]);
		  qapp->motionDataArray[0] = msg->axis[0];                        
		  qapp->motionDataArray[1] = msg->axis[1];                        
		  qapp->motionDataArray[2] = msg->axis[2];                        
		  qapp->motionDataArray[3] = msg->axis[3];                        
		  qapp->motionDataArray[4] = msg->axis[4];                        
		  qapp->motionDataArray[5] = msg->axis[5];
		  qapp->Move3d();
		  break;
		}
                        
	      case kConnexionCmdHandleButtons:
		{
		  //printf("value: %d\n", msg->value);
		  //printf("buttons: %u\n", msg->buttons);
		  uint32_t changedButtons = msg->buttons ^ lastButtons;
		  uint32_t pressedButtons = msg->buttons & changedButtons;
		  uint32_t releasedButtons = lastButtons & changedButtons;
		  for (uint8_t bt = 0; bt < 32; bt++)
		    {
		      if (pressedButtons & 1)
			qapp->Button3d(true, bt);
		      pressedButtons = pressedButtons>>1;
		    }
		  for (uint8_t bt = 0; bt < 32; bt++)
		    {
		      if (releasedButtons & 1)
			qapp->Button3d(false, bt);
		      releasedButtons = releasedButtons>>1;
		    }
		  lastButtons = msg->buttons;
		  break;
		}
                        
	      default:
		break;

	      } /* switch */
	  }

	break;

      default:
	/* other messageTypes can happen and should be ignored */
	break;
      }
  }

/*!
	Called with the processed motion data when a 3D mouse event is received

	The default implementation emits a Move3d signal with the motion data
*/
void Gui::GUIApplicationNativeEventAware::Move3d()
{
  QWidget *currentWidget = this->focusWidget();
  if (!currentWidget)
    return;
    //currentWidget = mainWindow;

  if (!setOSIndependentMotionData()) return;
  importSettings();
  
  Spaceball::MotionEvent *motionEvent = new Spaceball::MotionEvent();
  
  motionEvent->setTranslations(motionDataArray[0], motionDataArray[1], motionDataArray[2]);
  motionEvent->setRotations(motionDataArray[3], motionDataArray[4], motionDataArray[5]);
  
  this->postEvent(currentWidget, motionEvent);
}

/*!
	Called when a 3D mouse key is pressed or released

	The default implementation emits a On3dmouseKeyDown signal with the key code.
*/
void Gui::GUIApplicationNativeEventAware::Button3d(bool buttonDown, int buttonNumber)
{
    QWidget *currentWidget = this->focusWidget();
    if (!currentWidget)
      return;
    // currentWidget = mainWindow;

    Spaceball::ButtonEvent *buttonEvent = new Spaceball::ButtonEvent();
    buttonEvent->setButtonNumber(buttonNumber);
    if (buttonDown)
      {
	//printf("Button %d pressed\n", buttonNumber);
	buttonEvent->setButtonStatus(Spaceball::BUTTON_PRESSED);
      }
    else
      {
	//printf("Button %d released\n", buttonNumber);
	buttonEvent->setButtonStatus(Spaceball::BUTTON_RELEASED);
      }
    this->postEvent(currentWidget, buttonEvent);
}

