
/*
-----------------------------------------------------------------------------
This source file is part of OpenSpace3D
For the latest info, see http://www.openspace3d.com

Copyright (c) 2010 I-maginer

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt

You may alternatively use this source under the terms of a specific version of
the OpenSpace3D Unrestricted License provided you have obtained such a license from
I-maginer.
-----------------------------------------------------------------------------
*/

/*
 TUsb respiration belt library
 First version : august 2011
 Author : Aymeric Suteau
*/

/*! @defgroup grptusb Scol functions definition
 *  Scol functions definition
 *  @{
 */
/** @} */

/**
* @file Plugin.cpp
*
* @brief The file contains the sources for TUsb respiration belt Scol plugin
* and the definition of each function of the DLL
**/


// Include Header File
#include "Plugin.h"

//!Scol machine declaration for MM macros
cbmachine ww;
HWND HScol = NULL;	

//!Scol object declaration 
int OBJTUSBSCOL;

//!Scol CallBacks declaration 
//===== CB Connected ===
int SCOL_TUSB_CONNECTED_CB      = 0;
int TUSB_CONNECTED_CB;

//===== CB Disconnected ===
int SCOL_TUSB_DISCONNECTED_CB   = 1;
int TUSB_DISCONNECTED_CB;

//===== CB New data ===
int SCOL_TUSB_NEW_DATA_CB       = 2;
int TUSB_NEW_DATA_CB;

//===== CB Battery level ===
int SCOL_TUSB_BATTERY_LEVEL_CB  = 3;
int TUSB_BATTERY_LEVEL_CB;


/*! \mainpage TUsb respiration Scol Plugin
 *
 * \section intro_sec Introduction
 * This plugin allows Scol Virtual Machine to manage TUsb respiration belt.
 * The TUsb respiration belt is a remote sensor which is able to retrieve the extension of the chest (in percent).
 * For more information about the device, you can visit Product Page on TEA website : 
 * http://www.teaergo.com/index.php?option=com_content&view=article&id=151&Itemid=238&lang=fr
 * 
*/


//!Scol CallBack for ObjTUsb destruction
int destroyTUsbObj(mmachine m, int handsys, int respirationBeltTab) 
{
	// Read the first element of a TAB element (table of objects)
	RespirationBelt* RespirationBeltObj = (RespirationBelt*) MMfetch(m, MTOP(respirationBeltTab), 0);
  if (RespirationBeltObj == NULL)
  {
		// Write the first element in the stack, without pulling it
		MMset(m, 0, NIL); 
		return 0; 
	}

	// Safely dispose of "RespirationBeltObj" pointer
	SAFE_DELETE(RespirationBeltObj);
  MMechostr(MSKDEBUG, "destroyTUsbObj ...SAFE_DELETE ok\n");

	// Write the first element of a TAB element
	MMstore(m, MTOP(respirationBeltTab), 0, 0);

	// Display debug message
	MMechostr(0, "RespirationBeltObj destroyed.\n");
	return 0;
}


/*! @ingroup grptusb
* \brief _CRTUsbDevice : This function creates a new TUsb respiration belt instance
*
* <b>Prototype:</b> fun [Chn] ObjTUsb
* \param Chn : the current channel
*
* \return ObjTUsb : The RespirationBelt object or NIL if creation fails
*/
int _CRTUsbDevice(mmachine m)
{
	#ifdef	_SCOL_DEBUG_
		MMechostr(0,"_CRTUsbDevice\n");
	#endif

	// Declare local variables
	int k = 0;

  // Get and test Channel
	int channel = MMget(m, 0);
	if (channel == NIL) 
  { 
		MMechostr(MSKDEBUG, "Channel NIL\n");	  // Display Warning in Scol Console
		MMpull(m);
		MMpush(m, NIL);
		return 0;
	}

	// Create RespirationBelt instance
	RespirationBelt* respirationBelt = new RespirationBelt();

	// Allocate a block in the stack for a table of RespirationBelt objects
	int respirationBeltTab = MMmalloc(m, 1, TYPETAB);
	if (respirationBeltTab == NIL)
  {
		MMechostr(0, "_CRTUsbDevice ...MMmalloc failed\n");
		SAFE_DELETE(respirationBelt); 
		MMpull(m);
		return MMpush(m, NIL);
	}
	MMstore(m, respirationBeltTab, 0, (int)respirationBelt);
	MMpush(m, PTOM(respirationBeltTab));

	// Create a new TUsb respiration RespirationBelt object
	k = OBJcreate(m, OBJTUSBSCOL, (int)respirationBelt, -1, -1);

	#ifdef	_SCOL_DEBUG_
		MMechostr(0,"ok\n");
	#endif

	// Return RespirationBelt object
	return k;
}


/*! @ingroup grptusb
* \brief _DSTUsbDevice : This function deletes the TUsb respiration system instance
*
* <b>Prototype:</b> fun [ObjTUsb] I
* \param ObjTUsb : the current ObjTUsb
*
* \return I : 0 if success, NIL otherwise
*/
int _DSTUsbDevice(mmachine m)
{
	#ifdef	_SCOL_DEBUG_
		MMechostr(0,"_DSTUsbDevice\n");
  #endif

	// Get the table of RespirationBelt objects into the stack (without pulling it)
	int respirationBeltTab = MMget(m, 0);
	if (respirationBeltTab == NIL)
  {
		MMechostr(0, "_DSTUsbDevice ...ObjTUsb NIL\n");
		MMset(m, 0, NIL);
		return 0;
	}

	// Destroy RespirationBelt object according to its type and magma object, and reinitialize the stack
  OBJdelTM(m, OBJTUSBSCOL, respirationBeltTab);
	MMset(m, 0, ITOM(0));

	#ifdef	_SCOL_DEBUG_
		MMechostr(0,"ok\n");
	#endif

	return 0;
}


/*! @ingroup grptusb
* \brief _CBTUsbConnected : This function sets the Callback for a connected event received from TUsb respiration belt
*
* <b>Prototype:</b> fun [ObjTUsb fun [ObjTUsb u0] u1 u0] ObjTUsb
* \param ObjTUsb : the current ObjTUsb
* \param fun [ObjTUsb u0] : SCOL CallBack function to call
* - ObjTUsb : respiration belt object
* - u0 : user parameter
* \param u1 : user parameter
* \param u0 : user parameter
*
* \return I : ObjTUsb if success
*/
int _CBTUsbConnected(mmachine m)
{
  return (OBJaddreflex(m, OBJTUSBSCOL, SCOL_TUSB_CONNECTED_CB));
}

int getTUsbConnectedCb(mmachine m, HWND h, unsigned msg, UINT id, LONG param, int *ret)
{
	int k = 0;
	RespirationBelt* RespirationBeltObj = (RespirationBelt*) id;

	// OBJbeginreflex(mmachine, objecttype, objectptr, cbtype)
	if (OBJbeginreflex(m, OBJTUSBSCOL, (int)RespirationBeltObj, SCOL_TUSB_CONNECTED_CB))
  {
    MMechostr(0, "getTUsbConnectedCb -> OBJbeginreflex failed.\n");
		return 0;
  }
	return (OBJcallreflex(m, 0));
}


/*! @ingroup grptusb
* \brief _CBTUsbDisconnected : This function sets the Callback for a Disconnected event received from TUsb respiration belt
*
* <b>Prototype:</b> fun [ObjTUsb fun [ObjTUsb u0] u1 u0] ObjTUsb
* \param ObjTUsb : the current ObjTUsb
* \param fun [ObjTUsb u0] : SCOL CallBack function to call
* - ObjTUsb : respiration belt object
* - u0 : user parameter
* \param u1 : user parameter
* \param u0 : user parameter
*
* \return I : ObjTUsb if success
*/
int _CBTUsbDisconnected(mmachine m)
{
  return (OBJaddreflex(m, OBJTUSBSCOL, SCOL_TUSB_DISCONNECTED_CB));
}

int getTUsbDisconnectedCb(mmachine m, HWND h, unsigned msg, UINT id, LONG param, int *ret)
{
	int k = 0;
	RespirationBelt* RespirationBeltObj = (RespirationBelt*) id;

	// OBJbeginreflex(mmachine, objecttype, objectptr, cbtype)
	if (OBJbeginreflex(m, OBJTUSBSCOL, (int)RespirationBeltObj, SCOL_TUSB_DISCONNECTED_CB))
  {
    MMechostr(0, "getTUsbDisconnectedCb -> OBJbeginreflex failed.\n");
		return 0;
  }
	return (OBJcallreflex(m, 0));
}


/*! @ingroup grptusb
* \brief _CBTUsbNewData : This function sets the Callback for new data available from TUsb respiration belt
*
* <b>Prototype:</b> fun [ObjTUsb fun [ObjTUsb u0 [F r1]] u1 u0] ObjTUsb
* \param ObjTUsb : the current ObjTUsb
* \param  fun [ObjTUsb u0 [F r1]] : SCOL CallBack function to call
* - ObjTUsb : respiration belt object
* - u0 : user parameter
* - [F r1] : list of chest extension values
* \param u1 : user parameter
* \param u0 : user parameter
*
* \return I : ObjTUsb if success
*/
int _CBTUsbNewData(mmachine m)
{
  return (OBJaddreflex(m, OBJTUSBSCOL, SCOL_TUSB_NEW_DATA_CB));
}

int getTUsbNewDataCb(mmachine m, HWND h, unsigned msg, UINT id, LONG param, int *ret)
{
	int k = 0, nb = 0;
  BeltData* beltData = (BeltData*) param;
	RespirationBelt* RespirationBeltObj = (RespirationBelt*) id;

	// OBJbeginreflex(mmachine, objecttype, objectptr, cbtype)
	if (OBJbeginreflex(m, OBJTUSBSCOL, (int)RespirationBeltObj, SCOL_TUSB_NEW_DATA_CB))
  {
    SAFE_DELETE(beltData);
    MMechostr(0, "getTUsbNewDataCb -> OBJbeginreflex failed.\n");
		return 0;
  }

  // Browse new data
  std::list<float> lData = beltData->GetBeltData();
  std::list<float>::iterator itData = lData.begin();
  while (itData != lData.end())
  {
    if (MMpush(m, FTOM(*itData)))
    {
      SAFE_DELETE(beltData);
      return MERRMEM;
    }
    itData++;
    nb++;
  }

  if (MMpush(m, NIL))
  {
    SAFE_DELETE(beltData);
    return MERRMEM;
  }

  for (int i = 0; i < nb; i++)
	{
    if (MMpush(m, 2*2))
    {
      SAFE_DELETE(beltData);
      return MERRMEM;
    }
		if (k = MBdeftab(m))
    {
      SAFE_DELETE(beltData);
      return k;
    }
	}

  // Call reflex previously defined
	k = OBJcallreflex(m, 1);
  SAFE_DELETE(beltData);
  return k;
}


/*! @ingroup grptusb
* \brief _CBTUsbBatteryLevel : This function sets the Callback for a Battery level event received from TUsb respiration belt
*
* <b>Prototype:</b> fun [ObjTUsb fun [ObjTUsb u0 F] u1 u0] ObjTUsb
* \param ObjTUsb : the current ObjTUsb
* \param fun [ObjTUsb u0 F] : SCOL CallBack function to call
* - ObjTUsb : respiration belt object
* - u0 : user parameter
* - F : battery level
* \param u1 : user parameter
* \param u0 : user parameter
*
* \return I : ObjTUsb if success
*/
int _CBTUsbBatteryLevel(mmachine m)
{
  return (OBJaddreflex(m, OBJTUSBSCOL, SCOL_TUSB_BATTERY_LEVEL_CB));
}

int getTUsbBatteryLevelCb(mmachine m, HWND h, unsigned msg, UINT id, LONG param, int *ret)
{
	int k = 0;
	RespirationBelt* RespirationBeltObj = (RespirationBelt*) id;

	// OBJbeginreflex(mmachine, objecttype, objectptr, cbtype)
	if (OBJbeginreflex(m, OBJTUSBSCOL, (int)RespirationBeltObj, SCOL_TUSB_BATTERY_LEVEL_CB))
  {
    MMechostr(0, "getTUsbBatteryLevelCb -> OBJbeginreflex failed.\n");
		return 0;
  }

  // Push battery level into the stack
  MMpush(m, FTOM(param));
	return (OBJcallreflex(m, 1));
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////																											                                                         ////
///										                  DECLARATION OF FUNCTIONS (FOR SCOL)											                    ///
////																											                                                         ////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


//! Nb of Scol functions or types
#define NbTplPKG 7


/*!
*	Scol function names
*/
char *TplName[NbTplPKG] =
{
	"ObjTUsb",
  "_CRTUsbDevice",
	"_DSTUsbDevice",
  "_CBTUsbConnected",
  "_CBTUsbDisconnected",
  "_CBTUsbNewData",
  "_CBTUsbBatteryLevel"
};


/*!
*	Pointers to C functions that manipulate the VM for each scol function previously defined
*/
int (*TplFunc[NbTplPKG])(mmachine m)=
{
	NULL,										// ObjTUsb
  _CRTUsbDevice,				  // _CRTUsbDevice
	_DSTUsbDevice,				  // _DSTUsbDevice
  _CBTUsbConnected,       // _CBTUsbConnected
  _CBTUsbDisconnected,    // _CBTUsbDisconnected
  _CBTUsbNewData,         // _CBTUsbNewData
  _CBTUsbBatteryLevel     // _CBTUsbBatteryLevel
};


/*!
*	Nb of arguments of each scol function
*/
int TplNArg[NbTplPKG]=
{
	TYPTYPE,    // ObjTUsb
  1,					// _CRTUsbDevice
	1,					// _DSTUsbDevice
  3,          // _CBTUsbConnected
  3,          // _CBTUsbDisconnected
  3,          // _CBTUsbNewData
  3           // _CBTUsbBatteryLevel
};


/*!
*	Prototypes of the scol functions
*/
char* TplType[NbTplPKG]=
{
	NULL,																				              // ObjTUsb
  "fun [Chn] ObjTUsb",										                  // _CRTUsbDevice
	"fun [ObjTUsb] I",											                  // _DSTUsbDevice
  "fun [ObjTUsb fun [ObjTUsb u0] u1 u0] ObjTUsb",   	      // _CBTUsbConnected
  "fun [ObjTUsb fun [ObjTUsb u0] u1 u0] ObjTUsb",   	      // _CBTUsbDisconnected
  "fun [ObjTUsb fun [ObjTUsb u0 [F r1]] u1 u0] ObjTUsb",    // _CBTUsbNewData
  "fun [ObjTUsb fun [ObjTUsb u0 F] u1 u0] ObjTUsb"          // _CBTUsbBatteryLevel
};


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////																											                                                         ////
///												                        DLL CALL FUNCTIONS													                      ///
////																											                                                         ////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Everything inside _cond and _endcond is ignored by doxygen
//! \cond

/*!
* \brief Load the packages in Scol virtual machine
* \param mmachine : the scol machine
*/
int LoadTUsb(mmachine m)
{
  // Return variable for PKhardpak function
	int k;

	// Declare a new type of object ("OBJTUSBSCOL")
	OBJTUSBSCOL = OBJregister(4, 1, destroyTUsbObj, "OBJTUSBSCOL");

  // Get new user events and associate these events with a callback
  TUSB_CONNECTED_CB = OBJgetUserEvent();
  OBJdefEvent(TUSB_CONNECTED_CB, (int (__cdecl *)(struct Mmachine *, int, unsigned int, int, int, int *))getTUsbConnectedCb);

  TUSB_DISCONNECTED_CB = OBJgetUserEvent();
  OBJdefEvent(TUSB_DISCONNECTED_CB, (int (__cdecl *)(struct Mmachine *, int, unsigned int, int, int, int *))getTUsbDisconnectedCb);

  TUSB_NEW_DATA_CB = OBJgetUserEvent();
  OBJdefEvent(TUSB_NEW_DATA_CB, (int (__cdecl *)(struct Mmachine *, int, unsigned int, int, int, int *))getTUsbNewDataCb);

  TUSB_BATTERY_LEVEL_CB = OBJgetUserEvent();
  OBJdefEvent(TUSB_BATTERY_LEVEL_CB, (int (__cdecl *)(struct Mmachine *, int, unsigned int, int, int, int *))getTUsbBatteryLevelCb);

	// Load package
	k = PKhardpak(m, "FusionEngine", NbTplPKG, TplName, TplFunc, TplNArg, TplType);

  // Initialize COM library
  if (FAILED(::CoInitialize(NULL)))	
    return 0;

	return k;
}
//! \endcond


/*! 
* \brief Starting point of the DLL
*/
extern "C" __declspec (dllexport) int ScolLoadPlugin(mmachine m, cbmachine w) 
{
	SCOLinitplugin(w);

	// Get Scol window handle (for message callback)
	HScol = (HWND)SCgetExtra("hscol");	
	LoadTUsb(m);
	return 0;
}


/*! 
* \brief Ending point of the DLL
*/
extern "C" __declspec (dllexport) int ScolUnloadPlugin() 
{
  // Uninitialize COM library
  ::CoUninitialize();

	return 0;
}
