/*->c.fo */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.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.timex"
#include "h.xext"
#include "h.buffer"
#include "h.code"
#include "h.main"
#include "h.mym"
#include "h.serial"
#include "h.strdef"
#include "h.ram"
#include "h.file"
#include "h.pr"
#include "h.key"
#include "h.script"
#include "h.dir"
#include "h.view"
#include "h.batch"
#include "h.text"
#include "h.trans"

#include "h.fo"



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

#define FNMAXL 46
#define FXBOLD 0x1
#define FXITAL 0x2
#define FNMAXU 5
#define FNMAXF 16


typedef struct
{
 char name[FNMAXL];  /* font names Homerton.bold.oblique...            */
 char bits;
 char unit;
} fontnamestr;



static char *  fontbuff;    /* buffer to hold fontnames in     */
static int     totfonts;    /* total number of available fonts */


static int     dofontboot=1;


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

#define FL 12



typedef struct familymember
{
 char      names[FNMAXU][FL];
 char      bits;
 char      unit;
} familymember;


typedef struct family
{
 familymember member[FNMAXF];
} family;


static int italicfont(char * s)
{
 return(
        !cstrcmp("Italic",s) || !cstrcmp("Oblique",s)
       );
}


static int boldfont(char *s)
{
 return(
        !cstrcmp("Bold",s) || !cstrcmp("Demi",s)
       );
}




static int findbolditalicvar(family * fontf,int fmm,int fmax,int mode)
{
 int i;
 int j;
 int k;
 int ital;
 int bold;
 int italm=(mode & FXITAL)!=0;
 int boldm=(mode & FXBOLD)!=0;

 for(i=0;i<fmax;i++)
 {
  if(i!=fmm && (fontf->member[i].unit || fontf->member[i].bits)) continue;

  /* is this a italic/bold family member */

  ital=0;
  bold=0;
  j=1;
  while(j<FNMAXU)
  {
   if(!fontf->member[i].names[j][0]) {j=FNMAXU;break;}

   /* does this sub name appear in the family name ? */

   k=1;
   while(k<FNMAXU)
   {
    if(!fontf->member[fmm].names[k][0]) {k=FNMAXU;break;}
    if(!cstrcmp(fontf->member[i].names[j],
                fontf->member[fmm].names[k])) break;
    k++;
   }

   /* if k<FNMAXU yes */

   if(k==FNMAXU)
   {
    if(italm && italicfont(fontf->member[i].names[j])) ital=1;
    else
    if(boldm && boldfont(fontf->member[i].names[j])) bold=1;
    else break;
   }

   if(italm && italicfont(fontf->member[i].names[j]))  ital=1;
   else
   if(boldm && boldfont(fontf->member[i].names[j])) bold=1;

   j++;
  }

  if(ital==italm && bold==boldm && j==FNMAXU)
  {
   fontf->member[i].bits=mode;
   return(i);
  }
 }
 return(-1);
}


static int findfamilyname(family * fontf,int fmax)
{
 int ix;
 int i;
 int j;
 int units=1;
 int k;
 int goodone=0;
 int goodi;
 int goodm;
 int goodlen;
 int curm;
 int curlen;

 for(ix=0;ix<(2*fmax);ix++)
 {
  if(ix>=fmax)
  {
   if(goodone) break;
   goodi=i;
   i=ix-fmax;
  }
  else 
  {
   i=ix;
  }

  if(fontf->member[i].unit || fontf->member[i].bits) continue;
                                             /* this entry has been used */

  if(i==ix)
  {
   curm=0;
   for(j=1;j<FNMAXU;j++)
   {
    curlen=j;
    if(!fontf->member[i].names[j][0]) {j=FNMAXU;break;}
    if(boldfont(fontf->member[i].names[j])) break;
    if(italicfont(fontf->member[i].names[j])) break;
    if(!cstrcmp(fontf->member[i].names[j],"Medium")) curm=1;
   }
   if(j==FNMAXU)
   {
    if(!goodone || curlen<goodlen || (curm && curlen==goodlen))
    {                     
     goodm=curm;
     goodlen=curlen;
     goodi=i;
     goodone=1;
    }
   }
  }
  else break;
 }

 /* we now know which entry, is going to be the basis of the family name */
 /* we attempt to find the shortest unique name */

 i=goodi;

 for(k=0;k<fmax;k++)
 {
  if(fontf->member[k].unit)
  {
   for(j=1;j<FNMAXU;j++)
   {
    if(cstrcmp(fontf->member[i].names[j],
               fontf->member[k].names[j])) units|=(1<<j);
   }
  }
 }

 fontf->member[i].unit=units;
 return(i);
}




static void splitname(familymember * fmem,char * fontname)
{
 char * p;
 char * s;
 int    i;
 int    j;

 for(i=0;i<FNMAXU;i++)
 {
  fmem->names[i][0]=0;
  fmem->bits=0;
  fmem->unit=0;
 }

 p=s=fontname;
 i=0;

 while(1)
  {
   if((*p=='.') || (*p<32))
   {
    j=0;
    while(s<p) fmem->names[i][j++]=*s++;
    fmem->names[i][j]=0;
    s=p+1;
    i++;
    if(*p<32) break;
   }
   p++;
  }
}


static void makefname(familymember * fmem,fontnamestr * fname)
{
 int i;

 fname->bits=fmem->bits;
 fname->unit=fmem->unit;

 for(i=0;i<FNMAXU;i++)
 {
  if(!fmem->names[i][0]) break;
  else
  if(i)
  {
   strcat(fname->name,".");
   strcat(fname->name,fmem->names[i]);
  }
  else
   strcpy(fname->name,fmem->names[i]);
 }

}



static void sortfonts(void)
{
 fontnamestr * fontn=(fontnamestr *)fontbuff;
 family        fontf;
 int           fstart;
 int           fmstart;
 int           fmem;
 int           fn;
 int           fmm;
 int           fmb;
 int           fmi;
 int           fmbi;

 fn=0;

 while(1)
 {
   /* read in font families */

  fstart=fn;

  fmem=0;
  splitname(&fontf.member[fmem],fontn[fn].name);
  while(1)
  {
   fmem++;
   if(fmem>=FNMAXF) break;
   fn++;
   if(fn>=totfonts) break;
   splitname(&fontf.member[fmem],fontn[fn].name);
   if(cstrcmp(fontf.member[fmem].names[0],
              fontf.member[fmem-1].names[0])) break;
  }

  /* now have read in one font family */

  fmstart=0;

  while(fmstart<fmem)
  {
   fmm =findfamilyname(&fontf,fmem);
   fmbi=findbolditalicvar(&fontf,fmm,fmem,FXBOLD | FXITAL);
   fmb =findbolditalicvar(&fontf,fmm,fmem,FXBOLD);
   fmi =findbolditalicvar(&fontf,fmm,fmem,FXITAL);

   /* now move these members into a sub family at fmstart */

   if(fmm!=-1)
          {makefname(&fontf.member[fmm],&fontn[fstart+fmstart]);fmstart++;}
   if(fmb!=-1 && fmb!=fmm)
          {makefname(&fontf.member[fmb],&fontn[fstart+fmstart]);fmstart++;}
   if(fmi!=-1 && fmi!=fmm)
          {makefname(&fontf.member[fmi],&fontn[fstart+fmstart]);fmstart++;}
   if(fmbi!=-1 && fmbi!=fmm)
          {makefname(&fontf.member[fmbi],&fontn[fstart+fmstart]);fmstart++;}
  }

  if(fn>=totfonts) break;
 }

}


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



int findfontnumber(char * name)
{
 char * c;
 int    n;
 int    font;

 n=0;

 font=0;

 while(1)
 {
  c=fontname(n++);
  if(!c) break;
  if(!cstrcmp(name,c))
  {
   font=n-1;
   break;
  }
 }

 return(font);
}




/* return unique font number from internal font number */

static int ufont(int fn)
{
 int c;
 int u;
 fontnamestr * fontn=(fontnamestr *)fontbuff;

 u=0;
 c=0;
 while(c<fn)
 {
  c++;
  if(c>=totfonts) break; /* trouble we did not find internal number */
  if(fontn[c].unit) u++;
 }

 return(u);
}



/* return internal font number from unique font number */

static int ifont(int ufn)
{
 int c,u;
 fontnamestr * fontn=(fontnamestr *)fontbuff;

 u=0;
 c=0;
 while(u<ufn)
 {
  c++;
  if(c>=totfonts) {c=-1;break;} /* trouble did not find unique number */
  if(fontn[c].unit) u++;
 }

 return(c);
}



/* point at the i'th. unique font name */

char * fontname(int i)
{
 static char buff[FNMAXL];
 int c;
 int unit;
 fontnamestr * fontn=(fontnamestr *)fontbuff;
 char * p=buff;
 char * q;

 c=ifont(i);
 if(c<0) return(NULL);

 unit=fontn[c].unit;
 q=fontn[c].name;

 while(*q)
 {
  if(*q=='.')
  {
   unit=unit >> 1;
   if(unit)
   {
    if(unit & 0x1) *p++=' ';
   }
   else break;
   q++;
  }
  else
  if(unit & 0x1)
  {
   *p++=*q++;
  }
  else
        q++;
 }

 *p=0;

 return(buff);
}




static int fontfrmstyle(int ifn,int style)
{
 int best;
 int besthits;
 int hits;
 int s;
 fontnamestr * fontn=(fontnamestr *)fontbuff;

 best=ifn;
 besthits=0;

 while(ifn>0)              /* step back through font names */
 {
  if(!fontn[ifn].unit) ifn--;
  else break;
 }
  
                           /* now got to find a matching sub style */
 do
 {
  hits=0;
  s=fontn[ifn].bits;
  if(!s)   hits++;

  if(style & BOLD)
  {
   if(s & FXBOLD) hits+=2;
  }

  if(style & ITALICS)
  {
   if(s & FXITAL) hits+=2;
  }

  if(hits>besthits) {besthits=hits;best=ifn;}

  ifn++;
 } while(!fontn[ifn].unit && ifn<totfonts);

  return(best);
}



void writefontvariations(char * name,Draw_diag * diag,int pts)
{
 fontnamestr * fontn=(fontnamestr *)fontbuff;
 int ufn;
 int ifn;
 int xfn;
 
 ufn=findfontnumber(name);
 ifn=ifont(ufn);

 if(ifn>=0)
 {
  xfn=fontfrmstyle(ifn,0);
  writetotextcolumnf(diag,"\\F0 %s %d\n",fontn[xfn].name,pts);
  xfn=fontfrmstyle(ifn,BOLD);
  writetotextcolumnf(diag,"\\F1 %s %d\n",fontn[xfn].name,pts);
  xfn=fontfrmstyle(ifn,ITALICS);
  writetotextcolumnf(diag,"\\F2 %s %d\n",fontn[xfn].name,pts);
  xfn=fontfrmstyle(ifn,ITALICS|BOLD);
  writetotextcolumnf(diag,"\\F3 %s %d\n",fontn[xfn].name,pts);
 }
}


char * fontstylename(char * name,int type)
{
 fontnamestr * fontn;
 int           ufn;
 int           ifn;
 int           xfn;

 if(!fontinit())
 {
  fontn=(fontnamestr *)fontbuff;

  ufn=findfontnumber(name);
  ifn=ifont(ufn);

  if(ifn>=0)
  {
   xfn=fontfrmstyle(ifn,type);
   return(fontn[xfn].name);
  }
 }

 return(NULL);
}




int * setupfontmenu(void)
{ 
 int  * fontp;
 int  * fontm;
 char * c;
 static int totfontm;
 int    maxwidth=0;


 fontm=(int*)myrambuff;

 strings=myrambuff+MYRMAX;

 fontp=fontm+7;
 totfontm=0;

 while(1)
 {
  c=fontname(totfontm);
  totfontm++;
  if(!c) break;
  if(writemitem(&fontp,c,0,0,&maxwidth)) break;
  if((strings-(char*)fontp)<0x80) break; /* leave some room for ustyles */
 }
 *(fontp-6)|=0x80;

 writemheader(fontm,transtoken("Fonts"),maxwidth); /* Fonts */

 clearusermenus();
 font_menu=(int*)fontm;

 return(fontm);
}





 /* load up the font names */

static int getfonts(void)
{
 os_regset rx;
 os_error * ep;
 char fontnam[128];
 int  fo;
 fontnamestr * fontn;
 int  i;

 if(!fontbuff) flex_alloc((flex_ptr)&fontbuff,0);

 totfonts=fo=0;

 rx.r[1]=(int)fontnam;
 rx.r[2]=0;
 rx.r[3]=-1;
 ep=os_swix(0x40091,&rx);

 while(rx.r[2]!=-1 && !ep)
 {
  for(i=0;fontnam[i]>31;i++); /* find length of string nb terminates with CR */

  if(i<FNMAXL)
  {
   totfonts++;
   flex_extend((flex_ptr)&fontbuff,totfonts*sizeof(fontnamestr));

   fontn=(fontnamestr *)(fontbuff+fo*sizeof(fontnamestr));

   strcpy(fontn->name,fontnam);
   fontn->unit=0;
   fontn->bits=0;

   i=0;
   while(fontnam[i]>31)
   {
    fontn->name[i]=fontnam[i];
    i++;
   }
   fontn->name[i]=0;

   rx.r[1]=(int)fontnam;
   rx.r[3]=-1;
   ep=os_swix(0x40091,&rx);
   fo++;
  }
 }


 
 if(!totfonts || ep)
 {
  if(ep) report(ep);
  else
  errorbox("{FONT00}"); /* No fonts available! */
  flex_free((flex_ptr)&fontbuff);
  return(1);
 }

 sortfonts();

 return(0);
}



int fontinit(void)
{
 if(!dofontboot) return(0);
 if(getfonts()) return(1);
 dofontboot=0;
 return(0);
}



