/*
-----------------------------------------------------------------------------
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.
-----------------------------------------------------------------------------
*/

/*
 XInput dll for scol
 First version : may 2010
 Author : Bastien BOURINEAU
*/

/*! @defgroup grpjoypad Scol functions definition
 *  Scol functions definition
 *  @{
 */
/** @} */

#include "joypad.h"

LPDIRECTINPUT8 DIinterf;

//!Scol machine declaration for MM macros
cbmachine	ww;

HWND HScol = NULL;

int OBJJOYPADSCOL;

int JOYPAD_DATA_CB;
int SCOL_JOYPAD_DATA_CB = 0;

/*! \mainpage Joypad Scol Plugin
 *
 * \section intro_sec Introduction
 * This plugin allow Scol Virtual Machine to use Xinput Joypad
 * 
 */


/*!
* \brief Destroys a joypad Object in scol 
* \param mmachine : The VM
* \param int : object system handle
* \param int : object scol handle
*/
int destroyJoypadObj(mmachine m, int handsys, int handscol) {
	// Read the first element of a TAB element (table of object)
	Joypad* joypad = (Joypad*) MMfetch(m, MTOP(handscol), 0);
	if (joypad == NULL) {
		// Write the first element in the stack, without pulling it
		MMset(m, 0, NIL);
		return 0;
	}

	// Safely dispose of "joypad" pointer
	SAFE_DELETE(joypad);

	// Write the first element of a TAB element
	MMstore(m, MTOP(handscol), 0, NULL);

	// Display debug message
	MMechostr(MSKDEBUG,"Joypad object destroyed.\n");
	return 0;
}


/*! @ingroup grpjoypad
* \brief _OpenJoypad : This function open a joypad and init the data callback
*
* <b>Prototype:</b> fun [Chn ] ObjJoypad
* \param Chn : channel
*
* \return ObjJoypad : the joypad object if success, NIL otherwise 
*/
int _OpenJoypad(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_OpenJoypad\n");
#endif
	// Declare local variables
	int k = 0;
  
	// Get the channel without pulling it (first element in the stack)
	int pUser = MMget(m, 0);
  int cbfun = MMget(m, 1);
  int channel = MMget(m, 2);

	// Test the channel
	if (channel == NIL || cbfun == NIL) { 
		MMechostr(MSKDEBUG, "Channel NIL\n");
    SEDROP(m, 2); MMset(m, 0, NIL);
		return 0;
	}
  
  Joypad * joypad = new Joypad();
	if (joypad == NULL) {
		MMechostr(MSKDEBUG,"joypad failed\n");
		SAFE_DELETE(joypad); 
    SEDROP(m, 2); MMset(m, 0, NIL);
    return 0;
	}
  
  // re-push channel to create object
  if ((k = MMpush(m, MMget(m, 2))))     return k; //channel

	// Allocate a space in the stack for a table of joypad object
	int joyTab = MMmalloc(m, 1, TYPETAB);
	if (joyTab == NIL) {
		MMechostr(MSKDEBUG,"MMmalloc failed\n");
		SAFE_DELETE(joypad); 
    return joyTab;
	}
  
	// Push the table of joypad objects into the stack
	MMstore(m, joyTab, 0, (int)joypad);
	MMpush(m, PTOM(joyTab));

	// Create a new scol joypad object
	k = OBJcreate(m, OBJJOYPADSCOL, (int)joypad, NULL, NULL);

  /* add reflex */
  if ((k = MMpush(m, MMget(m, 2))))     return k; /* reading callback */
  if ((k = MMpush(m, MMget(m, 2))))     return k; /* user param       */
  if ((k = OBJaddreflex(m, OBJJOYPADSCOL, SCOL_JOYPAD_DATA_CB))) return k;

  k = MMpull(m); /* save joypad object */
  m->pp+=3;

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif

	return MMpush(m, k);
}


/*! @ingroup grpjoypad
* \brief _CloseJoypad : Destroy joypad object
*
* fun [ObjJoypad] I
* \param ObjJoypad : joypad Object to destroy
* \return I : 0 if success, NIL otherwise 
*/
int _CloseJoypad(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"_CloseJoypad\n");
#endif
	
	int joypad = MTOP( MMget(m,0) ) ;
	if ( joypad == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	OBJdelTM( m, OBJJOYPADSCOL, PTOM(joypad) ) ;
	MMset(m,0,0);

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"ok\n");
#endif
	return 0;
}


/*!
* C function that prepares the VM for the execution of the callback
* \param m :
* \param h :
* \param msg :
* \param id :
* \param param :
* \param ret :
*/
int getJoypadData(mmachine m, HWND h, unsigned msg, UINT id, LONG param, int *ret) {
	int k = 0;
  
	// Cast id parameter to Joypad type
	Joypad * joypad = (Joypad*) id;

	// Use : OBJbeginreflex(mmachine, type of object, ptr object, callback type)
  if (OBJbeginreflex(m, OBJJOYPADSCOL, (int)joypad, SCOL_JOYPAD_DATA_CB)) {
		MMechostr(MSKDEBUG,"Joypad not found\n");
		return 0;
	}

	int tuple = MMmalloc(m, 2, TYPETAB);
	if(tuple==NIL)					{	MMpush(m,NIL);	return MERRMEM;	}
  MMstore(m, tuple, 0, ITOM(joypad->js.lX));
	MMstore(m, tuple, 1, ITOM(joypad->js.lY));

    MMpush(m,PTOM(tuple));	
	
  MMpush(m,ITOM(joypad->js.lZ));

	int tuple2 = MMmalloc(m, 2, TYPETAB);
	if(tuple2==NIL)					{	MMpush(m,NIL);	return MERRMEM;	}
	MMstore(m, tuple2, 0, ITOM(joypad->js.lRx));
	MMstore(m, tuple2, 1, ITOM(joypad->js.lRy));

    MMpush(m,PTOM(tuple2));	
	
  MMpush(m,ITOM(joypad->js.lRz));

	int tuple3 = MMmalloc(m, 4, TYPETAB);
	if(tuple3==NIL)					{	MMpush(m,NIL);	return MERRMEM;	}
	MMstore(m, tuple3, 0, ITOM(joypad->js.rgdwPOV[0]));
	MMstore(m, tuple3, 1, ITOM(joypad->js.rgdwPOV[1]));
  MMstore(m, tuple3, 2, ITOM(joypad->js.rgdwPOV[2]));
  MMstore(m, tuple3, 3, ITOM(joypad->js.rgdwPOV[3]));

    MMpush(m,PTOM(tuple3));
  
	int tuple4 = MMmalloc(m, 2, TYPETAB);
	if(tuple4==NIL)					{	MMpush(m,NIL);	return MERRMEM;	}
	MMstore(m, tuple4, 0, ITOM(joypad->js.rglSlider[0]));
	MMstore(m, tuple4, 1, ITOM(joypad->js.rglSlider[1]));

    MMpush(m,PTOM(tuple3));

  // get which buttons are pressed
  for( int i = 0; i < 128; i++ )
  {
      if( joypad->js.rgbButtons[i] & 0x80 )
      {
        if(MMpush(m,ITOM(1)))		return MERRMEM;
      }
      else
      {
        if(MMpush(m,ITOM(0)))		return MERRMEM;
      }
  }

	if(MMpush(m,NIL))		return MERRMEM;

	for(int i = 0; i < 128; i++)
	{
    	if(MMpush(m,2*2))		return MERRMEM;
		if(k=MBdeftab(m))		return k;
	}
  
	// Call reflex previously defined
	k = OBJcallreflex(m, 7 /*nb param after obj and u0*/);
	return k;
}



//! Nb of Scol functions or types
#define NbTplPKG	3


/*!
*	Scol function names
*/
char	*TplName[NbTplPKG] =
{
	"ObjJoypad",
	"_OpenJoypad",
  "_CloseJoypad"
};


/*!
*	Pointers to C functions that manipulate the VM for each scol function previously defined
*/
int (*TplFunc[NbTplPKG])(mmachine m)=
{
  NULL,                                 // ObjJoypad
	_OpenJoypad,											    // _OpenJoypad
  _CloseJoypad	  									    // _CloseJoypad
};


/*!
*	Nb of arguments of each scol function
*/
int TplNArg[NbTplPKG]=
{
	TYPTYPE,			  							        // ObjJoypad
	3, 														        // _OpenJoypad
  1 														        // _CloseJoypad
};


/*!
*	Prototypes of the scol functions
*/
char* TplType[NbTplPKG]=
{
  NULL,                                 // ObjJoypad
	"fun [Chn fun [ObjJoypad u0 [I I] I [I I] I [I I I I] [I I] [I r1]] u1 u0] ObjJoypad",  		// _OpenJoypad
  "fun [ObjJoypad] I" 								  // _CloseJoypad
};



// Everything inside _cond and _endcond is ignored by doxygen
//! \cond

/*!
* \brief Load the packages in Scol virtual machine
* \param mmachine : the scol machine
*/
int LoadJoypad(mmachine m)
{
	int			k;

	MMechostr(MSKDEBUG,"\n" );
  MMechostr(MSKDEBUG," > Loading Joypad Support\n");

  // Declare a new type of object ("ObjJoypad")
	OBJJOYPADSCOL = OBJregister(1 /*nb of callback*/, 1, destroyJoypadObj, "OBJJOYPADSCOL");

	// ----- Define callbacks
	// Get a new user event
	JOYPAD_DATA_CB = OBJgetUserEvent();
	// Associate this event with a callback	
	OBJdefEvent(JOYPAD_DATA_CB, (int (__cdecl *)(struct Mmachine *, int, unsigned int, int, int, int *))getJoypadData);

	k = PKhardpak(m, "JoypadEngine", NbTplPKG, TplName, TplFunc, TplNArg, TplType);
	MMechostr(MSKDEBUG," > Successfully Loaded\n\n");
	return k;
}

//! \endcond


/*! 
* \brief Starting point of the DLL
* Function to add in Scol usm.ini for loading dll
*/
extern "C" __declspec (dllexport) int ScolLoadPlugin(mmachine m, cbmachine w)
{
	int		k = 0;
  SCOLinitplugin(w);
  
	// Get Scol window handle (for message callback)
	HScol = (HWND)SCgetExtra("hscol");

  DirectInput8Create((HINSTANCE)HScol,DIRECTINPUT_VERSION, IID_IDirectInput8, ( VOID** )&DIinterf, NULL );
	LoadJoypad(m);
	return k;
}


/*! 
* \brief Ending point of the DLL
* Function to add in Scol usm.ini for free dll
*/
extern "C" __declspec (dllexport) int ScolUnloadPlugin()
{
  SAFE_RELEASE(DIinterf);
	return 0;
}
