/*     
      SCOL ENVIRONMENT . Magma 1.0 . 1997 . Sylvain HUET

         scolobj.c : gestion des objets de l'environnement SCOL
*/
//
// Modification history:
//
//$ FA(29/11/2000): Debugger integration. Add notification of channel destruction
//
//$ LB(17/07/2001): add OBJsetHandsys function
//
//$ FA(12/11/2001): Replace SCOL_DEBUGGER_AWARE by INCLUDE_DEBUGGER
//
//$ LB (13/06/2002) : changed objbitmap management according to the new objbitmap structure
//
//$ LB (13/06/2002) : introduce a management of objects handler, in order to
//                    centralize the handler system of the ObjBitmaps, according to the
//                    new ObjBitmap structure.
//


#include "scolPrerequisites.h"
#include "common/vscol.h"
#include "scolConstants.h"

#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
#  include <windows.h>
#endif


#include"scolMMemory.h"
#include"listlab.h"
#include"vm/mbytec.h"
#include"mbytec2.h"
#include"scol.h"
#include"scolobj.h"
//$ FA(29/11/2000): Use debugger interface
#include "debug.h"
#if defined(INCLUDE_DEBUGGER)
#  include "fifo.h" // declaration of SCgetsocket()
#endif
//
#include "scolMacros.h"

// $ BB update max type to 120
#define MAXTYPOBJ   120
#define NEWTYPOBJ   4

int nexttypobj;

// $ BB update max type to 120
int nbreflex[MAXTYPOBJ] =
{
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0
};

// $ BB update max type to 120
int recreflex[MAXTYPOBJ] =
{
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0
};

// $ BB update max type to 120
int (*cbdestroy[MAXTYPOBJ])(mmachine m, int handsys, int objm) =
{
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL,
 NULL,NULL,NULL,NULL
};

// $ BB update max type to 120
char stringobj[MAXTYPOBJ][32] =
{
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","",""
};

//$BB pass from 128 to 256
//TODO change the method to optimize callbacks
int msghook[MAXHOOK];
#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
int (*cbhook[MAXHOOK])(mmachine m, HWND hwnd, unsigned msg, UINT wParam, LONG lParam, int *ret);
#else
int (*cbhook[MAXHOOK])(mmachine m, int hwnd, unsigned msg, int wParam, int lParam, int *ret);
#endif

int nbhook;














/****************************************************************************/
/* scol v 4                                                                 */
/*                                                                          */
/* int OBJsetHandsys (mmachine m, int typ, int oldHandsys, int newHandsys)  */
/*                                                                          */
/* replace the system handler of the object                                 */
/*                                                                          */
/****************************************************************************/
int OBJsetHandsys (mmachine m, int typ, int oldHandsys, int newHandsys)
{
	int p, q, handptr, hand;

  p = MMgetglobal(m,OFFSCOBJ);
  while (p != NIL)
  {
    p >>= 1;
    q = MMfetch(m, p, OFFLVAL) >>1;

    if (q != NIL)
	  {
		  handptr = MMfetch(m, q, OFFOBJHAND) >>1;
		  if (handptr != NIL) hand = *(int*)MMstart(m, handptr);
		  // check if it's the good object
		  if ((hand == oldHandsys) && (MMfetch(m, q, OFFOBJTYP) == (typ<<1))) 
		  {
			  //it is, so change the handler
			  *(int*)MMstart (m, handptr) = newHandsys;
			  return 0;
		  }
	  }
    p = MMfetch(m, p, OFFLNEXT);
  }
  return -1;
}






/* enregistrement d'un nouveau type d'objet */
int OBJregister(int nbrefl,int recrefl,int (*destroy)(mmachine m,int handsys,int objm),char *name)
{
	if (nexttypobj>=MAXTYPOBJ) return -1;
	nbreflex[nexttypobj]=nbrefl;
	recreflex[nexttypobj]=recrefl;
	cbdestroy[nexttypobj]=destroy;
	strcpy(stringobj[nexttypobj],name);
	nexttypobj++;
	return nexttypobj-1;
}


/* determine un numero de type en fonction du nom */
int OBJtypebyname(char *name)
{
	int i;

	for(i=nexttypobj-1;i>=0;i--)
		if (!strcmp(stringobj[i],name)) return i;
	return -1;
}

#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
/* enregistrement d'un nouvel evenement */
int OBJdefEvent(int event,int (*cbevent)(mmachine m,HWND hwnd, unsigned msg,
				UINT wParam,LONG lParam,int *ret))
{
	if (nbhook>=MAXHOOK) return -1;
	msghook[nbhook]=event;
	cbhook[nbhook]=cbevent;
	nbhook++;
	return 0;
}
#elif SCOL_PLATFORM == SCOL_PLATFORM_LINUX
/* enregistrement d'un nouvel evenement */
int OBJdefEvent(int event,int (*cbevent)(mmachine m,int hwnd, unsigned msg,
					 int wParam,int lParam,int *ret))
{
	if (nbhook>=MAXHOOK) return -1;
	msghook[nbhook]=event;
	cbhook[nbhook]=cbevent;
	nbhook++;
	return 0;
}
#endif

#if SCOL_PLATFORM != SCOL_PLATFORM_WINDOWS
#  define WM_USER (1)
#endif

int nbuserev;

/* obtention d'un nouvel evenement utilisateur */
int OBJgetUserEvent()
{
	nbuserev++;
	return nbuserev;
}

/* destruction d'un objet (pas de recursion a faire)
typ : type d'objet
handsys : handler systeme associe
objm : objet magma associe
*/
int OBJdestroy(mmachine m, int typ,int handsys,int objm,int flag)
{

//***********************************
#if SCOL_DEBUG
MMechostr (0, "\nOBJdestroy");
#endif
//***********************************

  if ((flag==0)&&(recreflex[typ]==0)) return 0;
  if (flag==-1) return 0;
  if ((typ>=NEWTYPOBJ)&&(typ<MAXTYPOBJ))
  {
	  if (cbdestroy[typ]) (*cbdestroy[typ])(m,handsys,objm);
	  return 0;
  }

  return 0;
}

/* trouve un objet en fonction de l'objet magma (notation obj) et du typ (notation int)
 retourne le pointeur ou -1 si introuvable */
int OBJfindTM(mmachine m,int typ,int objm)
{
  int p,q;

  p=MMgetglobal(m,OFFSCOBJ);
  while(p!=NIL)
    {
      p>>=1;
      q=MMfetch(m,p,OFFLVAL)>>1;
      if (q!=NIL)
	{
	  if ((MMfetch(m,q,OFFOBJMAG)==objm)
	      &&(MMfetch(m,q,OFFOBJTYP)==typ*2)) return q;
	}
      p=MMfetch(m,p,OFFLNEXT);
    }
  return -1;
}

/* trouve un objet en fonction du handler systeme (notation int) et du typ (notation int)
 retourne le pointeur ou -1 si introuvable */
int OBJfindTH(mmachine m,int typ,int handsys)
{
  int p,q,hand;

  p=MMgetglobal(m,OFFSCOBJ);
  while(p!=NIL)
  {
    p>>=1;
    q=MMfetch(m,p,OFFLVAL)>>1;
    if (q!=NIL)
	  {
		  hand=MMfetch(m,q,OFFOBJHAND)>>1;
		  if (hand!=NIL)
        hand=*(int*)MMstart(m,hand);
		  
      if ((hand==handsys)&&(MMfetch(m,q,OFFOBJTYP)==typ*2))
        return q;
	  }
    p=MMfetch(m,p,OFFLNEXT);
  }
  return -1;
}

/* trouve un objet en fonction du handler systeme pere (notation int)
 retourne le pointeur ou -1 si introuvable */
int OBJfindTP(mmachine m,int typ,int handpere)
{
  int p,q,hand;

  p=MMgetglobal(m,OFFSCOBJ);
  while(p!=NIL)
    {
      p>>=1;
      q=MMfetch(m,p,OFFLVAL)>>1;
      if (q!=NIL)
	  {
		  hand=MMfetch(m,q,OFFOBJHPERE)>>1;
		  if (hand!=NIL) hand=*(int*)MMstart(m,hand);
		  if ((hand==handpere)&&(MMfetch(m,q,OFFOBJTPERE)==typ*2)) return q;
	  }
      p=MMfetch(m,p,OFFLNEXT);
    }
  return -1;
}

/* trouve un objet en fonction du canal (notation obj)
 retourne le pointeur ou -1 si introuvable */
int OBJfindChn(mmachine m,int channel)
{
  int p,q;

  p=MMgetglobal(m,OFFSCOBJ);
  while(p!=NIL)
    {
      p>>=1;
      q=MMfetch(m,p,OFFLVAL)>>1;
      if (q!=NIL)
	{
	  if (MMfetch(m,q,OFFOBJCHN)==channel) return q;
	}
      p=MMfetch(m,p,OFFLNEXT);
    }
  return -1;
}

int OBJecho(mmachine m)
{
  int p,q;

  p=MMgetglobal(m,OFFSCOBJ);
  while(p!=NIL)
    {
      p>>=1;
      q=MMfetch(m,p,OFFLVAL)>>1;
      if (q!=NIL)
	{
		  MMechostr(MSKDEBUG,"objs: %x %d %d\n"
			,MMfetch(m,q,OFFOBJCHN)>>1
			,MMfetch(m,q,OFFOBJTYP)>>1
			,*(int*)MMstart(m,MMfetch(m,q,OFFOBJHAND)>>1)
			);
	}
      p=MMfetch(m,p,OFFLNEXT);
    }
  return -1;
}

/* creation d'un nouvel objet.
en 1 : canal
en 0 : objet magma
retourne : 
en 0 : objet inchange
*/
int OBJcreate(mmachine m,int typ,int handsys,int typpere,int handsyspere)
{
  int i,k,s;

//  if (typ==OBJTYPWINDOW) MMechostr(MSKDEBUG,"create win %d %x\n",typ,handsys);
  
  if ((typ<0)||(typ>=MAXTYPOBJ)) return MERRTYP;
  if ((MMget(m,1)==NIL))
  {
	  MMechostr(MSKTRACE,"Object Channel is nil %x %s\n",handsys,stringobj[typ]);
/*	  return MERRTYP;*/
  }
  if (MMpush(m,MMget(m,1))) return MERRMEM;
  MMset(m,2,MMget(m,1));
  if (MMpush(m,typ*2)) return MERRMEM;

  s=MMmalloc(m,4,TYPEBUF); if (s==NIL) return MERRMEM;
  *((int*)MMstart(m,s))=handsys;
  if (MMpush(m,s+s+1)) return MERRMEM;

  if (MMpush(m,typpere*2)) return MERRMEM;

  s=MMmalloc(m,4,TYPEBUF); if (s==NIL) return MERRMEM;
  *((int*)MMstart(m,s))=handsyspere;
  if (MMpush(m,s+s+1)) return MERRMEM;

  for(i=0;i<nbreflex[typ];i++)
    {
      if (MMpush(m,NIL)) return MERRMEM;
      if (MMpush(m,NIL)) return MERRMEM;
    }
  if (MMpush(m,(nbreflex[typ]*2+OFFOBJREF0)*2)) return MERRMEM;
  if (k=MBdeftab(m)) return k;
  if (MMpush(m,MMgetglobal(m,OFFSCOBJ))) return MERRMEM;
  if (MMpush(m,2*2)) return MERRMEM;
  if (k=MBdeftab(m)) return k;
  MMsetglobal(m,OFFSCOBJ,MMpull(m));
  return 0;
}

/* definition d'un reflexe
en 2 : objet magma
en 1 : fonction magma
en 0 : argument user
retourne :
en 0 : objet
*/
int OBJaddreflex(mmachine m,int typ, int num)
{
  int p;

  if ((typ<0)||(typ>=MAXTYPOBJ)) return MERRTYP;
  if ((num<0)||(num>=nbreflex[typ])) return MERRTYP;
	
  p=OBJfindTM(m,typ,MMget(m,2));
  if (p==-1)
    {
      MMpull(m);
      MMpull(m);
      return 0;
    }
  MMstore(m,p,OFFOBJREF0+2*num+1,MMpull(m));
  MMstore(m,p,OFFOBJREF0+2*num,MMpull(m));
  return 0;
}


/* preparation de l'appel d'un reflexe pour l'objet p, reflexe num*/
int OBJpreparereflex(mmachine m,int p,int num)
{
  MMsetglobal(m,OFFSCCUR,MMfetch(m,p,OFFOBJCHN));
  if (MMpush(m,MMgetglobal(m,OFFSCCUR))) return MERRMEM;
  if (MMpush(m,MMfetch(m,p,OFFOBJREF0+num*2))) return MERRMEM;
  if (MMpush(m,MMfetch(m,p,OFFOBJMAG))) return MERRMEM;
  if (MMpush(m,MMfetch(m,p,OFFOBJREF0+num*2+1))) return MERRMEM;
  return 0;
}

/* commence le traitement d'un reflexe d'un certain type, avec un certain handler
et un certain numero de reflexe.
retourne :
 0 si OK
 1 si objet non trouve
 autre si erreur RUNTIME
 */
int OBJbeginreflex(mmachine m,int typ,int handsys,int num)
{
  int p,k;


//***********************************
#if SCOL_DEBUG
MMechostr (0, "\nOBJbeginReflex %s", stringobj[typ]);
#endif
//***********************************
  p=OBJfindTH(m,typ,handsys);
  if (p==-1) 
    return 1;

  if (MMfetch(m,p,OFFOBJREF0+num*2)==NIL) 
  {
	  return -1;
  }

  k = OBJpreparereflex(m,p,num);
  return k;
}

/* appel du reflexe
  nbarg est le nombre d'arguments ajoutes par l'utilisateur */
int OBJcallreflex(mmachine m,int nbarg)
{
  int i;

  if (MMpush(m,MMget(m,nbarg+2))) return MERRMEM;
  i=Minterpreter(m);
  MMsetglobal(m,OFFSCCUR,MMget(m,2));
  MMset(m,1,MMget(m,0));
  if (i!=1) return i;
  MMpull(m);
  MMpull(m);
  MMpull(m);
  return 0;
}



int OBJdelobj(mmachine m,int todel)
{
  int p,q;

  //SH000808
  p=MMgetglobal(m,OFFSCOBJ)>>1;
  q=NIL;
  while(p!=NIL)
  {
	  if (MMfetch(m,p,OFFLVAL)==todel)
	  {
		  if (q==NIL) MMsetglobal(m,OFFSCOBJ,MMfetch(m,p,OFFLNEXT));
		  else MMstore(m,q,OFFLNEXT,MMfetch(m,p,OFFLNEXT));
		  return 0;
	  }
	  q=p;
	  p=MMfetch(m,p,OFFLNEXT)>>1;
  }
  //
  return 0;
}

/* destruction d'un objet */
int OBJdel(mmachine m,int p,int flag)
{
   int typ;
   int hand;
   int q;


//***********************************
#if SCOL_DEBUG
MMechostr (0, "\nOBJdel");
#endif
//***********************************

   OBJdelobj(m,p+p+1);
   typ=MMfetch(m,p,OFFOBJTYP)>>1;
   hand=MMfetch(m,p,OFFOBJHAND)>>1;
   if (hand!=NIL) hand=*(int*)MMstart(m,hand);


   q = MMfetch(m,p,OFFOBJMAG);
   while((p=OBJfindTP(m,typ,hand))!=-1) OBJdel(m,p,0);
   OBJdestroy(m,typ,hand,q,flag);

   return 0;
};

/* destruction d'un objet en fonction de son type et du handler systeme */
int OBJdelTH(mmachine m,int typ,int handsys)
{
  int p;

//***********************************
#if SCOL_DEBUG
MMechostr (0, "\nOBJdelTH");
#endif
//***********************************

  p=OBJfindTH(m,typ,handsys);
  if (p==-1) return 0;
  return OBJdel(m,p,1);
}

/* destruction d'un objet en fonction de son type et de l'objet magma */
int OBJdelTM(mmachine m,int typ,int q)
{
  int p;

  p=OBJfindTM(m,typ,q);
  if (p==-1) return 0;
  return OBJdel(m,p,1);
}

/* destruction de tous les objets lies a un canal (notation obj) */
int OBJdelChn(mmachine m,int canal)
{
  int p;

  if (MMpush(m,canal)) return MERRMEM;
  while((p=OBJfindChn(m,MMget(m,0)))!=-1) OBJdel(m,p,1);
#if defined(INCLUDE_DEBUGGER)
{
  int res;
  SEDUP(m); // push channel
  if (res = DBGNotifyChannelClosed(m))
    return res;
}
#endif
  SEDROP(m, 1);
//
  return 0;
}

/* donne a un canal tous les objets d'un autre */
int OBJchgchannel(mmachine m,int oldchn, int newchn)
{
  int p,q;

  p=MMgetglobal(m,OFFSCOBJ);
  while(p!=NIL)
    {
      p>>=1;
      q=MMfetch(m,p,OFFLVAL)>>1;
      if ((q!=NIL)&&(MMfetch(m,q,OFFOBJCHN)==oldchn))
		  MMstore(m,q,OFFOBJCHN,newchn);
      p=MMfetch(m,p,OFFLNEXT);
    }
  return 0;
}

/* compte le nombre d'objets d'un certain type */
int OBJcountT(mmachine m,int typ)
{
  int p,q,n;
  n=0;
  p=MMgetglobal(m,OFFSCOBJ);
  while(p!=NIL)
    {
      p>>=1;
      q=MMfetch(m,p,OFFLVAL)>>1;
      if (q!=NIL)
	  {
/*		  MMechostr(MSKDEBUG,"typobj=%d\n",MMfetch(m,q,OFFOBJTYP)>>1);*/
		  if (MMfetch(m,q,OFFOBJTYP)==typ*2) n++;
	  }
      p=MMfetch(m,p,OFFLNEXT);
    }
  return n;
}



#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
#include "OS_specific/windows/objstr.h"
/* compte le nombre d'objets d'un certain type */
//$ LB (13/06/2002) : changed objbitmap management according to the new objbitmap structure
int OBJfindFloatBmp(mmachine m)
{
  int p, q, objm, n;
  PtrObjVoid O;
  PtrObjBitmap B;
  
  n = 0;
  p = MMgetglobal(m, OFFSCOBJ);
  while(p != NIL)
  {
    p >>= 1;
    q = MMfetch(m, p, OFFLVAL)>>1;
    if ((q != NIL) && (MMfetch(m, q, OFFOBJTYP) == OBJtypebyname("OBJTYPBITMAP")*2))
	  {
		  n++;
		  objm = MMfetch(m, q, OFFOBJMAG);
		  if (!(m->tape[(objm>>1)+1]&1))
		  {
			  O = (PtrObjVoid)MMstart(m, objm>>1);
			  MMechostr(MSKDEBUG,">>>>>>>FLOATING BITMAP\n");
			  /* debut rem */
			  B = (PtrObjBitmap)MMstart(m, O->Buffer>>1);
			  if (O->Tab == NIL) MMechostr(MSKDEBUG, "bitmap is _CRbitmap\n");
			  else  MMechostr(MSKDEBUG, "bitmap is _LDbitmap : %s\n", MMstartstr(m, O->Tab>>1));
			  MMechostr(MSKDEBUG,"deleting bitmap %x %d %d\n", B->handler, B->TailleW, B->TailleH);
			  /* fin rem */
			  OBJdel(m, q, 1);
		  }
	  }
    p = MMfetch(m, p, OFFLNEXT);
  }
  MMechostr(MSKDEBUG, "---NbBitmaps = %d\n",n);
  return 0;
}
#else
/* compte le nombre d'objets d'un certain type */
int OBJfindFloatBmp(mmachine m)
{
  return 0;
}
#endif


/* initialisation de la gestion des objets */
int OBJinit()
{
	nexttypobj = NEWTYPOBJ;
	nbhook = 0;
	nbuserev = WM_USER + 20;
	return 0;
}

