/*     
      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<stdio.h>
#include<string.h>
#include "include/vscol.h"

#ifdef VERSION_WIN
#include <windows.h>
#endif


#include"mmemory.h"
#include"listlab.h"
#include"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 "macros.h"


#define MAXTYPOBJ   60
#define NEWTYPOBJ   4

int nexttypobj;

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
};

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
};

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
};

char stringobj[MAXTYPOBJ][32]=
{
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","","",
	"","","",""
};

#define MAXHOOK 128
int msghook[MAXHOOK];
#ifdef VERSION_WIN
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;
}

#ifdef VERSION_WIN
/* 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;
}
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
/* 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

#ifndef VERSION_WIN
#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");
#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;
#ifdef VERSION_MAC
   int q;
#endif


//***********************************
#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);


#ifdef VERSION_MAC
   q = MMfetch(m,p,OFFOBJMAG);
   while((p=OBJfindTP(m,typ,hand))!=-1) OBJdel(m,p,0);
   OBJdestroy(m,typ,hand,q,flag);
#else

   OBJdestroy(m,typ,hand,MMfetch(m,p,OFFOBJMAG),flag);

   while((p=OBJfindTP(m,typ,hand))!=-1) OBJdel(m,p,0);
#endif
   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;
}



#ifdef VERSION_WIN
#include "../win/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;
}

