/*
-----------------------------------------------------------------------------
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.
-----------------------------------------------------------------------------
*/

/*
 SERIAL IO library
 First version : jully 2011
 Author : Bastien Bourineau
*/

/*! @defgroup grpserial Scol functions definition
 *  Scol functions definition
 *  @{
 */
/** @} */

#include "plugin.h"
#include "serial.h"

cbmachine ww;

HWND HScol; //Scol window Handle

//OBJECT
int OBJSERIALSCOL;
int SERIALSCOL_NB_CB = 1;

//===== CB UNIT ON ===
int SCOL_SERIAL_READ_CB = 0;
int SERIAL_READ_CB;


//*********************************************
//
// SCopenSIO
//
// open a serial port communication
// return SerialIO object
//
// fun [Chn S [I I I I]   fun [SerialIO u0 S I] I   u0]   SerialIO
//
//********************************************
//
int SCopenSIO (mmachine m)
{
  int k = 0;
  int port, baudRate, parity, byteSize, stopBits;

  int pUser = MMpull(m);
  int pCbk  = MMpull(m);
  int pSettings = MMpull(m);
  int pName = MMpull(m);

  //MMechostr (0, "_openSIO : start\n");
  // check IO device name
  if (pName == NIL)
  {
	  MMechostr (MSKDEBUG, "\n_openSIO error : IO device name is nil...\n");
    MMset(m, 0, NIL);
	  return 0;
  }
  
  std::string name = (char*)MMstartstr(m, MTOP(pName));
  //remove "com" for retro compatibility
  name = name.substr(3);
  valueOf(name, port);

  if (pSettings == NIL)
  {
	  baudRate = 9600;
	  parity = 0;
	  byteSize = 8;
	  stopBits = 0;
  }
  else
  {
	  pSettings = MTOP(pSettings);
	  baudRate = MTOI(MMfetch (m, pSettings, 0));
	  parity = MTOI(MMfetch (m, pSettings, 1));
	  byteSize = MTOI(MMfetch (m, pSettings, 2));
	  stopBits = MTOI(MMfetch (m, pSettings, 3));
  }

  // check reading callback
  if (pCbk == NIL)
  {
    MMechostr (0, "\n_openSIO error : reading callback is nil...\n");
    MMset(m, 0, NIL);
    return 0;
  }
  
  MMechostr (0, "_openSIO : new SerialIO\n");
  // launch serial connection
  SerialIO* serialObj = new SerialIO(port, baudRate, parity, byteSize, stopBits);  
  if (serialObj == 0)
  {
    MMset(m, 0, NIL);
    return 0;		
  }

  /*if (!serialObj->IsConnected())
  {
    SAFE_DELETE(serialObj);
    SEDROP(m, 4);
    MMset(m, 0, NIL);
    return 0;		
  }*/

  // 
  // create scol SerialIO object
  //
  
  int objtab = MMmalloc(m, 1, TYPETAB);
	if (objtab == NIL)
	{
		SAFE_DELETE(serialObj);
    return MERRMEM;
	}

  MMstore(m, objtab, 0, (int)serialObj);
	if ((k = MMpush(m, PTOM(objtab)))) return k;
  if ((k = OBJcreate(m, OBJSERIALSCOL, (int)serialObj, NIL, NIL))) return k;
	
  /* add reflex */
  if ((k = MMpush(m, pCbk)))     return k; /* reading callback */
  if ((k = MMpush(m, pUser)))    return k; /* user param       */
  if ((k = OBJaddreflex(m, OBJSERIALSCOL, SCOL_SERIAL_READ_CB))) return k;
  
  //MMechostr (0, "_openSIO : done SerialIO\n");
  return 0;
}


/*! @ingroup grpx10
* \brief _closeSIO : This function destroy a X10 device Object
*
* <b>Prototype:</b> fun [SerialIO] I
* \param SerialIO : the current serial device
*
* \return I : 0 if success, NIL otherwise
*/
int SCcloseSIO(mmachine m) 
{
#ifdef _SCOL_DEBUG_
	MMechostr(MSKDEBUG,"SCcloseSIO\n");
#endif

	int objtab = MMget(m, 0);
	if (objtab == NIL)
  {
    MMechostr(MSKDEBUG, "_closeSIO : SerialIO NIL\n");
		MMset(m, 0, NIL);
		return 0;
	}

  OBJdelTM(m, OBJSERIALSCOL, objtab);
	MMset(m, 0, ITOM(0));

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"ok\n");
#endif
	return 0;
}

/*! @ingroup grpserial
* \brief _writeSIO : This function send data on serial device
*
* <b>Prototype:</b> fun [SerialIO S] I
* \param SerialIO : the current Serial device
* \param S : data to send
*
* \return I : 0 if success, NIL otherwise
*/
int SCwriteSIO(mmachine m)
{
	int s = MMpull(m);
  int objtab = MMget(m, 0);
	if ((s == NIL) || (objtab == NIL))
	{
  	MMset(m, 0, NIL);
		return 0;
	}

	SerialIO* serialObj = (SerialIO*)MMfetch(m, MTOP(objtab), 0);
  if (serialObj == 0)
  {
    MMset(m, 0, NIL);
		return 0;
  }

  std::string data = (char*)MMstart(m, (MTOP(s))+1);
  serialObj->Write(data);

  MMset(m, 0, ITOM(0));
	return 0;
}
 

//*********************************************
//
// SCtestSIO
//
// test dll
//
// fun [SerialIO S] I
//
//********************************************
//
int SCtestSIO (mmachine m)
{
	int s = MMpull (m);
	if (s == NIL)
	{
		MMechostr(0, "\n_writeSIO error : data is nil...\n");
		MMset(m, 0, NIL);
		return 0;
	}

  return MMpush(m, 0);
} 

int getSerialReadCb(mmachine m,HWND h,unsigned msg,UINT id,LONG param,int *ret)
{
	int k = 0;
  SerialIO* serialObj = (SerialIO*)id;
  std::string* data = (std::string*)param;

  if (serialObj == 0)
  {
    delete(data);
    return 0;
  }

	// OBJbeginreflex(mmachine, objecttype, objectptr, cbtype)
  if (OBJbeginreflex(m, OBJSERIALSCOL, (int)serialObj, SCOL_SERIAL_READ_CB))
  {
    delete(data);
    return 0;
  }

  int res = MMmalloc (m,((data->size()+4)>>2)+1, TYPEBUF);
  *(int*) MMstart(m, res) = data->size();
  char* BS = (char*)MMstart(m, res + 1);
  memcpy(BS, data->c_str(), data->size());

	if ((k = MMpush(m, (res<<1) + 1))) return k;
  if ((k = MMpush(m, ITOM(data->size())))) return k;

	k=OBJcallreflex(m, 2);
  delete(data);
	return k;
}


#define NSCOLSIO 4 

char* sioname[NSCOLSIO]=
{
 "_openSIO",
 "_closeSIO",
 "_writeSIO",
 "_testSIO"
 };


int (*siofun[NSCOLSIO])(mmachine m) =
{
 SCopenSIO,
 SCcloseSIO,
 SCwriteSIO,
 SCtestSIO
};


int sionarg[NSCOLSIO]=
{
 5,
 1,
 2,
 2 
 };


char* siotype[NSCOLSIO]=
{
 "fun [Chn S [I I I I] fun [SerialIO u0 S I] I u0] SerialIO",
 "fun [SerialIO] I",
 "fun [SerialIO S] I",
 "fun [SerialIO S] I"
 };


//!Scol CallBack for SerialIO destruction
int destroySerialIO(mmachine m, int handsys, int objtab) 
{
	SerialIO* serialObj = (SerialIO *) MMfetch(m, MTOP(objtab), 0);
	SAFE_DELETE(serialObj);
	MMstore(m, MTOP(objtab), 0, 0);

	return 0;
}


// Everything inside _cond and _endcond is ignored by doxygen
//! \cond
/*!
* \brief Load the packages in Scol virtual machine
* \param mmachine : the scol machine
*/
int LoadSerialIO(mmachine m)
{
	int			k;
  HScol = (HWND)SCgetExtra("hscol") ;

  // Scol type declaration
	OBJSERIALSCOL = OBJregister(SERIALSCOL_NB_CB, 0, destroySerialIO, "SerialIO");


	//******************************    Callbacks for serial    *******************************************************
    
  //===== CB READ ===
  SERIAL_READ_CB = OBJgetUserEvent();
  OBJdefEvent(SERIAL_READ_CB, (int (__cdecl *)(struct Mmachine *,int,unsigned int,int,int,int *))getSerialReadCb);

	// load scol functions
	k = PKhardpak(m, "serialIO.pkg", NSCOLSIO, sioname, siofun, sionarg, siotype);

  MMechostr(MSKDEBUG,"\n" );
	return k;
}
//! \endcond


/*! 
* \brief Starting point of the DLL
*/
extern "C" __declspec (dllexport) int ScolLoadPlugin(mmachine m, cbmachine w)
{
	int k = 0;
  SCOLinitplugin(w);
	LoadSerialIO(m);
	return k;
}


/*! 
* \brief Ending point of the DLL
*/
extern "C" __declspec (dllexport) int ScolUnloadPlugin()
{
	return 0;
}
