/*->c.main */


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


#include "kernel.h"
#include "swis.h"


#include "h.etc"


#include "h.header"


#include "h.main"



#define Message_TaskInitialise 0x400C2
#define Message_TaskCloseDown  0x400C3



       void * xprivate_word;


extern void claimvectors(void);
extern void relvectors(void);
extern void setfixmode(int newmode);
extern void fixprintservice(int mode);
extern void fixspriteservice(int mode);

static char * xstrcpy(char *s1, const char *s2)
{
 while((*s1++=*s2++)>=32);
 *(s1-1)=0;
 return(s1);
}



static int xcstrncmp(char * first,char * second,int n)
{
 int f;
 int s;

 while(n--)
 {
  f=toupper(*first++);
  s=toupper(*second++);
  if(f<32 && s<32) return(0);
  if(f!=s) return(f-s);
 }

 return(0);
}



static int xcstrcmp(char * first,char * second)
{
 return(xcstrncmp(first,second,-1));
}






static char * xstrncpy(char *s1,char *s2,int n)
{

 while(n-->0)
   if((*s1++=*s2++)<32) break;

 *(s1-1)=0;

 return(s1);
}






#define BLENDBIT 0x1
#define LOWBIT   0x2
#define GDRAWBIT 0x4



#define MAXTASKNAME 64


typedef struct taskdatastr
{
 char   name[MAXTASKNAME];
 int    flags;
 struct taskdatastr * next;

} taskdatastr;




typedef struct filterstr
{
 taskdatastr      * data;
 int                taskhandle;
 struct filterstr * next;

} filterstr;


static int defaultflags=BLENDBIT|GDRAWBIT;

static taskdatastr * firsttask=NULL;
static filterstr   * firstfilter=NULL;


static taskdatastr * findtask(char * name)
{
 taskdatastr * taskp;

 taskp=firsttask;

 while(taskp)
 {
  if(xcstrcmp(taskp->name,name)==0) break;
  taskp=taskp->next;
 }

 return(taskp);
}


static taskdatastr * addtask(char * name,int flags)
{
 taskdatastr * taskp;

 taskp=malloc(sizeof(taskdatastr));
 if(taskp)
 {
  taskp->next=firsttask;
  firsttask=taskp;

  xstrncpy(taskp->name,name,MAXTASKNAME);
  taskp->flags=flags;
 }

 return(taskp);
}



static void listtasks(void)
{
 taskdatastr * taskp;
 char          flags[32];

 taskp=firsttask;


 printf("Default setting %s%s%s\n",(defaultflags&BLENDBIT)?"B":"~B",
                                   (defaultflags&LOWBIT)?"L":"~L",
                                   (defaultflags&GDRAWBIT)?"G":"~G");

 while(taskp)
 {
  sprintf(flags,"%s%s%s",(taskp->flags&BLENDBIT)?"B":"~B",
                         (taskp->flags&LOWBIT)?"L":"~L",
                         (taskp->flags&GDRAWBIT)?"G":"~G");


  printf("%6s    %s\n",flags,taskp->name);
  taskp=taskp->next;
 }
}






static filterstr * findfilter(int taskhandle)
{
 filterstr * filterp;

 filterp=firstfilter;

 while(filterp)
 {
  if(filterp->taskhandle==taskhandle) break;
  filterp=filterp->next;
 }

 return(filterp);
}


static void listfilters(void)
{
 filterstr * filterp;

 filterp=firstfilter;

 while(filterp)
 {
  printf("&%08x    %s\n",filterp->taskhandle,filterp->data->name);

  filterp=filterp->next;
 }
}


static void addfilter(int taskhandle,taskdatastr * taskp)
{
 filterstr * filterp;

 filterp=malloc(sizeof(filterstr));
 if(filterp)
 {
  filterp->next=firstfilter;
  firstfilter=filterp;

  filterp->taskhandle=taskhandle;
  filterp->data=taskp;
 }
}


static void remfilter(int taskhandle)
{
 filterstr * filterp;
 filterstr * prev;

 prev=NULL;
 filterp=firstfilter;

 while(filterp)
 {
  if(filterp->taskhandle==taskhandle) break;
  prev=filterp;
  filterp=filterp->next;
 }

 if(filterp)
 {
  if(prev) prev->next=filterp->next;
  else     firstfilter=filterp->next;

  free(filterp);
 }
}





static void remfiltertask(taskdatastr * taskp)
{
 filterstr * filterp;
 filterstr * prev;
 filterstr * next;

 prev=NULL;
 filterp=firstfilter;

 while(filterp)
 {
  while(filterp)
  {
   if(filterp->data==taskp) break;
   prev=filterp;
   filterp=filterp->next;
  }

  if(filterp)
  {
   if(prev) prev->next=filterp->next;
   else     firstfilter=filterp->next;

   next=filterp->next;

   free(filterp);

   filterp=next;
  }
 }
}







typedef struct taskliststr
{
 int    taskhandle;
 char * taskname;
 int    memory;
 int    flags;


} taskliststr;



static void addfiltersfortask(taskdatastr * taskp)
{
 _kernel_swi_regs  rx;
 _kernel_oserror * err;
 taskliststr       tasklist;
 taskliststr     * limit;
 taskliststr     * p;
 filterstr       * filterp;

 rx.r[0]=0;


 while(1)
 {
  rx.r[1]=(int)&tasklist;
  rx.r[2]=sizeof(tasklist);

  err=_kernel_swi(TaskManager_EnumerateTasks,&rx,&rx);
  if(err) break;

  limit=(taskliststr*)rx.r[1];
  p=&tasklist;

  while(p<limit)
  {
   if(!xcstrcmp(p->taskname,taskp->name))
   {
    filterp=findfilter(p->taskhandle);
    if(!filterp) addfilter(p->taskhandle,taskp);
   }

   p++;
  }

  if(rx.r[0]<0) break;
 }

}



static _kernel_oserror * addcommand(char * args[],int argc)
{
 int           flags;
 char        * p;
 int           ch;
 int           not;
 taskdatastr * taskp;

 flags=defaultflags;
 not=0;
 taskp=NULL;

 if(argc==2)
 {
  taskp=findtask(args[1]);
  if(taskp) flags=taskp->flags;
  else
  {
   taskp=addtask(args[1],flags);
   if(taskp) addfiltersfortask(taskp);
  }
 }

 p=args[0];
 while((ch=*p++)!=0)
 {
  if(ch=='~') not=1;
  else
  {
   ch=toupper(ch);
   if(ch=='B')
   {
    if(not) flags&=~BLENDBIT;
    else    flags|=BLENDBIT;
   }
   else
   if(ch=='L')
   {
    if(not) flags&=~LOWBIT;
    else    flags|=LOWBIT;
   }
   else
   if(ch=='G')
   {
    if(not) flags&=~GDRAWBIT;
    else    flags|=GDRAWBIT;
   }
   not=0;
  }
 }

 if(taskp)
 {
  taskp->flags=flags;
 }
 else
 {
  defaultflags=flags;
 }

 return(NULL);
}





static _kernel_oserror * remcommand(char * args[],int argc)
{
 taskdatastr * taskp;
 taskdatastr * prev;

 prev=NULL;
 taskp=firsttask;

 while(taskp)
 {
  if(xcstrcmp(taskp->name,args[0])==0) break;
  prev=taskp;
  taskp=taskp->next;
 }

 if(taskp)
 {
  remfiltertask(taskp);

  if(prev) prev->next=taskp->next;
  else     firsttask=taskp->next;

  free(taskp);
 }

 return(NULL);
 USE(argc);
}







/* we're entering a task */

_kernel_oserror * postfilter_handler(_kernel_swi_regs *r, void *pw)
{
 filterstr       * filterp;
 int             * p;
 taskdatastr     * taskp;
 int               currenttask;
 _kernel_swi_regs  rx;


 rx.r[0]=5;
 _kernel_swi(Wimp_ReadSysInfo,&rx,&rx);
 currenttask=rx.r[0];


 if(r->r[0]==17 || r->r[0]==18)
 {
  p=(int*)r->r[1];

  if(p[4]==Message_TaskInitialise)
  {
   taskp=findtask((char*)(p+7));
   if(taskp)
   {
    filterp=findfilter(p[1]);
    if(!filterp) addfilter(p[1],taskp);
   }
  }
  else
  if(p[4]==Message_TaskCloseDown)
  {
   remfilter(p[1]);
  }
 }

 filterp=findfilter(currenttask);
 if(filterp)
 {
  setfixmode(filterp->data->flags);
 }

 return(NULL);
 USE(pw);
}


/* we're leaving a task */


_kernel_oserror * prefilter_handler(_kernel_swi_regs *r, void *pw)
{

 /* so set up default flags */

 setfixmode(defaultflags);


 return(NULL);
 USE(r);
 USE(pw);
}






static char * filtername="SpecialFX";


static _kernel_oserror * xregister(void *private_word)
{
 _kernel_oserror * err;
 _kernel_swi_regs  rx;


 rx.r[0]=(int)filtername;
 rx.r[1]=(int)postfilter_entry;
 rx.r[2]=(int)private_word;
 rx.r[3]=0; /* all tasks */
 rx.r[4]=0;

 err=_kernel_swi(Filter_RegisterPostFilter,&rx,&rx);

 if(!err)
 {
  rx.r[0]=(int)filtername;
  rx.r[1]=(int)prefilter_entry;
  rx.r[2]=(int)private_word;
  rx.r[3]=0; /* all tasks */
  rx.r[4]=0;
 
  err=_kernel_swi(Filter_RegisterPreFilter,&rx,&rx);
 }

 return(err);
}




static _kernel_oserror * deregister(void *private_word)
{
 _kernel_oserror * err;
 _kernel_swi_regs  rx;

 
 rx.r[0]=(int)filtername;
 rx.r[1]=(int)postfilter_entry;
 rx.r[2]=(int)private_word;
 rx.r[3]=0;  /* all tasks */
 rx.r[4]=0;

 err=_kernel_swi(Filter_DeRegisterPostFilter,&rx,&rx);

 if(!err)
 {
  rx.r[0]=(int)filtername;
  rx.r[1]=(int)prefilter_entry;
  rx.r[2]=(int)private_word;
  rx.r[3]=0; /* all tasks */
  rx.r[4]=0;

  err=_kernel_swi(Filter_DeRegisterPreFilter,&rx,&rx);
 }

 return(err);
}







_kernel_oserror * SpecialFXFinalise(int fatal, int podule,void * pw)
{

 relvectors();

 deregister(pw);

 return(NULL);

 USE(fatal);
 USE(podule);
 USE(pw);
}



typedef struct /* Format of a sprite header */
{
  int  next;      /*  Offset to next sprite                */
  char name[12];  /*  Sprite name                          */
  int  width;     /*  Width in words-1      (0..639)       */
  int  height;    /*  Height in scanlines-1 (0..255/511)   */
  int  lbit;      /*  First bit used (left end of row)     */
  int  rbit;      /*  Last bit used (right end of row)     */
  int  image;     /*  Offset to sprite image               */
  int  mask;      /*  Offset to transparency mask          */

  union
  {
   int mode;      /*  Mode sprite was defined in           */
   struct
   {
    unsigned int new:1;
    unsigned int xdpi:13;
    unsigned int ydpi:13;
    unsigned int type:5;
   } type;
  } selector;
} newsprite_header;



static void fixspriteservice2(_kernel_swi_regs * r)
{
 newsprite_header * sp;

 if(r->r[4]==0)  fixspriteservice(0);   // going to screen turn on
 else
 if(r->r[2]==61) fixspriteservice(1);   // going to mask turn off
 else                                   // going to a sprite
 {
  sp=(newsprite_header*)r->r[4];

  if(sp->selector.type.type==5 || sp->selector.type.type==6) // 16 or 32 bpp sprite turn on
  {
   fixspriteservice(0);
  }
  else
  {
   fixspriteservice(1);                                       // other bpp turn off
  }
 }
}






void SpecialFXService(int service_number,_kernel_swi_regs *r,
                                                  void *private_word)
{
 if(service_number==0x41) fixprintservice(r->r[2]);
 if(service_number==0x72) fixspriteservice2(r);

 USE(private_word);
 USE(r);
}








#define MAXARG 8
#define CLEN   256



_kernel_oserror *SpecialFXCommand(const char *arg_string,int argc,
                                      int cmd_no,void *private_word)
{
 _kernel_oserror   * err;
 char              * argv[MAXARG];
 char                cmd[CLEN];
 char              * p;
 int                 inquotes;
 char              * s;
 char              * e;


 err=NULL;

 xstrcpy(cmd,arg_string);

 argc=0;
 p=cmd;
 inquotes=0;

 while(1)
 {
  while(*p==' ') p++;
  if(*p==0) break;
  s=p;

  while(1)
  {
   if(*p=='\"')
   {
    inquotes^=1;
   }

   if((!inquotes && *p==' ') || *p==0)
   {
    e=p;
    if(*p!=0) p++;

    if(*s=='\"') s++;
    if(e>s && *(e-1)=='\"') e--;
    *e=0;
    argv[argc++]=s;

    break;
   }
   p++;
  }
 }

 switch(cmd_no)
 {
   case 0:
          err=addcommand(argv,argc);
          break;

   case 1:
          err=remcommand(argv,argc);
          break;

   case 2:
          listtasks();
          break;

   case 3:
          listfilters();
          break;

 }

 return(err);

 USE(private_word);
}



_kernel_oserror * SpecialFXInitialise(const char *cmd_tail,int podule_base,
                                                   void *private_word)
{
 _kernel_oserror * err;

 err=NULL;

 xprivate_word=private_word;

 claimvectors();
 err=xregister(private_word);
 if(!err)
 {
  SpecialFXCommand(cmd_tail,0,0,private_word);
 }

 return(err);

 USE(podule_base);
}

