/* Spreadsheet functions for SSS */
/* Tue,30 Jul 1991 */
/* Copyright C.T.Stretch 1991 */

#include "ssshdr.h"

#define YESNULL 0

entry **sheet;
wimp_w swind;
int dispwidth;
int nrows,ncols,width[NCOLS];
int sx0=0,sx1=0,sy0=0,sy1=0;
int ex0,ex1,ey0,ey1;
int bx0=0,by1=LINEHT,bw=INITWIDTH*CHWIDTH,bh=LINEHT;
int ebx0=0,eby1=LINEHT,ebw=INITWIDTH*CHWIDTH,ebh=LINEHT;
char buf[BUFLEN+16],fname[BUFLEN];
int uy;
BOOL autox,changed,fixed;
int prwidth=80;
BOOL repfirst=FALSE;
BOOL mono=FALSE;
char sep[4]=",";
static int tempsize;

void sheet_change()
{ uy=0;changed=FALSE;
  event_setmask(YESNULL);
}

void sheet_box()
{ int x,bx,by,more;
  int cx0=bx0,cw=bw,cy1=by1,ch=bh;
  wimp_redrawstr r[1];
  r->w=swind;
  r->box.x0=0;r->box.y1=0;
  r->box.x1=dispwidth;r->box.y0=-LINEHT*nrows;
  bx0=0;bw=0;
  for(x=0;x<NCOLS;x++)
  { if(x>=sx0) break;
    bx0+=width[x]*CHWIDTH;
  }
  for(;x<NCOLS;x++)
  { bw+=width[x]*CHWIDTH;
    if(x>=sx1) break;
  }
  by1=(sy1+1)*LINEHT;
  bh=(sy1-sy0+1)*LINEHT;
  wimpt_noerr(wimp_update_wind(r,&more));
  bx=r->box.x0-r->scx;
  by=r->box.y1-r->scy;
  while(more)
  { wimp_setcolour(15+16*3);
    bbc_rectanglefill(bx+bx0,by-by1,bw,bh);
    bbc_rectanglefill(bx+bx0+8,by-by1+8,bw-16,bh-16);
    bbc_rectanglefill(bx+cx0,by-cy1,cw,ch);
    bbc_rectanglefill(bx+cx0+8,by-cy1+8,cw-16,ch-16);
    wimp_get_rectangle(r,&more);
  }
}

void sheet_ebox()
{ int x,bx,by,more;
  int cx0=ebx0,cw=ebw,cy1=eby1,ch=ebh;
  wimp_redrawstr r[1];
  r->w=swind;
  r->box.x0=0;r->box.y1=0;
  r->box.x1=dispwidth;r->box.y0=-LINEHT*nrows;
  ebx0=0;ebw=0;
  for(x=0;x<NCOLS;x++)
  { if(x>=ex0) break;
    ebx0+=width[x]*CHWIDTH;
  }
  for(;x<NCOLS;x++)
  { ebw+=width[x]*CHWIDTH;
    if(x>=ex1) break;
  }
  eby1=(ey1+1)*LINEHT;
  ebh=(ey1-ey0+1)*LINEHT;
  wimpt_noerr(wimp_update_wind(r,&more));
  bx=r->box.x0-r->scx;
  by=r->box.y1-r->scy;
  while(more)
  { wimp_setcolour(12+16*3);
    bbc_rectanglefill(bx+ebx0,by-eby1,ebw,ebh);
    bbc_rectanglefill(bx+cx0,by-cy1,cw,ch);
    wimp_get_rectangle(r,&more);
  }
}

void sheet_home()
{ ex0=ex1=0;
  ey0=ey1=0;
  sx0=sy0=0;
  sx1=sy1=0;
  sheet_box();
  sheet_ebox();
}

BOOL sheet_init()
{ int i;
  nrows=INITNROWS;
  ncols=INITNCOLS;
  if(!flex_alloc((flex_ptr)&sheet,4*NCOLS*nrows)) return FALSE;
  for(i=0;i<nrows*NCOLS;i++) sheet[i]=0;
  for(i=0;i<NCOLS;i++) width[i]=INITWIDTH;
  strcpy(fname,"Sheet");
  return TRUE;
}

void sheet_clear(int nr)
{ int i;
  visdelay_begin();
  for(i=0;i<nrows*NCOLS;i++) if(sheet[i]) flex_free((flex_ptr)(sheet+i));
  nrows=nr;
  if(!flex_extend((flex_ptr)&sheet,4*NCOLS*nrows)) werr(1,"No room");
  for(i=0;i<nrows*NCOLS;i++) sheet[i]=0;
  for(i=0;i<NCOLS;i++) width[i]=INITWIDTH;
  visdelay_end();
}

void sheet_getentry(int x,int y,int t)
{ entry **cb=sheet+x+y*NCOLS;
  int len=strlen(buf)+13;
  for(expr=buf;*expr==' ';expr++);
  if(!(*expr))
  { if(*cb) flex_free((flex_ptr)cb);
    *cb=0;
    return;
  }
  if(!((*cb)?flex_extend((flex_ptr)cb,len):flex_alloc((flex_ptr)cb,len)))
     { werr(0,"Out of memory");return;}
  *((int *)(*cb))=t;
  strcpy(&((*cb)->c),buf);
  if(t>=FINT)
  { vx=x;vy=y;expr=buf;
    eval();
    (*cb)->v=value;
    (*cb)->a=(errno!=0);
    (*cb)->p=(perr!=0);
    (*cb)->u=fixed;
  }
}

void sheet_update()
{ entry *cb;
  int ux;
  for(ux=0;ux<ncols;ux++)
  { cb=sheet[ux+uy*NCOLS];
    if(!cb) continue;
    if(cb->u) continue;
    if(cb->t<FINT) continue;
    if(cb->p) continue;
    vx=ux;vy=uy;expr=&(cb->c);
    eval();
    if(cb->v!=value)
    { changed=TRUE;
      cb->v=value;
    }
    if(cb->a!=(errno!=0))
    { changed=TRUE;
      cb->a=(errno!=0);
    }
  }
}

static void clear(int x,int y)
{ entry **ce=sheet+x+y*NCOLS;
  if(*ce) 
  { flex_free((flex_ptr)ce);
    *ce=0;
  }
}

static void copy(int xs,int ys,int xt,int yt)
{ entry **es=sheet+xs+ys*NCOLS,**et=sheet+xt+yt*NCOLS;
  int len;
  if(*es)
  { len=flex_size((flex_ptr)es);
    if(!((*et)?flex_extend((flex_ptr)et,len):flex_alloc((flex_ptr)et,len)))
     { werr(0,"Out of memory");return;}
     memcpy((char*)*et,(char*)*es,len);
  }
  else if(*et)
  { flex_free((flex_ptr)et);
    *et=0;
  }
}

void sheet_newrow(void)
{ int x,y;
  if(!flex_extend((flex_ptr)&sheet,4*NCOLS*(nrows+1))) return;
  for(x=0;x<NCOLS;x++) sheet[x+nrows*NCOLS]=0;
  for(y=nrows;y>ey0;y--) for(x=0;x<ncols;x++) copy(x,y-1,x,y);
  for(x=0;x<ncols;x++) clear(x,ey0);
  nrows++;
  if(autox) sheet_change();
}

void sheet_newcol(void)
{ int x,y;
  if(ncols>=NCOLS) return;
  for(x=ncols;x>ex0;x--) for(y=0;y<nrows;y++) copy(x-1,y,x,y);
  for(y=0;y<nrows;y++) clear(ex0,y);
  ncols++;
  if(autox) sheet_change();
}

void sheet_delrow(void)
{ int x,y,d=ey1-ey0+1;
  visdelay_begin();
  for(x=0;x<ncols;x++)
  { for(y=ey1+1;y<nrows;y++) copy(x,y,x,y-d);
    for(y=nrows-d;y<nrows;y++) clear(x,y);
  }
  nrows-=d;if(nrows<1) nrows=1;
  flex_extend((flex_ptr)&sheet,4*NCOLS*nrows);
  if(autox) sheet_change();
  visdelay_end();
}

void sheet_delcol(void)
{ int x,y,d=ex1-ex0+1;
  visdelay_begin();
  for(y=0;y<nrows;y++)
  { for(x=ex1+1;x<ncols;x++) copy(x,y,x-d,y);
    for(x=ncols-d;x<ncols;x++) clear(x,y);
  }
  ncols-=d;
  if(ncols<1) ncols=1;
  if(autox) sheet_change();
  visdelay_end();
}


void sheet_bcopy(void)
{ int x,y,tx,ty;
  if((ex0==ex1)&&(ey0==ey1))
  { for(y=sy0;y<=sy1;y++) for(x=sx0;x<=sx1;x++)
    if((x+ex0-sx0<ncols)&&(y+ey0-sy0<nrows)) copy(x,y,x+ex0-sx0,y+ey0-sy0);
  }
  else
  { tx=ex0;ty=ey0;
    for(y=sy0;y<=sy1;y++) for(x=sx0;x<=sx1;x++)
    { if(tx>ex1) {tx=ex0;ty++;}
      if(ty>ey1) return;
      copy(x,y,tx,ty);
      tx++;
    }
  }
  if(autox) sheet_change();
}

static int numcmp(const void *s,const void *t)
{ double a,b;
  a=sheet[(*(int*)s)*NCOLS+sx0]->v;
  b=sheet[(*(int*)t)*NCOLS+sx0]->v;
  if(a<b) return -1;
  return (a>b)?1:0;
}

static int textcmp(const void *s,const void *t)
{ char *a,*b;
  a=&(sheet[(*(int*)s)*NCOLS+sx0]->c);
  b=&(sheet[(*(int*)t)*NCOLS+sx0]->c);
  return strcmp(a,b);
}

static int numcmpr(const void *s,const void *t)
{ double a,b;
  a=sheet[(*(int*)s)+sy0*NCOLS]->v;
  b=sheet[(*(int*)t)+sy0*NCOLS]->v;
  if(a<b) return -1;
  return (a>b)?1:0;
}

static int textcmpr(const void *s,const void *t)
{ char *a,*b;
  a=&(sheet[(*(int*)s)+sy0*NCOLS]->c);
  b=&(sheet[(*(int*)t)+sy0*NCOLS]->c);
  return strcmp(a,b);
}

static void temps(int x,int y)
{ entry **e=sheet+x+y*NCOLS;
  if(*e)
  { tempsize=flex_size((flex_ptr)e);
    memcpy(buf,(char*)(*e),tempsize);
  }
  else tempsize=0;
}

static void tempr(int x,int y)
{ entry **e=sheet+x+y*NCOLS;
  if(tempsize)
  { if(flex_extend((flex_ptr)e,tempsize)) memcpy((char*)(*e),buf,tempsize);
  }
  else if(e) flex_free((flex_ptr)e);
}

static void sortcol()
{ int x,y,z,n=sy1-sy0+1;
  int *p,*q;
  entry *e=sheet[sx0+sy0*NCOLS];
  if(!e) return;
  if((sy0!=ey0)||(sy1!=ey1))return;
  if(!flex_alloc((flex_ptr)&p,4*n)) return;
  if(!flex_alloc((flex_ptr)&q,4*n)) return;
  for(y=0;y<n;y++) p[y]=sy0+y;
  if(e->t<FINT)
  { for(y=sy0;y<=sy1;y++)
    { e=sheet[sx0+y*NCOLS];
      if(!e) goto done;
      if(e->t>=FINT) goto done;
    }
    qsort((void*)p,n,4,textcmp);
  }
  else
  { for(y=sy0;y<=sy1;y++)
    { e=sheet[sx0+y*NCOLS];
      if(!e) goto done;
      if((e->t<FINT)||(e->a)||(e->p)) goto done;
    }
    qsort((void*)p,n,4,numcmp);
  }
  for(x=ex0;x<=ex1;x++)
  { for(y=0;y<n;y++) q[y]=TRUE;
    for(y=0;y<n;y++) if((q[y])&&(p[y]!=y+sy0))
    { temps(x,y+sy0);
      for(z=y;p[z]!=y+sy0;z=p[z]-sy0)
      { copy(x,p[z],x,z+sy0);
        q[p[z]-sy0]=FALSE;
      }
      tempr(x,z+sy0);
    }
  }
  done:flex_free((flex_ptr)&p);
  flex_free((flex_ptr)&q);
}

static void sortrow()
{ int x,y,z,n=sx1-sx0+1;
  int *p,*q;
  entry *e=sheet[sx0+sy0*NCOLS];
  if(!e) return;
  if((sx0!=ex0)||(sx1!=ex1))return;
  if(!flex_alloc((flex_ptr)&p,4*n)) return;
  if(!flex_alloc((flex_ptr)&q,4*n)) return;
  for(x=0;x<n;x++) p[x]=sx0+x;
  if(e->t<FINT)
  { for(x=sx0;x<=sx1;x++)
    { e=sheet[x+sy0*NCOLS];
      if(!e) goto done;
      if(e->t>=FINT) goto done;
    }
    qsort((void*)p,n,4,textcmpr);
  }
  else
  { for(x=sx0;x<=sx1;x++)
    { e=sheet[x+sy0*NCOLS];
      if(!e) goto done;
      if((e->t<FINT)||(e->a)||(e->p)) goto done;
    }
    qsort((void*)p,n,4,numcmpr);
  }
  for(y=ey0;y<=ey1;y++)
  { for(x=0;x<n;x++) q[x]=TRUE;
    for(x=0;x<n;x++) if((q[x])&&(p[x]!=x+sx0))
    { temps(x+sx0,y);
      for(z=x;p[z]!=x+sx0;z=p[z]-sx0)
      { copy(p[z],y,z+sx0,y);
        q[p[z]-sx0]=FALSE;
      }
      tempr(z+sx0,y);
    }
  }
  done:flex_free((flex_ptr)&p);
  flex_free((flex_ptr)&q);
}

void sheet_sort(void)
{ if(sx0==sx1) sortcol();
  else if(sy0==sy1) sortrow();
  if(autox) sheet_change();
}
