/*
-----------------------------------------------------------------------------
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.
-----------------------------------------------------------------------------
*/

/*! \mainpage USB-UIRT main Page
 *
 * \section intro_sec Introduction
 * This project is a scol plugin to manage USB-UIRT device.
 * 
 */

#include <scol.h>
#include "usbuirt.h"

HWND HScol = NULL;

//!Scol machine declaration for MM macros
cbmachine ww;

//! Bloc Object in Scol
int OBJ_USBUIRT_SCOL;

//! New data event callback number
int SCOL_URSBUIRT_RECEIVE_DATA_CB=0;

//! New data event number
int USBUIRT_RECEIVE_DATA_CB;


/*! @defgroup group1 Scol functions definition
 *  User Scol functions definition
 *  @{
 */
/** @} */

/*! @ingroup group1
* \brief Scol Bloc Object 
* It allows simple operations like getting and setting its attributes.
*/
//! BlocObj is set here as a type (an int) for doxygen documentation
typedef int ObjUsbUIRT;



/*****************************************************************************/
/* IRReceiveCallback: Receive IR Callback Procedure						     */
/*																			 */
/* This procedure is called by the UUIRT .dll whenever an IRcode is received */
/* The IRcode is passed to the callback in UIR format.						 */
/*																			 */
/*****************************************************************************/
void WINAPI IRReceiveCallback (char *IREventStr, void *userData)
{
  UsbUIRT* uirtobj = (UsbUIRT*) userData;

  MMechostr(MSKDEBUG, ">>>>>>>>IR Receive : %s\n", IREventStr);

	// send the change data callback message to scol, use PostMessage instead if you need to poll the message
	SendMessage( HScol, USBUIRT_RECEIVE_DATA_CB,(int)uirtobj,(LPARAM)IREventStr);
}


/*!
* \brief Scol object destroy callback
*
* \param mmachine : scol machine structure
* \param int : scol object system handle
* \param int : scol object stack handle
*
* \return int : 0
*/
int destroyUsbUIRTObj(mmachine m, int handsys, int objTab) {
	// Read the first element of a TAB element (table of objects)
	UsbUIRT* uirtobj = (UsbUIRT*) MMfetch(m, MTOP(objTab), 0);

	// Safely dispose of "Bloc" pointer
	SAFE_DELETE(uirtobj);

	// Write the first element of a TAB element
	MMstore(m, MTOP(objTab), 0, NULL);

	// Display debug message
	MMechostr(MSKDEBUG,"ObjUsbUIRT destroyed.\n");
	return 0;
}


/*! @ingroup group1
* \brief _CRusbUIRT : Open, initialize ObjUsbUIRT
*
* <b>Prototype:</b> fun [Chn] ObjUsbUIRT
*
* \param Chn : current channel
*
* \return ObjUsbUIRT : ObjUsbUIRT object if success, NIL otherwise 
*/
int _CRusbUIRT(mmachine m) {
	#ifdef	_SCOL_DEBUG_
		MMechostr(MSKDEBUG,"_CRusbUIRT\n");
	#endif

	// Declare local variables
	int k = 0;

	// Get the channel without pulling it (first element in the stack)
	int channel = MMget(m, 0);

	// Test the channel
	if (channel == NIL) { 
		MMechostr(MSKDEBUG, "Channel NIL\n");
		MMpull(m);						  // Pull the channel													
		MMpush(m, NIL);					// Push NIL on the stack
		return 0;
	}

	// Create UsbUIRT instance
	UsbUIRT * uirtobj = new UsbUIRT();
  
  if (uirtobj == NULL || !uirtobj->isInitialized()) {
		MMechostr(MSKDEBUG, "_CRusbUIRT ...initialization failed\n");
		SAFE_DELETE(uirtobj) ;
		MMpull(m);								// Pull the channel
		MMpush(m, NIL);					  // Push NIL on the stack
		return 0;
	}
	MMechostr(MSKDEBUG,"_CRusbUIRT ...initialization successful\n");
  
	// Register a callback function for IR receive...
	uirtobj->SetReceiveCallback(&IRReceiveCallback);
  
	// Retrieve information about the USB-UIRT device...
  MMechostr(MSKDEBUG, "USB-UIRT Info: FirmwareVer=%d.%d ProtVer=%d.%d FirmwareDate=%d/%d/%d\n",
		uirtobj->UsbUirtInfo.fwVersion>>8,
		uirtobj->UsbUirtInfo.fwVersion&0xff,
		uirtobj->UsbUirtInfo.protVersion>>8,
		uirtobj->UsbUirtInfo.protVersion&0xff,
		uirtobj->UsbUirtInfo.fwDateMonth,
		uirtobj->UsbUirtInfo.fwDateDay,
		uirtobj->UsbUirtInfo.fwDateYear+2000);
  
  MMechostr(MSKDEBUG, "USB-UIRT Config: %08X (LED_RX=%d, LED_TX=%d, LEGACY_RX=%d)\n",
		uirtobj->uConfig,
		uirtobj->uConfig & UUIRTDRV_CFG_LEDRX ? 1 : 0,
		uirtobj->uConfig & UUIRTDRV_CFG_LEDTX ? 1 : 0,
		uirtobj->uConfig & UUIRTDRV_CFG_LEGACYRX ? 1 : 0);

	// Allocate a space in the stack for a table of bloc objects
	int objTab = MMmalloc(m, 1, TYPETAB);
	if (objTab == NIL) {
		MMechostr(MSKDEBUG,"_CRusbUIRT ...MMmalloc failed\n");
		SAFE_DELETE(uirtobj); 
		MMpull(m);								// Pull the channel
		return MMpush(m, NIL);		// Push NIL on the stack
	}
	MMechostr(MSKDEBUG,"_CRusbUIRT ...MMmalloc successful\n");
  
	// Push the table of bloc objects into the stack
	MMstore(m, objTab, 0, (int)uirtobj);
	MMpush(m, PTOM(objTab));

	// Create a new scol bloc object
	k = OBJcreate(m, OBJ_USBUIRT_SCOL, (int)uirtobj, NULL, NULL);
	MMechostr(MSKDEBUG,"_CRusbUIRT ...object creation successful\n");

	#ifdef	_SCOL_DEBUG_
		MMechostr(MSKDEBUG,"ok\n");
	#endif

	// Return bloc object
	return k;
}


/*! @ingroup group1
* \brief _DSusbUIRT : Destroy ObjUsbUIRT object
*
* <b>Prototype:</b> fun [ObjUsbUIRT] I
*
* \param ObjUsbUIRT : object to destroy
*
* \return I : 0 if success, NIL otherwise 
*/
int _DSusbUIRT(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"_DSusbUIRT\n");
#endif
	
	int objTab = MTOP( MMget(m,0) );
	if ( objTab == NIL ) { MMset(m,0,NIL); return 0; }
	
	OBJdelTM( m, OBJ_USBUIRT_SCOL, PTOM(objTab) );
	MMset(m,0,0);

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"ok\n");
#endif
	return 0;
}


/*! @ingroup group1
* \brief _SendUsbUIRTData : Send data to USB-UIRT device
*
* <b>Prototype:</b> fun [ObjUsbUIRT S I I I] I
*
* \param ObjUsbUIRT : object to use
* \param S : IR code to send
* \param I : IR code format 0 for PRONTO (default), 1 for UUIRT
* \param I : number of IR code repeat
* \param I : wait time between repeats
*
* \return I : 1 if success, 0 on fail, NIL otherwise 
*/
int _SendUsbUIRTData(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"_SendUsbUIRTData\n");
#endif

  int waitTime = MTOI(MMpull(m));
  int repeat = MTOI(MMpull(m));
  int format = MTOI(MMpull(m));
  int data = MTOP(MMpull(m));
	int objTab = MTOP(MMget(m,0));
  
  if (!repeat)
    repeat = 1;

	if (objTab == NIL || data == NIL) { MMset(m,0,NIL); return 0; }
  
	UsbUIRT* uirtobj = (UsbUIRT*) MMfetch(m, objTab, 0);
  if (uirtobj == NULL) { MMset(m,0,NIL); return 0; }
  
  char * sIrCode = MMstartstr(m, data);
  int dformat = UUIRTDRV_IRFMT_PRONTO;
  
  MMechostr(MSKDEBUG, ">>>>>>>> Send IR CODE : %s\n", sIrCode); 

  if (format == 1)
    dformat = UUIRTDRV_IRFMT_UUIRT;
	
  if(uirtobj->SendIrData(sIrCode, dformat, repeat, waitTime))
    MMset(m,0,1);
  else
    MMset(m,0,0);

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"ok\n");
#endif
	return 0;
}


/*! @ingroup group1
* \brief _CBusbUIRTReceiveData : This function sets the callback to be executed when UsbUIRT device receive data
*
* <b>Prototype:</b> fun [BlocObj fun [ObjUsbUIRT u0 S] u1 u0] ObjUsbUIRT
*
* \param ObjUsbUIRT : Object to apply the callback
* \param fun [ObjUsbUIRT u0 S] u1 : The callback to call when the event occurs.
* - S : data received on the object
* \param u0 : User parameter
*
* \return ObjUsbUIRT : The same object
*/
int _CBusbUIRTReceiveData(mmachine m) {
	// Add a reflex
	MMechostr(MSKDEBUG, "_CBusbUIRTReceiveData ...adding reflex\n");
	return OBJaddreflex(m, OBJ_USBUIRT_SCOL, SCOL_URSBUIRT_RECEIVE_DATA_CB);
}


/*!
* \brief sample function for calling scol reflex defined for USBUIRT_RECEIVE_DATA_CB
*
* \param mmachine : scol machine structure
* \param HWND : target window handle
* \param unsigned msg : window message
* \param UINT : callback param
* \param LONG : callback param
* \param int : return value
*/
int getUsbUIRTReceiveData(mmachine m, HWND h, unsigned msg, UINT id, LONG param, int *ret) {
	int k = 0;
  
	// Cast id parameter to BlocObj type
  UsbUIRT* uirtobj = (UsbUIRT*) id;
  
  char * receiveData = (char*) param;

	// Use : OBJbeginreflex(mmachine, type of object, ptr object, callback type)
	if (OBJbeginreflex(m, OBJ_USBUIRT_SCOL, (int)uirtobj, SCOL_URSBUIRT_RECEIVE_DATA_CB)) {
		MMechostr(MSKDEBUG,"Object not found\n");
		return 0;
	}
  
  // Retrieve current bloc name, and set it as 4th parameter of the callback
  Mpushstrbloc(m, receiveData);
  
	// Call reflex previously defined
	k = OBJcallreflex(m, 1 /*nb param after obj and u0*/);
	return k;
}



//! Nb of Scol functions or types
#define NbTplPKG	5


/*!
*	Scol function names
*/
char	*TplName[NbTplPKG] =
{	
	"ObjUsbUIRT",
	"_CRusbUIRT",
	"_DSusbUIRT",
	"_CBusbUIRTReceiveData",
  "_SendUsbUIRTData"
};


/*!
*	Pointers to C functions that manipulate the VM for each scol function previously defined
*/
int (*TplFunc[NbTplPKG])(mmachine m)=
{
  NULL, 
  _CRusbUIRT,
	_DSusbUIRT,
	_CBusbUIRTReceiveData,
  _SendUsbUIRTData
};


/*!
*	Nb of arguments of each scol function
*/
int TplNArg[NbTplPKG]=
{
  TYPTYPE,	
	1,
	1,
	3,
  5
};


/*!
*	Prototypes of the scol functions
*/
char* TplType[NbTplPKG]=
{
	NULL,
	"fun [Chn] ObjUsbUIRT",         						                  // _CRusbUIRT
	"fun [ObjUsbUIRT] I",					                                // _DSusbUIRT
	"fun [ObjUsbUIRT fun [ObjUsbUIRT u0 S] u1 u0] ObjUsbUIRT",    // _CBusbUIRTReceiveData
  "fun [ObjUsbUIRT S I I I] I",		                              // _SendUsbUIRTData
};


/*!
* \brief Load the Bloc
*
* \param mmachine : scol machine structure
*
* \return int : 0 if succes, error code otherwise
*/
int LoadPlugin(mmachine m) {
	int k;

	// Declare a new type of object ("OBJ_USBUIRT_SCOL")
	OBJ_USBUIRT_SCOL = OBJregister(1 /*nb of callback*/, /* deleted from parent */ 1, destroyUsbUIRTObj, "OBJ_USBUIRT_SCOL");

	// ----- Define callbacks
	// Get a new user event
	USBUIRT_RECEIVE_DATA_CB = OBJgetUserEvent();

	// Associate this event with a callback	
	OBJdefEvent(USBUIRT_RECEIVE_DATA_CB, (int (__cdecl *)(struct Mmachine *, int, unsigned int, int, int, int *))getUsbUIRTReceiveData);
  
	// Load package
	k = PKhardpak(m, "UsbUIRTEngine", NbTplPKG, TplName, TplFunc, TplNArg, TplType);
	return k;
}


/*! @defgroup usmconfig Scol plugin configuration
 *  Scol plugin configuration
 *
 *  usm.ini entry :
 *  plugin plugins/UsbUIRT.dll SCOLloadUsbUIRT SCOLfreeUsbUIRT
 *
 *  @{
 */
/** @} */


// Everything inside _cond and _endcond is ignored by doxygen
//! \cond
/*! 
* \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");
  LoadPlugin(m);
	return 0; 
}

/*! 
* \brief Ending point of the DLL
*/
extern "C" __declspec (dllexport) int ScolUnloadPlugin()
{
  return 0;
}

//! \endcond