#include <u.h>
#include <libc.h>
#include "complete.h"

static int
longestprefixlength(char *a, char *b, int n)
{
       int i, w;
       Rune ra, rb;

       for(i=0; i<n; i+=w){
               w = chartorune(&ra, a);
               chartorune(&rb, b);
               if(ra != rb)
                       break;
               a += w;
               b += w;
       }
       return i;
}

void
freecompletion(Completion *c)
{
       if(c){
               free(c->filename);
               free(c);
       }
}

static int
strpcmp(const void *va, const void *vb)
{
       char *a, *b;

       a = *(char**)va;
       b = *(char**)vb;
       return strcmp(a, b);
}

Completion*
complete(char *dir, char *s)
{
       long i, l, n, nfile, len, nbytes;
       int fd, minlen;
       Dir *dirp;
       char **name, *p;
       ulong* mode;
       Completion *c;

       if(strchr(s, '/') != nil){
               werrstr("slash character in name argument to complete()");
               return nil;
       }

       fd = open(dir, OREAD);
       if(fd < 0)
               return nil;

       n = dirreadall(fd, &dirp);
       if(n <= 0){
               close(fd);
               return nil;
       }

       /* find longest string, for allocation */
       len = 0;
       for(i=0; i<n; i++){
               l = strlen(dirp[i].name) + 1 + 1; /* +1 for /   +1 for \0 */
               if(l > len)
                       len = l;
       }

       name = malloc(n*sizeof(char*));
       mode = malloc(n*sizeof(ulong));
       c = malloc(sizeof(Completion) + len);
       if(name == nil || mode == nil || c == nil)
               goto Return;
       memset(c, 0, sizeof(Completion));

       /* find the matches */
       len = strlen(s);
       nfile = 0;
       minlen = 1000000;
       for(i=0; i<n; i++)
               if(strncmp(s, dirp[i].name, len) == 0){
                       name[nfile] = dirp[i].name;
                       mode[nfile] = dirp[i].mode;
                       if(minlen > strlen(dirp[i].name))
                               minlen = strlen(dirp[i].name);
                       nfile++;
               }

       if(nfile > 0) {
               /* report interesting results */
               /* trim length back to longest common initial string */
               for(i=1; i<nfile; i++)
                       minlen = longestprefixlength(name[0], name[i], minlen);

               /* build the answer */
               c->complete = (nfile == 1);
               c->advance = c->complete || (minlen > len);
               c->string = (char*)(c+1);
               memmove(c->string, name[0]+len, minlen-len);
               if(c->complete)
                       c->string[minlen++ - len] = (mode[0]&DMDIR)? '/' : ' ';
               c->string[minlen - len] = '\0';
               c->nmatch = nfile;
       } else {
               /* no match, so return all possible strings */
               for(i=0; i<n; i++){
                       name[i] = dirp[i].name;
                       mode[i] = dirp[i].mode;
               }
               nfile = n;
               c->nmatch = 0;
       }

       /* attach list of names */
       nbytes = nfile * sizeof(char*);
       for(i=0; i<nfile; i++)
               nbytes += strlen(name[i]) + 1 + 1;
       c->filename = malloc(nbytes);
       if(c->filename == nil)
               goto Return;
       p = (char*)(c->filename + nfile);
       for(i=0; i<nfile; i++){
               c->filename[i] = p;
               strcpy(p, name[i]);
               p += strlen(p);
               if(mode[i] & DMDIR)
                       *p++ = '/';
               *p++ = '\0';
       }
       c->nfile = nfile;
       qsort(c->filename, c->nfile, sizeof(c->filename[0]), strpcmp);

 Return:
       free(name);
       free(mode);
       free(dirp);
       close(fd);
       return c;
}