#include <u.h>
#include <libc.h>
#include <bio.h>
#include "dict.h"

/*
* Robert Électronique.
*/

enum
{
       CIT = MULTIE+1, /* citation ptr followed by long int and ascii label */
       BROM,           /* bold roman */
       ITON,           /* start italic */
       ROM,            /* roman */
       SYM,            /* symbol font? */
       HEL,            /* helvetica */
       BHEL,           /* helvetica bold */
       SMALL,          /* smaller? */
       ITOFF,          /* end italic */
       SUP,            /* following character is superscript */
       SUB             /* following character is subscript */
};

static Rune intab[256] = {
       /*0*/   /*1*/   /*2*/   /*3*/   /*4*/   /*5*/   /*6*/   /*7*/
/*00*/  NONE,   L'☺', L'☻', L'♥', L'♦', L'♣', L'♠', L'•',
       0x25d8, L'ʘ',  L'\n',  L'♂', L'♀', L'♪', 0x266b, L'※',
/*10*/  L'⇨', L'⇦', L'↕', L'‼', L'¶',  L'§',  L'⁃', L'↨',
       L'↑', L'↓', L'→', L'←', L'⌙', L'↔', 0x25b4, 0x25be,
/*20*/  L' ',   L'!',   L'"',   L'#',   L'$',   L'%',   L'&',   L''',
       L'(',   L')',   L'*',   L'+',   L',',   L'-',   L'.',   L'/',
/*30*/  L'0',   L'1',   L'2',   L'3',   L'4',   L'5',   L'6',   L'7',
       L'8',   L'9',   L':',   L';',   L'<',   L'=',   L'>',   L'?',
/*40*/  L'@',   L'A',   L'B',   L'C',   L'D',   L'E',   L'F',   L'G',
       L'H',   L'I',   L'J',   L'K',   L'L',   L'M',   L'N',   L'O',
/*50*/  L'P',   L'Q',   L'R',   L'S',   L'T',   L'U',   L'V',   L'W',
       L'X',   L'Y',   L'Z',   L'[',   L'\\',  L']',   L'^',   L'_',
/*60*/  L'`',   L'a',   L'b',   L'c',   L'd',   L'e',   L'f',   L'g',
       L'h',   L'i',   L'j',   L'k',   L'l',   L'm',   L'n',   L'o',
/*70*/  L'p',   L'q',   L'r',   L's',   L't',   L'u',   L'v',   L'w',
       L'x',   L'y',   L'z',   L'{',   L'|',   L'}',   L'~',   L'',
/*80*/  L'Ç',  L'ü',  L'é',  L'â',  L'ä',  L'à',  L'å',  L'ç',
       L'ê',  L'ë',  L'è',  L'ï',  L'î',  L'ì',  L'Ä',  L'Å',
/*90*/  L'É',  L'æ',  L'Æ',  L'ô',  L'ö',  L'ò',  L'û',  L'ù',
       L'ÿ',  L'Ö',  L'Ü',  L'¢',  L'£',  L'¥',  L'₧', L'ʃ',
/*a0*/  L'á',  L'í',  L'ó',  L'ú',  L'ñ',  L'Ñ',  L'ª',  L'º',
       L'¿',  L'⌐', L'¬',  L'½',  L'¼',  L'¡',  L'«',  L'»',
/*b0*/  NONE,   NONE,   NONE,   NONE,   NONE,   NONE,   NONE,   NONE,
       NONE,   NONE,   NONE,   NONE,   NONE,   NONE,   NONE,   NONE,
/*c0*/  NONE,   NONE,   NONE,   NONE,   NONE,   NONE,   NONE,   NONE,
       CIT,    BROM,   NONE,   ITON,   ROM,    SYM,    HEL,    BHEL,
/*d0*/  NONE,   SMALL,  ITOFF,  SUP,    SUB,    NONE,   NONE,   NONE,
       NONE,   NONE,   NONE,   NONE,   NONE,   NONE,   NONE,   NONE,
/*e0*/  L'α',  L'ß',  L'γ',  L'π',  L'Σ',  L'σ',  L'µ',  L'τ',
       L'Φ',  L'Θ',  L'Ω',  L'δ',  L'∞', L'Ø',  L'ε',  L'∩',
/*f0*/  L'≡', L'±',  L'≥', L'≤', L'⌠', L'⌡', L'÷',  L'≈',
       L'°',  L'∙', L'·',  L'√', L'ⁿ', L'²',  L'∎', L' ',
};

static Rune suptab[] = {
       ['0'] L'⁰',   ['1'] L'¹',    ['2'] L'²',    ['3'] L'³',
       ['4'] L'⁴',   ['5'] L'⁵',   ['6'] L'⁶',   ['7'] L'⁷',
       ['8'] L'⁸',   ['9'] L'⁹',   ['+'] L'⁺',   ['-'] L'⁻',
       ['='] L'⁼',   ['('] L'⁽',   [')'] L'⁾',   ['a'] L'ª',
       ['n'] L'ⁿ',   ['o'] L'º'
};

static Rune subtab[] = {
       ['0'] L'₀',   ['1'] L'₁',   ['2'] L'₂',   ['3'] L'₃',
       ['4'] L'₄',   ['5'] L'₅',   ['6'] L'₆',   ['7'] L'₇',
       ['8'] L'₈',   ['9'] L'₉',   ['+'] L'₊',   ['-'] L'₋',
       ['='] L'₌',   ['('] L'₍',   [')'] L'₎'
};

#define GSHORT(p)       (((p)[0]<<8) | (p)[1])
#define GLONG(p)        (((p)[0]<<24) | ((p)[1]<<16) | ((p)[2]<<8) | (p)[3])

static char     cfile[] = "/lib/dict/robert/cits.rob";
static char     dfile[] = "/lib/dict/robert/defs.rob";
static char     efile[] = "/lib/dict/robert/etym.rob";
static char     kfile[] = "/lib/dict/robert/_phon";

static Biobuf * cb;
static Biobuf * db;
static Biobuf * eb;

static Biobuf * Bouvrir(char*);
static void     citation(int, int);
static void     robertprintentry(Entry*, Entry*, int);

void
robertindexentry(Entry e, int cmd)
{
       uchar *p = (uchar *)e.start;
       long ea, el, da, dl, fa;
       Entry def, etym;

       ea = GLONG(&p[0]);
       el = GSHORT(&p[4]);
       da = GLONG(&p[6]);
       dl = GSHORT(&p[10]);
       fa = GLONG(&p[12]);
       USED(fa);

       if(db == 0)
               db = Bouvrir(dfile);
       if((def.start = malloc(dl+1)) == nil)
               sysfatal("malloc: %r");
       def.end = def.start + dl;
       def.doff = da;
       Bseek(db, da, 0);
       Bread(db, def.start, dl);
       *def.end = 0;
       if(cmd == 'h'){
               robertprintentry(&def, 0, cmd);
       }else{
               if(eb == 0)
                       eb = Bouvrir(efile);
               if((etym.start = malloc(el+1)) == nil)
                       sysfatal("malloc: %r");
               etym.end = etym.start + el;
               etym.doff = ea;
               Bseek(eb, ea, 0);
               Bread(eb, etym.start, el);
               *etym.end = 0;
               robertprintentry(&def, &etym, cmd);
               free(etym.start);
       }
       free(def.start);
}

static void
robertprintentry(Entry *def, Entry *etym, int cmd)
{
       uchar *p, *pe;
       Rune r; int c, n;
       int baseline = 0;
       int lineno = 0;
       int cit = 0;

       p = (uchar *)def->start;
       pe = (uchar *)def->end;
       while(p < pe){
               if(cmd == 'r'){
                       outchar(*p++);
                       continue;
               }
               c = *p++;
               switch(r = intab[c]){   /* assign = */
               case BROM:
               case ITON:
               case ROM:
               case SYM:
               case HEL:
               case BHEL:
               case SMALL:
               case ITOFF:
               case NONE:
                       if(debug)
                               outprint("\\%.2ux", c);
                       baseline = 0;
                       break;

               case SUP:
                       baseline = 1;
                       break;

               case SUB:
                       baseline = -1;
                       break;

               case CIT:
                       n = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
                       p += 4;
                       if(debug)
                               outprint("[%d]", n);
                       while(*p == ' ' || ('0'<=*p && *p<='9') || *p == '.'){
                               if(debug)
                                       outchar(*p);
                               ++p;
                       }
                       ++cit;
                       outnl(2);
                       citation(n, cmd);
                       baseline = 0;
                       break;

               case '\n':
                       outnl(0);
                       baseline = 0;
                       ++lineno;
                       break;

               default:
                       if(baseline > 0 && r < nelem(suptab))
                               r = suptab[r];
                       else if(baseline < 0 && r < nelem(subtab))
                               r = subtab[r];
                       if(cit){
                               outchar('\n');
                               cit = 0;
                       }
                       outrune(r);
                       baseline = 0;
                       break;
               }
               if(r == '\n'){
                       if(cmd == 'h')
                               break;
                       if(lineno == 1 && etym)
                               robertprintentry(etym, 0, cmd);
               }
       }
       outnl(0);
}

static void
citation(int addr, int cmd)
{
       Entry cit;

       if(cb == 0)
               cb = Bouvrir(cfile);
       Bseek(cb, addr, 0);
       cit.start = Brdline(cb, 0xc8);
       cit.end = cit.start + Blinelen(cb) - 1;
       cit.doff = addr;
       *cit.end = 0;
       robertprintentry(&cit, 0, cmd);
}

long
robertnextoff(long fromoff)
{
       return (fromoff & ~15) + 16;
}

void
robertprintkey(void)
{
       Biobuf *db;
       char *l;

       db = Bouvrir(kfile);
       while(l = Brdline(db, '\n'))    /* assign = */
               Bwrite(bout, l, Blinelen(db));
       Bterm(db);
}

void
robertflexentry(Entry e, int cmd)
{
       uchar *p, *pe;
       Rune r; int c;
       int lineno = 1;

       p = (uchar *)e.start;
       pe = (uchar *)e.end;
       while(p < pe){
               if(cmd == 'r'){
                       Bputc(bout, *p++);
                       continue;
               }
               c = *p++;
               r = intab[c];
               if(r == '$')
                       r = '\n';
               if(r == '\n'){
                       ++lineno;
                       if(cmd == 'h' && lineno > 2)
                               break;
               }
               if(cmd == 'h' && lineno < 2)
                       continue;
               if(r > MULTIE){
                       if(debug)
                               Bprint(bout, "\\%.2ux", c);
                       continue;
               }
               if(r < Runeself)
                       Bputc(bout, r);
               else
                       Bputrune(bout, r);
       }
       outnl(0);
}

long
robertnextflex(long fromoff)
{
       int c;

       if(Bseek(bdict, fromoff, 0) < 0)
               return -1;
       while((c = Bgetc(bdict)) >= 0){
               if(c == '$')
                       return Boffset(bdict);
       }
       return -1;
}

static Biobuf *
Bouvrir(char *fichier)
{
       Biobuf *db;

       db = Bopen(fichier, OREAD);
       if(db == 0){
               fprint(2, "%s: impossible d'ouvrir %s: %r\n", argv0, fichier);
               exits("ouvrir");
       }
       return db;
}