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

#define INV(n) (((n&0xff)<<24)|((n&0xff00)<<8)|((n&0xff0000)>>8)|((n&0xff000000)>>24))
uchar xlat[256];
uchar xtend[64];
uchar xtrans[512];
uchar *(maps[32]);
int mp,mt;
uchar gopen,gclose;
int  letternr;
char std_letters[]="letters.conf";

NODE *load_dawg(char *name, long *info,long mark);
NODE *load_dwg(char *name, long *info);
NODE *load_pck(char *name, long *info);
NODE *load_pck2(char *name, long *info);
int load_abc(char *name);
uchar chlen_utf(uchar *utfstr);
uchar strlen_utf(uchar *utfstr);
int get_char_utf(uchar **str);
uchar get_char_scr(uchar **str);
int ch_scr2utf(uchar nr,uchar *utfstr);
void str_scr2utf(uchar *scrstr,uchar *utfstr);
void ins_char(int nr);
void xins(uchar *str,int nr);
void cleanup_abc();

NODE *load_dawg(char *name, long *info,long mark)
{ FILE *h;
  NODE *ptr,*pp;
  int i,j;

  if((h=fopen(name,"rb"))!=NULL) {
    fread(info,sizeof(long),4,h);
    if(info[0]==mark) {
      if((ptr=(NODE *)malloc(info[1]))!=NULL) {
        fread(ptr,1,info[1],h);
      }
    } else {
      if(info[0]==INV(mark)) {
        if((ptr=(NODE *)malloc(INV(info[1])))!=NULL) {
          fread(ptr,1,INV(info[1]),h);
          j=INV(info[1])/4;
          for(i=0;i<j;i++) {
            ptr[i]=INV(ptr[i]);
          }
          for(i=0;i<4;i++) {
            info[i]=INV(info[i]);
          }
        } else ptr=NULL;
      } else ptr=NULL;
    }
    fclose(h);
    return(ptr);
  } else return(NULL);
}

NODE *load_dwg(char *name, long *info)
{ return(load_dawg(name,info,DAWG_MAGIC)); }
NODE *load_pck(char *name, long *info)
{ return(load_dawg(name,info,PACK_MAGIC)); }
NODE *load_pck2(char *name, long *info)
{ return(load_dawg(name,info,PACK2_MAGIC)); }

void xins(uchar *str,int nr)
{ uchar *map;
  int i;
  uchar c;
  map=xlat;
  for(i=chlen_utf(str)-1;i>0;i--) {
    if(c=map[*(str++)]) map=maps[c&(~128)];
    else { map[*(str-1)]=128|mp;
      map=maps[mp++]=((uchar *)memset((uchar *)malloc(128),0,128))-128;
    }
  }
  map[*str]=nr;
}

int load_abc(char *name)
{ FILE *h;
  int c;
  uchar line[80];
  uchar *ptr,ptr2;
  uchar *map;
  int jletternr,i;
  
  if(*name==0) name=std_letters;
  if((h=fopen(name,"rb"))==NULL) return(0);
  memset(xlat,0,256);
  memset(xtend,0,64);
  ptr2=mp=mt=0;
  while((c=getc(h))!=(uchar)EOF) {
    ptr=line;
    do { *(ptr++)=c; }
    while(((c=getc(h))!='\n') && (c!=EOF)) ;
    if(*(ptr-1)==0x0d) ptr--;
    *ptr=0;
    if(line[0]!='#') {
      ptr=line; while(*(ptr++)!=':');
      while(*(ptr++)==' ');ptr--;
/*      printf("%s\n",line); */
      switch(line[0]) {
        case 'a': { letternr=1;
	                while(c=*(ptr++)) {
	                  if(c<128) {
	                    xlat[letternr++]=c;
	                  } else {
	                    xlat[letternr++]=128|ptr2;
	                    for(i=chlen_utf(--ptr);i>0;i--) xtend[ptr2++]=*(ptr++);
	                    xtend[ptr2++]=0;
	                  }                    
	                }
	                for(i=1;i<letternr;i++) {
	                  if(xlat[i]<128) xlat[xlat[i]]=i;
	                  else xins(xtend+(xlat[i]&(~128)),i);
	                }
	                break; 
	              }
	    case 'j': { jletternr=M_JOKER;
	                xlat[jletternr++]='?';
	                while(c=*(ptr++)) {
	                  if(c<128) {
	                    xlat[jletternr++]=c;
	                  } else {
	                    xlat[jletternr++]=128|ptr2;
	                    for(i=chlen_utf(--ptr);i>0;i--) xtend[ptr2++]=*(ptr++);
	                    xtend[ptr2++]=0;
	                  }
	                }
	                for(i=M_JOKER+1;i<jletternr;i++) {
	                  if(xlat[i]<128) xlat[xlat[i]]=i;
	                  else xins(xtend+(xlat[i]&(~128)),i);
	                }
	                break; 
	              }
	    case 'e': { uchar **st2;int mt1;
	                st2=&ptr;
	                while(*ptr) {
	                  while(*ptr==' ') ptr++;
	                  mt1=mt;
	                  xtrans[mt++]=0;
	                  if(*ptr==gopen) {
	                    while(*(++ptr)!=gclose) xtrans[mt++]=*ptr;
	                    ptr++;
	                  } else {
	                    for(i=chlen_utf(ptr);i>0;i--) xtrans[mt++]=*(ptr++);
	                  }
	                  xtrans[mt++]=0;
	                  if(*ptr==gopen) {
	                    while(*(++ptr)!=gclose) xtrans[mt++]=get_char_scr(st2);
	                    ptr++;
	                  } else {
	                    xtrans[mt++]=get_char_scr(st2);
	                  }
	                  xtrans[mt++]=0;
	                  xtrans[mt1]=mt-mt1;
	                  if(mt>500) printf("Alarm\n");
	                }
	                break; 
	              }
	    case 'g': { gopen=*(ptr++);
	                gclose=*(ptr++);
	                break; 
	              }
	    default: {
	      printf("Unknown line identifier\n");
	      return(FALSE);
	    }
	  }
	}
  }
  fclose(h);
/*  for(i=0;i<256;i++) printf("%x ",xlat[i]);
  printf("%d\n",mp);
  for(i=128;i<256;i++) printf("%x ",(maps[0])[i]);
  printf("\n");
  for(i=0;i<300;i++) printf("%x ",xtrans[i]);  */
  return(TRUE);
}

void cleanup_abc()
{ if(mp) do free(maps[--mp]+128); while(mp);
}

uchar chlen_utf(uchar *utfstr)
{ return(((0xe5000000>>(((*utfstr)>>3)&0x1e))&3)+1); }

uchar strlen_utf(uchar *utfstr)
{ uchar ctr;
  ctr=0;
  while(*utfstr) {
    utfstr+=chlen_utf(utfstr);
    ctr++;
  }
  return(ctr);
}

int get_char_utf(uchar **str)
{ int i,l,c;
  l=chlen_utf(*str);
  c=(*((*str)++))&((1<<(8-l))-1);
  while(--l) { c=(c<<6)|((*((*str)++))&0x3f); }
  return(c);
}

uchar get_char_scr(uchar **str)
{ uchar c;
  uchar *map;
  map=xlat;
  if(**str==' ') return(0);
  while((c=map[*((*str)++)])>=128) map=maps[c-128];
  if(c==0) (*str)--;
  return(c);
}

int ch_scr2utf(uchar nr,uchar *utfstr)
{ uchar c,c1;
  if((c=xlat[nr])<128) { *utfstr=c; return(1); }
  else { c1=c-=128;
    while(*(utfstr++)=xtend[c1++]);
    return(c1-c-1);
  }
}

void str_scr2utf(uchar *scrstr,uchar *utfstr)
{ uchar *pscr;
  uchar *putf;
  pscr=scrstr;putf=utfstr;
  while(*pscr) putf+=ch_scr2utf(*(pscr++),putf);
  *putf=0; }
