/*->c.group */

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

#include "h.os"
#include "h.wimp"
#include "h.bbc"
#include "h.flex"

#include "h.Drawlevel0"

#include "h.def"
#include "h.wos"
#include "h.main"
#include "h.mym"
#include "h.serial"
#include "h.ram"
#include "h.file"
#include "h.strdef"
#include "h.pr"
#include "h.key"
#include "h.script"
#include "h.timex"
#include "h.fsx"
#include "h.dir"
#include "h.view"
#include "h.txfax"
#include "h.trans"

#include "h.group"



typedef struct groupline
{
 int      dirn;             /* number of directory entry */
 char     selected;
 char     a;
 char     b;
 char     c;
 int      spare[3];
} groupline;


typedef struct group
{
 int        entries; 
 int        n;       /* unique number */
 int        handle;
 int        spare[8];
 groupline  line[1]; /* ... */
} group;


typedef struct groupbase
{
 int        gxn;                                    /* how many            */
 int        gxnext;                                 /* unique inc number   */
 group   grp[1];
} groupbase;



static groupbase * gx;

static int         gxmenu;




static group * gxnptr(int gn)
{
 group * gxq;
 int     entries;

 gxq=&gx->grp[0];
 while(gn--)
 {
  entries=gxq->entries;
  gxq=(group *)((char*)gxq+entries*sizeof(groupline)+sizeof(group));
 }

 return(gxq);
}


static group * gxptr(int gn)
{
 group * gxq;
 int     entries;
 int     i;

 gxq=&gx->grp[0];
 for(i=0;i<gx->gxn;i++)
 {
  if(gxq->n==gn) return(gxq);
  entries=gxq->entries;
  gxq=(group *)((char*)gxq+entries*sizeof(groupline)+sizeof(group));
 }
 return(NULL);
}


int grouplines(int gn)
{
 group * gxp;

 gxp=gxptr(gn);
 if(gxp) return(gxp->entries);
 else    return(0);
}


int grouplineid(int gn,int line)
{
 group * gxp;

 gxp=gxptr(gn);
 if(gxp) return(gxp->line[line].dirn);
 else    return(0);
}


    
static void gzerohandles(void)
{
 group *    gxq;
 int        entries;
 int        i;
 int        j;

 gxq=&gx->grp[0];
 for(i=0;i<gx->gxn;i++)
 {
  gxq->handle=0;
  entries=gxq->entries;

  for(j=0;j<entries;j++)
  {
   gxq->line[j].selected=0;
  }

  gxq=(group *)((char*)gxq+entries*sizeof(groupline)+sizeof(group));
 }
}




os_error * addgroup(int * gn)
{
 os_error * err;
 int        size;
 group    * gxp;

 size=flex_size((flex_ptr)&gx);

 err=flex_extende((flex_ptr)&gx,size+sizeof(group));
 if(!err)
 {

  gxp=gxnptr(gx->gxn);

  gxp->entries=0;
  *gn=gxp->n=gx->gxnext++;
  gxp->handle=0;

  gx->gxn++;
 }
 else report(err);

 return(err);
}



static os_error * delgroup(int gn)
{
 os_error * err;
 group    * gxp;
 int        size;
 int        entries;
 int        offset;

 err=NULL;

 if(gn)
 {
  gxp=gxptr(gn);
  if(gxp)
  {
   entries=gxp->entries;
   size=entries*sizeof(groupline)+sizeof(group);
   offset=(char*)gxp-(char*)gx;
   err=flex_midextende((flex_ptr)&gx,offset+size,-size);
   gx->gxn--;
  }
 }

 return(err);
}



os_error * addgroupline(int gn,int dirn)
{
 os_error * err;
 group    * gxp;
 int        size;
 int        offset;
 int        entries;

 gxp=gxptr(gn);

 entries=gxp->entries;
 size=entries*sizeof(groupline)+sizeof(group);
 offset=(char*)gxp-(char*)gx;

 err=flex_midextende((flex_ptr)&gx,offset+size,sizeof(groupline));

 gxp->line[entries].dirn=dirn;
 gxp->entries++;

 return(err);
}




static os_error * delgroupline(int gn,int n)
{
 os_error * err;
 group    * gxp;
 int        size;
 int        offset;
 int        entries;

 err=NULL;

 gxp=gxptr(gn);

 if(gxp)
 {
  entries=gxp->entries;
  size=n*sizeof(groupline)+sizeof(group);
  offset=(char*)gxp-(char*)gx;

  err=flex_midextende((flex_ptr)&gx,offset+size,-sizeof(groupline));

  gxp->entries--;
 }

 return(err);
}







void loadgroup(FILE * fp)
{
 os_error * err;
 int        length;

 if(fp)
 {
  fread(&length,1,sizeof(int),fp);
  err=flex_alloce((flex_ptr)&gx,length);
  if(!err)
  {
   fread(gx,1,length,fp);
   gzerohandles();
  }
 }
 else
 {
  err=flex_alloce((flex_ptr)&gx,sizeof(groupbase));
  if(!err)
  {
   gx->gxn=0;
   gx->gxnext=1;
  }
 }
}



void savegroup(FILE * fp)
{
 int length;

 length=flex_size((flex_ptr)&gx);

 fwrite(&length,1,sizeof(int),fp);
 fwrite(gx,1,flex_size((flex_ptr)&gx),fp);
}



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


                        /* step between each macro entry in window */
#define TDIRDY   56
                        /* step between top of window and first macro */
#define TDIRFY   12
                        /* height of macro definitions */
#define TDIRHY   48
                        /* x step between entries */
#define TDIRDX   8
#define TDIRPX   8


#define TDIRFW   24

#define TDIRFXW   TDIRFW*16+2*TDIRPX

#define TDIRFX    8

#define TDIRWIDTH TDIRFX+TDIRFXW+TDIRDX


static void gxextent(group * gxp)
{
 int sety;
 int n;

 if(gxp->entries<4) n=4;
 else               n=gxp->entries;

 sety=TDIRFY+TDIRDY*n+TDIRFY;

 extent(gxp->handle,0,-sety,TDIRWIDTH+TDIRDX,0);
}



int getgn(int handle)
{
 int        i;
 group    * gxq;
 int        entries;

 gxq=&gx->grp[0];

 for(i=0;i<gx->gxn;i++)
 {
  entries=gxq->entries;
  if(gxq->handle==handle) return(gxq->n);

  gxq=(group *)((char*)gxq+entries*sizeof(groupline)+sizeof(group));
 }
 return(-1);
}




static void redrawgroupsub(wimp_redrawstr * redrawstr,int more,group * gxp)
{
 int        oy;
 int        ox;
 int        ylo;
 int        yhi;
 int        n;
 int        y;
 int        yp;
 int        entries;
 int        col;


 wimpfontstart();

 entries=gxp->entries;

 while(more)
 {
  ox=redrawstr->box.x0-redrawstr->scx;
  oy=redrawstr->box.y1-redrawstr->scy;

  ylo=-(redrawstr->g.y1-oy);            /* small number, top of window */
  yhi=-(redrawstr->g.y0-oy);            /* bigger, bottom of window */

  n=(ylo-TDIRFY)/TDIRDY;
  if(n<0) n=0;
  y=n*TDIRDY+TDIRFY;

  ox+=4;

  while(y<=yhi && n<entries)
  {
   yp=oy-y-4;

   if(gxp->line[n].selected) col=(6<<8)|0;
   else                      col=(1<<8)|7;

   plinthtext(ox+TDIRFX,yp,TDIRFXW,TDIRHY,diridname(gxp->line[n].dirn),col);

   y+=TDIRDY;
   n++;
  } 
  wimp_get_rectangle(redrawstr,&more);
 }

 wimpfontend();
}


void redrawgroup(int gn)
{
 wimp_redrawstr redrawstr;
 int            more;
 group        * gxp;

 gxp=gxptr(gn);
 redrawstr.w=ewindow;
 wimp_redraw_wind(&redrawstr,&more);
 redrawgroupsub(&redrawstr,more,gxp);
}


static void grefreshlineentry(group * gxp,int n)
{
 wimp_redrawstr r;
 int handle=gxp->handle;
 int more;

 r.box.x0=0;
 r.box.x1=TDIRWIDTH;
 r.box.y1=-TDIRFY-n*TDIRDY;
 r.box.y0=r.box.y1-TDIRHY-4;
 r.w=handle;

 wimp_update_wind(&r,&more);
 redrawgroupsub(&r,more,gxp);
}




static void gclearselect(group * gxp)
{
 int i;
 for(i=0;i<gxp->entries;i++)
 {
  if(gxp->line[i].selected)
  {
   gxp->line[i].selected=0;
   grefreshlineentry(gxp,i);
  }
 }
}



/* returns number of entries selected, and number of first */

static int gselected(group * gxp,int * k)
{
 int i;
 int n;

 n=0;

 for(i=0;i<gxp->entries;i++)
 {
  if(gxp->line[i].selected)
  {
   if(!n) *k=i;
   n++;
  }
 }
 return(n);
}




void icongroup(int gn)
{
 int        n;
 int        goodn;
 group * gxp;
 int        ns;
 int        k;

 gxp=gxptr(gn);

 getw(gxp->handle);

 n=(by-mousey-TDIRFY)/TDIRDY;
 goodn=(n>=0 && n<gxp->entries);

 if(buttons==0x1 && goodn)   /* adjust */
 {
  if(gxp->line[n].selected) gxp->line[n].selected=0;
  else                      gxp->line[n].selected=1;

  grefreshlineentry(gxp,n);
 }
 else
 if(buttons==0x4)   /* select */
 {
  gclearselect(gxp);
  if(goodn)
  {
   gxp->line[n].selected=1;
   grefreshlineentry(gxp,n);
  }
 }
 else
 if(buttons==0x2)   /* menu */
 {
  if(goodn)
  {
   ns=gselected(gxp,&k);

   if(ns==1) 
   {
    if(gxp->line[k].selected==2 && k!=n)
    {
     gxp->line[k].selected=0;
     grefreshlineentry(gxp,k);
     ns=0;
    }
   }
   if(!ns)
   {
    gxp->line[n].selected=2;
    grefreshlineentry(gxp,n);
   }
  }
  popgroup(gn);
 }
}


static void gdelselection(int gn)
{
 int        i;
 group * gxp;

 gxp=gxptr(gn);

 for(i=0;i<gxp->entries;i++)
 {
  if(gxp->line[i].selected)
  {
   delgroupline(gn,i);
   i--;
  }
 }

 refreshwindow(gxp->handle);
 moddir();
}




static void gclrselection(group * gxp)
{
 gclearselect(gxp);
}



static void gselall(group * gxp)
{
 int i;
 for(i=0;i<gxp->entries;i++)
 {
  if(!gxp->line[i].selected)
  {
   gxp->line[i].selected=1;
  }
 }
 refreshwindow(gxp->handle);
}




/* delete any group lines which belong to this dir entry */

void deletegrouplines(int dirid)
{
 group *    gxq;
 int        i;
 int        j;
 int        modded;

 gxq=&gx->grp[0];

 for(i=0;i<gx->gxn;i++)
 {
  modded=0;

  for(j=0;j<gxq->entries;j++)
  {
   if(gxq->line[j].dirn==dirid)
   {
    delgroupline(gxq->n,j);
    modded=1;
    j--;
   }
  }

  if(modded && gxq->handle)
  {
   refreshwindow(gxq->handle);
  }

  gxq=(group *)((char*)gxq+gxq->entries*sizeof(groupline)+sizeof(group));
 }
}



void closegroup(int gn)
{
 group * gxp;

 gxp=gxptr(gn);
 closedownhandle(&gxp->handle);
}



void delgrouphi(int gn)
{
 group * gxp;

 if(gn)
 {
  gxp=gxptr(gn);

  if(gxp)
  {
   if(gxp->handle) closegroup(gn);
   delgroup(gn);
  }
 }
}



void addgrouphi(int gn,char * name)
{
 int     dirn;
 group * gxp;

 gxp=gxptr(gn);

 dirn=finddirid(name);

 if(dirn<0) bbc_vdu(7);
 else
 {
  addgroupline(gn,dirn);
  gxextent(gxp);
  grefreshlineentry(gxp,gxp->entries-1);
  moddir();
 }
}


void addgrouphimenu(char * name)
{
 addgrouphi(gxmenu,name);
}



static void gxpopup(int gn)
{
 wimp_openstr   o;
 group        * gxp;

 gxp=gxptr(gn);

 o.w=gxp->handle;
 getw(o.w);

 o.box.x0=128+(gn % 5)*vscrlbar;
 o.box.x1=o.box.x0+x1-x0;
 o.box.y1=screeny-2*hscrlbar-(gn % 4)*hscrlbar;
 o.box.y0=o.box.y1-(y1-y0);
 o.y=0;
 o.x=0;
 o.behind=-1;

 wimp_open_wind(&o);
}



os_error * opengroup(int dirid,int gn)
{
 os_error  * err;
 wimp_wind * wp;
 group     * gxp;

 err=NULL;

 if(gn)
 {
  gxp=gxptr(gn);

  if(gxp->handle)
  {
   forward(gxp->handle);
  }
  else
  {
   wp=windpoi[GROUP];
   gxp->handle=0;
   strcpy(wp->title.text,diridname(dirid));
   report(wimp_create_wind(wp,&gxp->handle));
   gxextent(gxp);
   gxpopup(gn);
  }
 }
 return(err);
}


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


static void setpopgroup(int gn)
{
 group     * gxp;
 int         n;
 int         k;
 char      * p;

 gxp=gxptr(gn);

 getw(gxp->handle);

 n=gselected(gxp,&k);

 if(n>1)
  strcpy(menuaddr(group_menu,1),transtoken("DIR07"));
 else
 {
  if(n==1)
  {
   p=diridname(gxp->line[k].dirn);
  }
  else p="";

  sprintf(menuaddr(group_menu,1),transtoken("DIR08"),p);
 }

 unshadest(group_menu,1,n>0);
 tickst(group_menu,4,dirgroupshow(gxp->n));

}



void decodegroup(int m2,int m3,int m4,int m5)
{
 switch(m2)
 {
  case 0:
         gdecodedial(m3);
         break;

  case 1:
         gdelselection(gxmenu);
         break;

  case 2:
         gselall(gxptr(gxmenu));
         break;

  case 3:
         gclrselection(gxptr(gxmenu));
         break;

  case 4:
         dirtogglegroup(gxptr(gxmenu)->n);
         break;

 }

 m3=m4=m5;
 setpopgroup(gxmenu);
}


void popgroup(int gn)
{
 gxmenu=gn;
 setpopgroup(gn);
 popmenu(group_menu);
}

