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

#include "indexl.h"

#define NBITS 5

static Dict D;
static Map l;
static Bn *Ush1,*Ush,*Ushe;
static Query Q;
static Code code;
static int dopen;

static INLINE int
query_close_maps(void) {

  Hit *h;
  Map m={0};

  for (h=Q.h1;h<Q.he;h++)
    if (h==(Hit *)l.m-1 || h->m1!=h[1].m1) {
      m.m1=(void *)h->m1;
      m.me=(void *)h->me;
      if (!map_close(&m))
	return 0;
    }

  return 1;

}

static void
query_end(void) {

  dict_close(&D);

}


int
query_new_dict(const char *s,int new) {

  static int n;

  Ush1=Ush=Ushe=NULL;
  if (!dict_close(&D))
    return 0;
  
  dopen=0;

  if (!dict_open(&D,s,new))
    return 0;

  if (!map_get(&D.w.b,0,D.w.b.size))
    return 0;

  if (!n && atexit(query_end)) {
    err("Can't setup query_end on exit\n");
    return 0;
  } else
    n=1;

  dopen=1;

  return 1;

}

void
query_dict_files(char **f1,char **fe) {

  if (!dopen)
    return;

  *f1=D.f.n.m1;
  *fe=D.f.n.me;
  for ((*fe)--;**fe!='z'+1;(*fe)--);
  (*fe)--;
  
}


int
query_new_word(const char *s) {

  Gen g={s,strlen(s)};
  Datum *n;

  if (!s || !dopen)
    return 0;

  Ush1=Ush=Ushe=NULL;
  memset(&code,0,sizeof(code));

  Q.he=l.m;
  if (!query_close_maps())
    return 0;

  if (!map_close(&l))
    return 0;
  memset(&Q,0,sizeof(Q));

  if (!(n=hash_get(&D.w.d,&g)))
    return 0;
  
  Ush=(Bn *)(D.w.b.m1+n->off.u);
  Ush+=2;
  Ush1=Ush;
  Ushe=Ush+Ush[-1];

  code=n->code.c;

  return 1;

}

static int
bncomp(const void *v1,const void *v2) {

  const Bn *b1=v1,*b2=v2;

  return *b1<*b2 ? -1 : +1;

}

static int
query_set_file(const char *s) {

  Datum *n;
  Gen g;
  Bn *b,t;
  int i;

  Q.he=l.m;
  if (!query_close_maps())
    return 0;

  if (!map_close(&l))
    return 0;
  memset(&Q,0,sizeof(Q));

  g.v=s;
  g.w=strlen(s);

  if (!(n=hash_get(&D.f.d,&g)))
    return 0;

  t=n->off.o.bn;
  b=bsearch1(&t,Ush1,Ushe-Ush1,sizeof(*Ush1),bncomp,&i);

  Ush=b==Ush1 ? b : b-1;

  return 1;

}


const Query *
query_word_lines(unsigned num,const char *f) {

  Map m;
  Hit *h,*hh;
  int skip=0;

  if (!dopen)
    return NULL;

  if (f && !query_set_file(f)) {
    err("Can't set file %s\n",f);
    return NULL;
  }

  if (!query_close_maps()) {
    err("Can't close maps\n");
    return NULL;
  }

  if (Q.he) {
    memcpy(l.m1,Q.he,l.m-(void *)Q.he);
    l.m=l.m1+(l.m-(void *)Q.he);
  }
    
  if ((Hit *)l.me-(Hit *)l.m1<num)
    if (!map_get(&l,l.o,l.o+num*sizeof(Hit)))
      return 0;

  hh=h=l.m;
  for (;Ush<Ushe && (Hit *)l.m-(Hit *)l.m1-skip<num;Ush++) {
    memset(&m,0,sizeof(m));
    z_file_decode(&D,Ush[0],code.value,&m,&l);
    for (;h<(Hit *)l.m;h++) 
      if (f && strcmp(h->f,f)<0)
	skip++;
  }

  Q.h1=l.m1;
  Q.h=Q.h1+skip;
  Q.he=l.m;
  Q.he=Q.he-Q.h > num ? Q.h+num : Q.he;

  return  Q.he>Q.h ? &Q : NULL;

}  

const Query *
query_current(void) {

  return  dopen && Q.he>Q.h ? &Q : NULL;

}


char **
query_format_line(const Hit *h,unsigned n) {

  static char *c1,*c,*ce;
  unsigned i,j;
  const char *t;

  i=n/2;
  t=h->l-h->b>i ? h->l-i : h->b;
  for (;t<h->l && !isspace(*t);t++);
  for (;t<h->l && isspace(*t);t++);
  j=h->l-t;
  
  c1=map_temp_str("%*.*s%-*.*s",i,j,t,i,i,h->l);
  ce=memchr(c1,0,n+1);

  for (c=c1;c<ce && *c;c++) 
    *c=*c==10 ? 32 : *c;
    
  for (;--c>=c1 && *c!=32;);
  *c=0;

  return &c1;

}

const char **
query_word_derivatives(const char *s) {

  static Map d,dd;
  W *w;
  const char *c,z=0;
  unsigned i,j,u;

  if (!dopen || !s || !*s)
    return NULL;

  j=strlen(s);
  d.m=d.m1;
  dd.m=dd.m1;
  for (i=0,w=D.w.he;w<(W *)D.w.h.me;w=(W *)(w->c+w->u)) {
    for (c=w->c,u=w->u;u>=j && (c=memchr(c,*s,u-j));c++,u=w->u-(c-w->c))
      if (!memcmp(c,s,j)) {
	map_write(&dd,w->c,w->u);
	map_write_element(&dd,z);
	i++;
	break;
      }
  }	
  for (c=dd.m1;c<(char *)dd.m;c+=strlen(c)+1)
    map_write_element(&d,c);
  c=0;
  map_write_element(&d,c);

  return d.m1;

}
