//
//$ FA(20/07/2001): Replace 100000 by 10000 in mt_minper()
//

#include <stdio.h>
#include <string.h>


#include <windows.h>
#include <winuser.h>
#include <mmsystem.h>

#include "../kernel/include/kernel.h"
#include "../kernel/listlab.h"
#include "../kernel/include/common.h"
#include "winscol.h"

/* reflexes type TIMER */
#define RFLTIMER_NB     1
#define RFLTIMER_CLOCK  0

int OBJTYPTIMER;


int OBJTYPTIMERMM;
int WM_TMM;
TIMECAPS tc;

#define MT_NB 4096
int mt_per[MT_NB];
int mt_nxt[MT_NB];
int mt_param[MT_NB];
int (*mt_fun[MT_NB])(int i,int param);
int mt_ind;

int mt_timer;
int mt_curper;
int flag_tm;

int mt_new()
{
	int i;
	for(i=0;i<mt_ind;i++) if (!mt_per[i]) return i;
	if (mt_ind>=MT_NB) return -1;
	mt_ind++;
	return mt_ind-1;
}

int mt_minper()
{
	int i,min;

	min=-1;
	for(i=0;i<mt_ind;i++)
		if ((mt_per[i])&&((min==-1)||(mt_per[i]<min))) min=mt_per[i];
	if (min==-1) return min;
	min>>=3;
	if (min<10) return 10;
	if (min>10000) return 10000; //$ FA(20/07/2001)
	return min;
}


int mt_event(mmachine m,HWND h,unsigned msg,UINT x,LONG y,int *ret)
{
  int t,i;


//  MMechostr(MSKDEBUG,"### event\n",GetTickCount());
  t=GetTickCount()+1;

  for(i=0;i<mt_ind;i++) if (mt_per[i])
  {
	  if (t-mt_nxt[i]>=0)
	  {
		  (*mt_fun[i])(i,mt_param[i]);
		  if (mt_per[i]) mt_nxt[i]+=(1+((t-mt_nxt[i])/mt_per[i]))*mt_per[i];
	  }
  }

  flag_tm=0;
  return 0;
}

void CALLBACK mt_clockCB(UINT sys,UINT uMsg,DWORD x,DWORD dw1,DWORD dw2)
{
	if (!flag_tm)
	{
		flag_tm=1;
		PostMessage(hscol,WM_TMM,0,0);
	}
}

int restart()
{
	int t;

	t=mt_minper();
//	MMechostr(MSKDEBUG,"restart %d %d %d\n",mt_curper,t,GetTickCount());
	if ((mt_curper)&&(t!=mt_curper))
	{
//		MMechostr(MSKDEBUG,"##stop %d\n",mt_curper);
		timeKillEvent(mt_timer);
		mt_curper=0;
	}
	if ((!mt_curper)&&(t!=-1))
	{
		mt_curper=t;
//		MMechostr(MSKDEBUG,"##restart %d\n",mt_curper);
		mt_timer=timeSetEvent(t,tc.wPeriodMax,mt_clockCB,0,TIME_PERIODIC);
	}
	return 0;
}

int mt_start(int per,int param,int (*fun)(int i,int param))
{
	int i;

//	MMechostr(MSKDEBUG,"start %d\n",per);
	if ((per<=0)||(fun==NULL)) return -1;
	if (per<10) per=10;
	i=mt_new();
	if (i<0) return -1;
	mt_per[i]=per;
	mt_param[i]=param;
	mt_fun[i]=fun;
	mt_nxt[i]=GetTickCount()+per;
	restart();
//	MMechostr(MSKDEBUG,"### new %d\n",i); // SH000821
	return i;
}

int mt_del(int i)
{
//	MMechostr(MSKDEBUG,"### del %d\n",i); // SH000821
	if ((i<0)||(i>=MT_NB)) return -1;
	mt_per[i]=0;
	while((mt_ind>0)&&(mt_per[mt_ind-1]==0)) mt_ind--;
	restart();
	return 0;
}

int mt_ini()
{
	mt_ind=0;
	mt_curper=0;
	flag_tm=0;
	return 0;
}


int ClockCB(int sys,int param)
{
	int k;
	mmachine m;

	m=(mmachine)param;
	k=OBJbeginreflex(m,OBJTYPTIMERMM,sys,RFLTIMER_CLOCK);
	if (k==0) OBJcallreflex(m,0);
	return 0;
}

int TMMMstarttimer(mmachine m)
{
  int freq,k,sys;

  freq=MMget(m,0);
  if ((MMget(m,1)==NIL)||(freq<=0))
    {
      MMpull(m);
      MMset(m,0,NIL);
      return 0;
    }
  sys=mt_start(freq>>1,(int)m,ClockCB);
  if (sys<0)
    {
      MMpull(m);
      MMset(m,0,NIL);
      return 0;
    }
  if (MMpush(m,sys*2)) return MERRMEM;
  if (MMpush(m,0*2)) return MERRMEM;
  if (MMpush(m,3*2)) return MERRMEM;
  if (k=MBdeftab(m)) return k;
  return OBJcreate(m,OBJTYPTIMERMM,sys,-1,0);
}

int TMMMdeltimer2(mmachine m,int handsys,int objm)
{
	mt_del(handsys);
	MMstore(m,objm>>1,1,NIL); //SH000821
    return 0;
}

int TMMMdeltimer(mmachine m)
{
  int i;

  i=MMget(m,0)>>1;
  if (i==NIL) return 0;
  OBJdelTH(m,OBJTYPTIMERMM,MMfetch(m,i,1)>>1);
  MMset(m,0,0);
  return 0;
}

int TMMMrfltimer(mmachine m)
{
  return OBJaddreflex(m,OBJTYPTIMERMM,RFLTIMER_CLOCK);
}

#define NTIMEPKG 4
char* timename[NTIMEPKG]=
{"Timer","_starttimer","_rfltimer","_deltimer"};

int (*timefun[NTIMEPKG])(mmachine m)=
{
 NULL,TMMMstarttimer,TMMMrfltimer,TMMMdeltimer
};

int timenarg[NTIMEPKG]=
{
 TYPTYPE,2,3,1
};

char* timetype[NTIMEPKG]=
{
    NULL,"fun [Chn I] Timer","fun [Timer fun [Timer u0] u1 u0] Timer","fun [Timer] I"
};

int SCOLloadTimer(mmachine m)
{
  timeGetDevCaps(&tc, sizeof(TIMECAPS));
	MMechostr(MSKDEBUG,"timer res=%d %d\n",tc.wPeriodMin,tc.wPeriodMax);
	timeBeginPeriod(tc.wPeriodMax);
	WM_TMM=OBJgetUserEvent();
	OBJdefEvent(WM_TMM,mt_event);
	OBJTYPTIMERMM=OBJregister(RFLTIMER_NB,0,TMMMdeltimer2,"OBJTYPTIMERMM");
//$ FA(23/11/2001)
  if (tc.wPeriodMin <= 0 || tc.wPeriodMin > 1000) {
    const char* msg = 
      "Il se peut qu'il y ait un conflit avec d'autres logiciels intallés sur votre ordinateur."
      "Essayer de désactiver votre anti-virus (si vous en avez un) et de redémarrer votre ordinateur."
      "Pour en savoir plus, vous pouvez consulter la note technique à l'URL suivante : "
      "http://techno.cryonetworks.com/Support/SCOL/SCOL_Support.htm\n\n"
      "There may be a problem with other software installed on your computer. Try to disable your "
      "antivirus software (in case you have one) and restart your computer. To know more about this problem,"
      "you can read the technical note at the following Internet address: "
      "http://techno.cryonetworks.com/Support/SCOL/SCOL_Support.htm";
    MessageBox(hscol, msg, "Fatal Error", MB_OK);
    return -1;
  }
//
/*	SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_LOWEST);*/

	return PKhardpak(m,"timer.pkg",NTIMEPKG,timename,timefun
		   ,timenarg,timetype);
}

int SCOLendTimer(mmachine m)
{
    if (mt_curper)
	{
		MMechostr(1,">>>>>kill timer\n");
		timeKillEvent(mt_timer);
		mt_curper=0;
	}
	timeEndPeriod(tc.wPeriodMax);
	return 0;
}