/*->c.trace */
/* Tracer       (c) D. J. Pilling,  December 1990                     */
/*                         Main Section Code                          */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>
#include <locale.h>
#include <math.h>


#include "h.os"
#include "h.wimp"
#include "h.sprite"
#include "h.wimpt"
#include "h.bbc"
#include "h.akbd"
#include "h.werr"
#include "h.flex"

#include "h.DrawLevel0"


#include "h.wos"
#include "h.ram"
#include "h.main"
#include "h.DrawLib"
#include "h.pic"
#include "h.mym"
#include "h.pic"
#include "h.fit"
#include "h.err"

#include "h.mytrace"

#include "h.file"


/****************************************************************************/


Draw_diag spritediag;
Draw_diag drawdiag;
Draw_diag lastdiag;

int       windowsopen;

zoomer    zoom={1,1,0};


void opentrace(void)
{
 int shandle=createwindow(SPRITEW);
 int dhandle=createwindow(DRAWW);

 extent(shandle,0,-esposy(),esposx(),0);
 extent(dhandle,0,-esposy(),esposx(),0);

 open(dhandle,200,300,800,900,0,0,-1);
 open(shandle,0,400,600,1000,0,0,-1);

 windowsopen=1;
}



void closetrace(void)
{
 windowsopen=0;

 closedownt(SPRITEW);
 closedownt(DRAWW);
}



void redrawfunction(Draw_diag diag,int second,Draw_diag diag2)
{
 int more;
 wimp_redrawstr redrawstr;
 wimp_box box;

 redrawstr.w=ewindow;

 wimp_redraw_wind(&redrawstr,&more);
 while(more)
 {
  box.x0=0;
  box.x1=(sposx*zoom.mul)/zoom.div;
  box.y1=0;
  box.y0=(sposy*zoom.mul)/zoom.div;

  picrend(&box,diag,&redrawstr); 

  if(second) picrend(&box,diag2,&redrawstr);
  wimp_get_rectangle(&redrawstr,&more);
 }
}



void updatefunction(Draw_diag diag)
{
 int more;
 wimp_redrawstr redrawstr;
 wimp_box box;
 int handle;

 handle=whandle[DRAWW];

 getw(handle);

 redrawstr.w=handle;
 redrawstr.box.x0=x0-bx;
 redrawstr.box.x1=x1-bx;
 redrawstr.box.y0=y0-by; 
 redrawstr.box.y1=y1-by;   

 wimp_update_wind(&redrawstr,&more);
 while(more)
 {
  box.x0=0;
  box.x1=(sposx*zoom.mul)/zoom.div;
  box.y1=0;
  box.y0=(sposy*zoom.mul)/zoom.div;

  picrend(&box,diag,&redrawstr); 

  wimp_get_rectangle(&redrawstr,&more);
 }
}




void redrawsprite(void)
{
 redrawfunction(spritediag,0,spritediag);
}



void redrawdraw(void)
{
 redrawfunction(drawdiag,1,lastdiag);
}


/*****************************************************************************/

int esposx(void)
{
 if(!zoom.var) return((sposx*zoom.mul)/zoom.div);
 else          return(sposx);
}


int esposy(void)
{
 if(!zoom.var) return((sposy*zoom.mul)/zoom.div);
 else          return(sposy);
}


void newzoom(int mul,int div)
{
 if(zoom.mul*div!=zoom.div*mul)
 {
  zoom.mul=mul;
  zoom.div=div;
  refreshwindow(whandle[DRAWW]);
  refreshwindow(whandle[SPRITEW]);
 }
}


void varzoomlo(void)
{
 int xmul;
 int xdiv;
 int ymul;
 int ydiv;


 getw(whandle[DRAWW]);

 xdiv=sposx>>4;
 ydiv=sposy>>4;
 xmul=(x1-x0)>>4;
 ymul=(y1-y0)>>4;

 if(xmul>xdiv) xmul=xdiv;
 if(ymul>ydiv) ymul=ydiv;

 /* problem is that wimp will round up window extent to next biggest pixel */
 /* so xmul can be bigger than the extent xdiv */


 if(xmul*ydiv>ymul*xdiv) newzoom(xmul,xdiv);
 else                    newzoom(ymul,ydiv);
}




void zoomfn(void)
{
 int handle;

 getw(handle=whandle[DRAWW]);
 extent(handle,0,-(sposy*zoom.mul)/zoom.div,(sposx*zoom.mul)/zoom.div,0);
 open(handle,x0,y0,x1,y1,scx,scy,bhandle);
 refreshwindow(handle);

 getw(handle=whandle[SPRITEW]);
 extent(handle,0,-(sposy*zoom.mul)/zoom.div,(sposx*zoom.mul)/zoom.div,0);
 open(handle,x0,y0,x1,y1,scx,scy,bhandle);
 refreshwindow(handle);
}





void forcespritetodraw(void)
{
 int width;
 int height;
 int handle;
 int dw;
 int dh;

 getw(whandle[DRAWW]);

 width=x1-x0;
 height=y1-y0;

 getw(handle=whandle[SPRITEW]);
 open(handle,x0,y1-height,x0+width,y1,scx,scy,bhandle);

 getw(handle);

 if(width>(x1-x0))  dw=width-(x1-x0);
 else               dw=0;

 if(height>(y1-y0)) dh=height-(y1-y0);
 else               dh=0;

 if(dw || dh) open(handle,x0-dw,y1-height,x0+width,y1-dh,scx,scy,bhandle);
}



void forcedrawtosprite(void)
{
 int width;
 int height;
 int handle;
 int dw;
 int dh;

 getw(whandle[SPRITEW]);

 width=x1-x0;
 height=y1-y0;

 getw(handle=whandle[DRAWW]);
 open(handle,x0,y1-height,x0+width,y1,scx,scy,bhandle);

 getw(handle);

 dw=width-(x1-x0);
 dh=height-(y1-y0);

 if(dw || dh) open(handle,x0-dw,y1-height,x0+width,y1-dh,scx,scy,bhandle);
}




void vzoomf(void)
{
 int handle;

 getw(handle=whandle[DRAWW]);
 extent(handle,0,-esposy(),esposx(),0);
 open(handle,x0,y0,x1,y1,scx,scy,bhandle);

 getw(handle=whandle[SPRITEW]);
 extent(handle,0,-esposy(),esposx(),0);
 open(handle,x0,y0,x1,y1,scx,scy,bhandle);

 forcespritetodraw();

 varzoomlo();
}






void openwindows(void)
{
 wimp_open_wind(&(wimpevent.data.o));
 if(zoom.var)
 {
  if(ewindow==whandle[DRAWW])   forcespritetodraw();
  else
  if(ewindow==whandle[SPRITEW]) forcedrawtosprite();
  varzoomlo();
 }
}


/****************************************************************************/

char * spseenmask=NULL;


os_error * initseenmask(void)
{
 int seensize;
 int * i;
 os_error * err;

 seensize=(spx*spy);
 seensize=(seensize>>3)+((seensize & 7)>0);
 seensize=(seensize>>2)+((seensize & 3)>0);

 err=flex_alloce((flex_ptr)&spseenmask,seensize*4);

 if(!err)
 {
  i=(int *)spseenmask;
  while(seensize--) *i++=0;
 }

 return(err);
}


void trashseenmask(void)
{
 flex_free((flex_ptr)&spseenmask);
}


int testseenmask(int x,int y)
{
 int bit=x+y*spx;
 int byte=*(spseenmask+(bit>>3));
 bit=bit & 0x7;
 return((byte>>bit) & 0x1);
}


void setseenmask(int x,int y)
{
 int     bit=x+y*spx;
 char *  byte=(spseenmask+(bit>>3));
 bit=bit & 0x7;
 *byte|=(0x1 << bit);
}


int pixelmxys(int x,int y)
{            
 if(x<0 || x>=spx || y<0 || y>=spy) return(-1);
 if(testseenmask(x,y)) return(-1);
 return(pixelxy(x,y));
}

int pixelmxy(int x,int y)
{
 int code;

/* dprintf(5,"before");  */
 code=pixelmxys(x,y);
/* dprintf(5,"code=%d",code);  */

 return(code);
}



/*****************************************************************************/


#define INITPSIZE 2560
#define STEPPSIZE 2560



ipoint2 * pointstore=NULL;
int       npoints;
int       maxpoints;


os_error * initpointstore(void)
{
 os_error * err;

 err=flex_alloce((flex_ptr)&pointstore,INITPSIZE);
 if(!err)
 {
  npoints=0;
  maxpoints=INITPSIZE/(sizeof(ipoint2))-2;
 }

 return(err);
}


void clearpointstore(void)
{
 npoints=0;
}



void trashpointstore(void)
{
 flex_free((flex_ptr)&pointstore);
}



os_error * addpoint(int x,int y)
{
 os_error * err;

 err=NULL;

 pointstore[npoints].x=x;
 pointstore[npoints].y=y;
 npoints++;

 if(npoints>=maxpoints)
 {
  maxpoints+=STEPPSIZE/sizeof(ipoint2);
  err=flex_extende((flex_ptr)&pointstore,maxpoints*sizeof(ipoint2));
 } 

 return(err);
}





/*****************************************************************************/

typedef struct stackitem
{
 int x;
 int y;
 int llim;
 int rlim;
 int dir;
} stackitem;

#define STACKLIM 1024


stackitem * stack;
stackitem * stkptr;
stackitem * stklt;


void popstack(stackitem * item)
{
 if(stkptr==stack)
 {
  item->x=item->y=-1;
 }
 else
 {
  stkptr--;
  *item=*stkptr;
 }
}



os_error * pushstack(int x,int y,int * stackx,int startx,int dir)
{
 os_error * err;

 err=NULL;

 if(stkptr==stklt)
 {
  err=flex_extende((flex_ptr)&stack,(stklt-stack+STACKLIM)*sizeof(stackitem));
  if(!err)
  {
   stklt+=STACKLIM;
  }
 }

 if(!err)
 {
  stkptr->x=*stackx;
  stkptr->y=y;
  stkptr->llim=startx;
  stkptr->rlim=x;
  stkptr->dir=dir;
  stkptr++;
  *stackx=-1;
 }

 return(err);
}


os_error * checkgap(int x,int y,int * stackx,int startx,int target,int dir)
{
 os_error * err;

 err=NULL;

 if(*stackx==-1)
 {
/*  if(pixelmxy(x+1,y)==target) *stackx=x;  */
  if(pixelmxy(x,y)==target)   *stackx=x;
/*  if(pixelmxy(x-1,y)==target) *stackx=x;  */
 }
 else
 {
  if(pixelmxy(x,y)!=target) err=pushstack(x,y,stackx,startx,dir);
 }

 return(err);
}


os_error * fillline(stackitem * item,int target)
{
 int startup=-1;
 int startdown=-1;
 int startx;
 int x=item->x;
 int y=item->y;
 int dir=item->dir;
 int llim=item->llim;
 int rlim=item->rlim;
 os_error * err;

 while(pixelmxy(x,y)==target) x--;
 x++;
 startx=x;

 err=NULL;

 while(pixelmxy(x,y)==target)
 {
  setseenmask(x,y);

  if(dir & 0x2)      /* going down */
  {
   err=checkgap(x,y-1,&startdown,startx,target,2);
   if(err) break;
   if(!(x>=llim && x<=rlim))
   {
    checkgap(x,y+1,&startup,startx,target,1);
    if(err) break;
   }
  }

  if(dir & 0x1)      /* going up */
  {
   err=checkgap(x,y+1,&startup,startx,target,1);
   if(err) break;
   if(!(x>=llim && x<=rlim))
   {
    checkgap(x,y-1,&startdown,startx,target,2);
    if(err) break;
   }
  }
  x++;
 }

 if(!err)
 {
  if(startup!=-1)   err=pushstack(x /* -1 */,y+1,&startup,startx,1);
  if(!err)
  {
   if(startdown!=-1) err=pushstack(x /* -1 */,y-1,&startdown,startx,2);
  }
 }

 return(err);
}



/* return 1 if fails, else 0 */

os_error * fillarea(int x,int y,int target)
{
 stackitem  item;
 os_error * err;

 err=flex_alloce((flex_ptr)&stack,STACKLIM*sizeof(stackitem));
 if(!err)
 {
  stkptr=stack;
  stklt=stack+STACKLIM;

  item.x=x;
  item.y=y;                                                                       item.llim=-1;
  item.rlim=-1;                                                                   item.dir=3;

  do
  {
   err=fillline(&item,target);
   if(err) break;
   popstack(&item);
  } while((!(item.x==-1 && item.y==-1)));
  
  flex_free((flex_ptr)&stack);
 }

 return(err);
}


/*****************************************************************************/


void getoutlinebig(int x,int y,int target)
{
 int i;
 int dir;
 int sx;
 int sy;
 int dx;
 int dy;

 dir=0;
 sx=x;
 sy=y;

 do
 {
  i=dir;
  do
  {
   switch(i & 0x7)
   {
    case 0:
           dx=-1;
           dy=1;
           break;

    case 1:
           dx=0;
           dy=1;
           break;

    case 2:
           dx=1;
           dy=1;
           break;

    case 3:
           dx=1;
           dy=0;
           break;

    case 4:
           dx=1;
           dy=-1;
           break;

    case 5:
           dx=0;
           dy=-1;
           break;

    case 6:
           dx=-1;
           dy=-1;
           break;

    case 7:
           dx=-1;
           dy=0;
           break;
   }

   if(pixelmxy(x+dx,y+dy)!=-1) {i=i & 0x7;break;}
   i++;

  }while(!(i>(dir+7)));

  if(i>(dir+7))
  {
   dx=dy=0;
  }

  x+=dx;
  y+=dy;

  addpoint(x*256*spdx,y*256*spdy);

  dir=(i+5) & 0x7;

 }while(!(x==sx && y==sy));
}





os_error * getoutline(int x,int y,int target)
{
 int        i;
 int        dir;
 int        sx;
 int        sy;
 int        dx;
 int        dy;
 os_error * err;

 dir=0;
 sx=x;
 sy=y;
 err=NULL;

 do
 {
  i=dir;
  do
  {
   switch(i & 0x7)
   {
    case 0:
           dx=-1;
           dy=1;
           break;

    case 1:
           dx=0;
           dy=1;
           break;

    case 2:
           dx=1;
           dy=1;
           break;

    case 3:
           dx=1;
           dy=0;
           break;

    case 4:
           dx=1;
           dy=-1;
           break;

    case 5:
           dx=0;
           dy=-1;
           break;

    case 6:
           dx=-1;
           dy=-1;
           break;

    case 7:
           dx=-1;
           dy=0;
           break;
   }

   if(pixelmxy(x+dx,y+dy)==target) {i=i & 0x7;break;}
   i++;

  }while(!(i>(dir+7)));

  if(i>(dir+7))
  {
   dx=dy=0;
  }

  x+=dx;
  y+=dy;

  err=addpoint(x*256*spdx,y*256*spdy);
  if(err) break;

  dir=(i+5) & 0x7;

 }while(!(x==sx && y==sy));

 return(err);
}





int pointcmp(const void * p1,const void * p2)
{
 if((((ipoint2*)p1)->y)==(((ipoint2*)p2)->y))
   return((((ipoint2*)p1)->x)-(((ipoint2*)p2)->x));
 else
   return((((ipoint2*)p1)->y)-(((ipoint2*)p2)->y));
}



/* fill area using outline */

void fillareax(int target)
{
 int i;
 int xs;
 int ys;
 int x;
 int y;

 /* sort points by y then x */

 qsort(pointstore,npoints,sizeof(ipoint2),pointcmp);

 i=0;

 while(i<npoints)
 {
  y=pointstore[i].y;
  ys=y/(256*spdy);

  while((i<npoints) && (pointstore[i].y==y))
  {
   /* got one point */

   xs=pointstore[i].x/(256*spdx);

/*printf("xs=%d ys=%d target=%d mask=%d\n",xs,ys,target,pixelmxy(xs,ys));*/

   x=xs+1;
   while(pixelmxy(x,ys)==target)
   {
    setseenmask(x,ys);
    x++;
   }

   x=xs-1;
   while(pixelmxy(x,ys)==target)
   {
    setseenmask(x,ys);
    x--;
   }

   if(pixelmxy(xs,ys)==target) setseenmask(xs,ys);

   i++;
  }
 }
}


static int nextcol;
static int xcoltab[4]=
{
 0xFF000000,
   0xFF0000,
     0xFF00,
        0x0,
};




int spbackground=0;

os_error * fitoutline(void)
{
 int x;
 int y;
 int target;
 int i;
 os_error * err;
 unsigned int c1;

 err=NULL;

 for(y=spy-1;y>=0;y--)
 {
  for(x=0;x<spx;x++)
  {
   if(!testseenmask(x,y))
   {
    target=pixelmxy(x,y);
    if(target!=-1)
    {
     clearpointstore();
     err=addpoint(x*256*spdx,y*256*spdy);
     if(err) break;

     err=getoutline(x,y,target);
     if(err) break;

     if(npoints>4)
     {
      c1=paltab[target];   /* somewhat amazingly OK for 256 colour sprites */

      if(spbpp==3 && (palentries==64 || palentries==16))
      {
       if(palentries==64) c1=paltab[(target & 0xF)+48];
       else               c1=paltab[(target & 0xF)];

       if(target & 0x10) c1|=0x8000;
       else              c1&=~0x8000;

       if(target & 0x20) c1|=0x400000;
       else              c1&=~0x400000;

       if(target & 0x40) c1|=0x800000;
       else              c1&=~0x800000;

       if(target & 0x80) c1|=0x80000000;
       else              c1&=~0x80000000;

       c1&=0xF0F0F000;
       c1|=(c1>>4);
      }


      veclinecolour(c1 & 0xFFFFFF00,&lastdiag);
      vecfillcolour(c1 & 0xFFFFFF00,&lastdiag); 
//      veclinecolour(xcoltab[(nextcol++) & 0x3],&lastdiag);
//      vecfillcolour(-1,&lastdiag);

      err=fitcurve(0,npoints);
      if(!err)
      {
       closediag(&lastdiag);
       updatefunction(lastdiag);
       err=mergediag(&lastdiag,&drawdiag);
       if(!err) opendiag(&lastdiag,spdrx,spdry);
      }
      if(bbc_inkey(0)==27) /* ESCAPE-hope nothing should follow this */
      {
       err=&err_escape;
       break;
      }
     }

     if(!err)
     {
      for(i=0;i<npoints;i++)
      {
       err=fillarea(pointstore[i].x/(256*spdx),
                                  pointstore[i].y/(256*spdy),target);
       if(err) break;
      }
     }
    }
   } 
   if(err) break;
  }
  if(err) break;
 }

 return(err);
}



os_error * traceoutline(void)
{
 int        x;
 int        y;
 int        target;
 int        i;
 os_error * err;
 unsigned int c1;

 err=NULL;

 for(y=spy-1;y>=0;y--)
 {
  for(x=0;x<spx;x++)
  {
   if(!testseenmask(x,y))
   {
    target=pixelmxy(x,y);

    clearpointstore();

    err=addpoint(x*256*spdx,y*256*spdy);
    if(err) break;

    err=getoutline(x,y,target);
    if(err) break;

    if(npoints>1)
    {
     c1=paltab[target];

     if(spbpp==3 && palentries==64)
     {
      c1=paltab[(target & 0xF)+48];

      if(target & 0x10) c1|=0x8000;
      else              c1&=~0x8000;

      if(target & 0x20) c1|=0x400000;
      else              c1&=~0x400000;

      if(target & 0x40) c1|=0x800000;
      else              c1&=~0x800000;

      if(target & 0x80) c1|=0x80000000;
      else              c1&=~0x80000000;

      c1&=0xF0F0F000;
      c1|=(c1>>4);
     }

     veclinecolour(c1 & 0xFFFFFF00,&drawdiag);
     vecfillcolour(c1 & 0xFFFFFF00,&drawdiag);

     err=vecmove(pointstore[0].x,pointstore[0].y,&drawdiag);

     if(!err)
     {
      for(i=1;i<npoints;i++)
      {
       err=vecdraw(pointstore[i].x,pointstore[i].y,&drawdiag);
       if(err) break;
      }
     }

     closedraw(&drawdiag);
    }

    if(!err)
    {
     for(i=0;i<npoints;i++)
     {
      err=fillarea(pointstore[i].x/(256*spdx),
                                     pointstore[i].y/(256*spdy),target);
      if(err) break;
     }
    }
   }
   if(err) break;
  }
  if(err) break;
 }

 return(err);
}








/* actually does trace */


void dotrace(void)
{
 os_error * err;

 if(!spritediag.data || !spritediag.length) return;

 cleardiag(&drawdiag);
 cleardiag(&lastdiag);

 err=initseenmask();
 if(!err)
 {
  err=initpointstore();
  if(!err)
  {
   err=opendiag(&drawdiag,spdrx,spdry);
   if(!err)
   {
    err=opendiag(&lastdiag,spdrx,spdry);
    if(!err)
    {
     err=traceoutline();
    }
    closediag(&drawdiag);
   }
   trashpointstore();
  }
  trashseenmask();
 }

 if(err)
 {
  cleardiag(&lastdiag);
  cleardiag(&drawdiag);
 }
 report(err);

 refreshwindow(whandle[DRAWW]);
}



void dofit(void)
{
 os_error * err;

 if(!spritediag.data || !spritediag.length) return;

 hourglasson();

 cleardiag(&drawdiag);
 cleardiag(&lastdiag);

 err=initseenmask();

 if(!err)
 {
  err=initpointstore();

  if(!err)
  {
   err=opendiag(&drawdiag,spdrx,spdry);
   if(!err)
   {
    if(!err)
    {
     err=opendiag(&lastdiag,spdrx,spdry);
     if(!err) 
     {
      err=fitoutline();
     }
     closediag(&drawdiag);
    }
   }
   trashpointstore();
  }
  trashseenmask();
 }
 hourglassoff();

 if(err)
 {
  cleardiag(&lastdiag);
  cleardiag(&drawdiag);
  refreshwindow(whandle[DRAWW]);
 }

 report(err);
}




void mainicon(void)
{
 if(buttons==2) popmain();
}



/****************************************************************************/

char   drawpath[256];
char   spritepath[256];

/* passed path, should be to ...Sprite, need ...Draw at same level */


int loadbatch(char * name)
{
 char * p;
 char   string[256];

 strcpy(spritepath,name);
 strcpy(drawpath,name);
 p=leaf(drawpath);
 strcpy(p,"Draw");

 sprintf(string,"CDIR %s",drawpath);

 oscli(string);

 addzeroevent(BATCH);

 return(1);
}



void dobatch(void)
{
 fstat fs;
 char  string1[256];
 char  string2[256];

 remzeroevent(BATCH);

 startscan();

 while(nextitem(spritepath,&fs,NULL))
 {
  sprintf(string1,"%s.%s",spritepath,fs.name);
  sprintf(string2,"%s.%s",drawpath,fs.name);

  if(fs.type==SPRITE)
  {
   loadspritefile(string1);
   dofit();
   saveasdraw(string2);
  }
 }
}

