/*
-----------------------------------------------------------------------------
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 Bloc object main Page
 *
 * \section intro_sec Introduction
 * This project is a template for developping Scol applications via C langage.
 * It consist of a simple Scol object : BloObj
 * 
 */

#include <scol.h>
#include "Bloc.h"

HWND HScol = NULL;

//!Scol machine declaration for MM macros
cbmachine ww;

//! Bloc Object in Scol
int OBJBLOCSCOL;

//! New data event callback number
int SCOL_BLOC_NEW_DATA_CB=0;

//! New data event number
int BLOC_NEW_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 BlocObj;

/*! @ingroup group1
 * \brief _HelloWorld : Log a "Hello World" message in the console
 *
 * <b>Prototype:</b> fun [] I
 *
 * \return I : 0 in all cases.
 **/
int _HelloWorld(mmachine m)
{
    // Show the message
    MMechostr(MSKRUNTIME, "Hello World!\n");

    // Scol return value.
 	  MMpush(m, 0);

    return 0;
}

/*! @ingroup group1
 * \brief _GETblocValue : Get the value of the Bloc object
 *
 * <b>Prototype:</b> fun [BlocObj] I
 *
 * \param BlocObj : bloc object
 *
 * \return I : return bloc value, nil otherwise
 **/
int _GETblocValue(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(MSKDEBUG,"_GETblocValue\n");
#endif
    // Get bloc table from the stack, then transform it
	int blocTable = MTOP( MMget(m,0) ) ;
	if ( blocTable == NIL ) { MMset(m,0,NIL) ; return 0 ; }

    // cast this bloc in a Bloc object
	Bloc* bloc = (Bloc*)MMfetch(m, blocTable, 0) ;
	if( !bloc ) { MMset(m,0,NIL) ; return 0 ; }

    // put the return of the function call in the stack
	MMset(m,0, ITOM(bloc->getValue()));

#ifdef _SCOL_DEBUG_
	MMechostr(MSKDEBUG,"ok\n");
#endif

	return 0;
}


/*! @ingroup group1
 * \brief _SETblocValue : Set the value of the bloc
 *
 * <b>Prototype:</b> fun [BlocObj I] I
 *
 * \param BlocObj : bloc object
 * \param I : New value
 *
 * \return I : 0 if success, -1 otherwise 
 **/
int _SETblocValue(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"_SETblocValue\n");
#endif
    // Get param
	int value = MTOI( MMpull(m) ) ;

	// Get Bloc table
    int blocTable = MTOP( MMget(m,0) ) ;
	if ( blocTable == NIL ) { MMset(m,0,-1) ; return 0 ; }

    // Cast the content of bloc table to Bloc*
	Bloc* bloc = (Bloc*)MMfetch(m, blocTable, 0) ;
	bloc->setValue(value);
 	MMset(m,0,0);
	
	// send the change data callback message to scol, use PostMessage instead if you need to poll the message
	SendMessage( HScol, BLOC_NEW_DATA_CB,(int)bloc,(LPARAM)NULL);

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"ok\n");
#endif

	return 0;
}


/*! @ingroup group1
 * \brief _GETblocName : Get the name of the bloc
 *
 * <b>Prototype:</b> fun [BlocObj] S
 *
 * \param BlocObj : bloc object
 *
 * \return S : The name of the bloc if success, NIL otherwise 
 **/
int _GETblocName(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(MSKDEBUG,"_GETblocName\n");
#endif
    // Get bloc table in the stack, then transform it in system pointer
	int blocTable = MTOP( MMget(m,0) ) ;
	if ( blocTable == NIL ) { MMset(m,0,NIL) ; return 0 ; }

    // cast this bloc in a Bloc object
	Bloc* bloc = (Bloc*)MMfetch(m, blocTable, 0) ;
	if( !bloc ) { MMset(m,0,NIL) ; return 0 ; }

	// remove param from stack
	MMpull(m);

	// put in the stack the return of the function call  
	Mpushstrbloc(m, bloc->getName());

#ifdef _SCOL_DEBUG_
	MMechostr(MSKDEBUG,"ok\n");
#endif

	return 0;
}


/*! @ingroup group1
 * \brief _SETblocName : Set the name of the bloc
 *
 * <b>Prototype:</b> fun [BlocObj S] I
 *
 * \param BlocObj : bloc object
 * \param S : New name
 *
 * \return I : 0 if success, -1 otherwise 
 **/
int _SETblocName(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"_SETblocName\n");
#endif
    // Get param
	int name = MTOP( MMpull(m) ) ;

	// Get Bloc table
    int blocTable = MTOP( MMget(m,0) ) ;
	if ( blocTable == NIL ) { MMset(m,0,-1) ; return 0 ; }

    // Cast the content of bloc table to Bloc*
	Bloc* bloc = (Bloc*)MMfetch(m, blocTable, 0) ;

	char* sname = MMstartstr(m, name);
	bloc->setName(sname);
 	MMset(m,0,0);

    // send the change data callback message to scol, use PostMessage instead if you need to poll the message
	SendMessage(HScol, BLOC_NEW_DATA_CB,(int)bloc,(LPARAM)NULL);

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"ok\n");
#endif

	return 0;
}


/*!
 * \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 destroyBlocObj(mmachine m, int handsys, int blocTab)
{
	// Read the first element of a TAB element (table of objects)
	Bloc* bloc = (Bloc*) MMfetch(m, MTOP(blocTab), 0);
	if (bloc == NULL)
    {
		// Write the first element in the stack, without pulling it
		MMset(m, 0, NIL); 
		return 0;
	}

	// Safely dispose of "Bloc" pointer
	SAFE_DELETE(bloc);

	// Write the first element of a TAB element
	MMstore(m, MTOP(blocTab), 0, NULL);

	// Display debug message
	MMechostr(MSKDEBUG,"Bloc object destroyed.\n");
	return 0;
}


/*! @ingroup group1
 * \brief _CRbloc : Open, initialize Bloc object
 *
 * <b>Prototype:</b> fun [Chn] BlocObj
 *
 * \param Chn : current channel
 *
 * \return BlocObj : Bloc object if success, NIL otherwise 
 **/
int _CRbloc(mmachine m)
{
	#ifdef	_SCOL_DEBUG_
		MMechostr(MSKDEBUG,"_CRbloc\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 bloc instance
	Bloc * bloc = new Bloc();

    if (bloc == NULL)
    {
		MMechostr(MSKDEBUG, "_CRbloc ...initialization failed\n");
		SAFE_DELETE(bloc) ;
		MMpull(m);								// Pull the channel
		MMpush(m, NIL);							// Push NIL on the stack
		return 0;
	}
	MMechostr(MSKDEBUG,"_CRbloc ...initialization successful\n");

	// Allocate a space in the stack for a table of bloc objects
	int blocTab = MMmalloc(m, 1, TYPETAB);
	if (blocTab == NIL)
    {
		MMechostr(MSKDEBUG,"_CRbloc ...MMmalloc failed\n");
		SAFE_DELETE(bloc); 
		MMpull(m);								// Pull the channel
		return MMpush(m, NIL);					// Push NIL on the stack
	}
	MMechostr(MSKDEBUG,"_CRbloc ...MMmalloc successful\n");

	// Push the table of bloc objects into the stack
	MMstore(m, blocTab, 0, (int)bloc);
	MMpush(m, PTOM(blocTab));

	// Create a new scol bloc object
	k = OBJcreate(m, OBJBLOCSCOL, (int)bloc, NULL, NULL);
	MMechostr(MSKDEBUG,"_CRbloc ...object creation successful\n");

	#ifdef	_SCOL_DEBUG_
		MMechostr(MSKDEBUG,"ok\n");
	#endif

	// Return bloc object
	return k;
}


/*! @ingroup group1
 * \brief _DSbloc : Destroy bloc object
 *
 * <b>Prototype:</b> fun [BlocObj] I
 *
 * \param BlocObj : Bloc Object to destroy
 *
 * \return I : 0 if success, NIL otherwise 
 **/
int _DSbloc(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"_DSbloc\n");
#endif
	
	int bloc = MTOP( MMget(m,0) );
	if ( bloc == NIL ) { MMset(m,0,NIL); return 0; }
	
	OBJdelTM( m, OBJBLOCSCOL, PTOM(bloc) );
	MMset(m,0,0);	

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"ok\n");
#endif
	return 0;
}


/*! @ingroup group1
* \brief _CBblocChangeValue : This function sets the reflexive function to be executed when Bloc value change event happens
*
* <b>Prototype:</b> fun [BlocObj fun [ObjBloc u0 I S] u1 u0] BlocObj
*
* \param BlocObj : Bloc Object whose value has changed
* \param fun [ObjBloc u0 I S] u1 : The reflexive function to call when the event occurs.
* - I : int value of the object
* - S   : name value of the object
* \param u0 : User parameter
*
* \return BlocObj : The Bloc object whose value has changed
*/
int _CBblocChangeValue(mmachine m)
{
	// Add a reflex
	MMechostr(MSKDEBUG, "_CBblocChangeValue ...adding reflex\n");
	return OBJaddreflex(m, OBJBLOCSCOL, SCOL_BLOC_NEW_DATA_CB);
}


/*!
* \brief Callback function for calling scol reflexive function defined for BLOC_NEW_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 getBlocNewData(mmachine m, HWND h, unsigned msg, UINT id, LONG param, int *ret)
{
	int k = 0;
  
	// Cast id parameter to BlocObj type
	Bloc * bloc = (Bloc*) id;

	// Use : OBJbeginreflex(mmachine, type of object, ptr object, callback type)
	if (OBJbeginreflex(m, OBJBLOCSCOL, (int)bloc, SCOL_BLOC_NEW_DATA_CB))
    {
		MMechostr(MSKDEBUG,"Bloc not found\n");
		return 0;
	}
  
	// Retrieve current bloc value, and set it as 3rd parameter of the callback
	MMpush(m, ITOM(bloc->getValue()));
    // Retrieve current bloc name, and set it as 4th parameter of the callback
    Mpushstrbloc(m, bloc->getName());

	// Call reflex previously defined
	k = OBJcallreflex(m, 2 /*nb param after obj and u0*/);
	return k;
}


//! Nb of Scol functions or types
#define NbTplPKG	9


/*!
 *	Scol function names
 **/
char* TplName[NbTplPKG] =
{
  "_HelloWorld",
	"ObjBloc",
	"_CRbloc",
	"_DSbloc",
	"_GETblocValue",
	"_GETblocName",
	"_SETblocName",
	"_SETblocValue",
	"_CBblocChangeValue"
};


/*!
 *	Pointers to C functions that manipulate the VM for each scol function previously defined
 **/
int (*TplFunc[NbTplPKG])(mmachine m)=
{
  _HelloWorld,
  NULL,
  _CRbloc,
	_DSbloc,
	_GETblocValue,
	_GETblocName,
	_SETblocName,
	_SETblocValue,
	_CBblocChangeValue
};


/*!
 *	Nb of arguments of each scol function
 **/
int TplNArg[NbTplPKG]=
{
  0,
  TYPTYPE,
	1,
	1,
  1,
	1,
	2,
	2,
	3
};


/*!
 *	Prototypes of the scol functions
 **/
char* TplType[NbTplPKG]=
{
  "fun [] I",                                        // _HelloWorld
	NULL,
	"fun [Chn] ObjBloc",						                   // _CRbloc
	"fun [ObjBloc] I",					                       // _DSbloc
	"fun [ObjBloc] I",                                 // _GETblocValue
	"fun [ObjBloc] S",                                 // _GETblocName
	"fun [ObjBloc S] I",                               // _SETblocName
	"fun [ObjBloc I] I",                               // _SETblocValue
	"fun [ObjBloc fun [ObjBloc u0 I S] u1 u0] ObjBloc" //_CBblocChangeValue
};


/*!
 * \brief Load the template functions
 *
 * \param mmachine : scol machine structure
 *
 * \return int : 0 if succes, error code otherwise
 **/
int LoadTemplate(mmachine m)
{
	int k;

	// Declare a new type of object ("OBJBLOCSCOL")
	OBJBLOCSCOL = OBJregister(1 /*nb of callback*/, 1/* deleted from parent */, destroyBlocObj, "OBJBLOCSCOL");

	// ----- Define callbacks for the call of the reflexive function.
	// Get a new user event
	BLOC_NEW_DATA_CB = OBJgetUserEvent();

	// Associate this event with a callback	
	OBJdefEvent(BLOC_NEW_DATA_CB, (int (__cdecl *)(struct Mmachine *, int, unsigned int, int, int, int *))getBlocNewData);

	// Load package
	k = PKhardpak(m, "TemplateEngine", NbTplPKG, TplName, TplFunc, TplNArg, TplType);
	return k;
}


/*! @defgroup usmconfig Scol plugin configuration
 *  Scol plugin configuration
 *
 *  usm.ini entry :
 *  plugin plugins/template.dll SCOLloadTemplate SCOLfreeTemplate
 *
 *  @{
 **/
/** @} */


// 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");
    return LoadTemplate(m);
}

/*! 
 * \brief Ending point of the DLL
 **/
extern "C" __declspec (dllexport) int ScolUnloadPlugin()
{
    return 0;
}

//! \endcond