/* $Id$ */
#include "bbs.h"

#ifndef NEW_AIDS
#error "Not complete yet"
#endif

aidu_t fn2aidu(const char *fn)
{
  aidu_t aidu = 0;
  aidu_t type = 0;
  aidu_t v1 = 0;
  aidu_t v2 = 0;
  char fnbuf[FNLEN + 1];
  char *sp = NULL;

  if(fn == NULL)
    return 0;
  strncpy(fnbuf, fn, FNLEN);
  fnbuf[FNLEN] = '\0';
  sp = fnbuf;

  switch(*(sp ++))
  {
    case 'M':
      type = 0;
      break;
    case 'G':
      type = 1;
      break;
    default:
      return 0;
      break;
  }

  if(*(sp ++) != '.')
    return 0;
  v1 = strtoul(sp, &sp, 10);
  if(sp == NULL)
    return 0;
  if(*sp != '.' || *(sp + 1) != 'A')
    return 0;
  sp += 2;
  if(*(sp ++) == '.')
  {
    v2 = strtoul(sp, &sp, 16);
    if(sp == NULL)
      return 0;
  }
  aidu = ((type & 0xf) << 44) | ((v1 & 0xffffffff) << 12) | (v2 & 0xfff);

  return aidu;
}

/* IMPORTANT:
 *   size of buf must be at least 8+1 bytes
 */
char *aidu2aidc(char *buf, const aidu_t orig_aidu)
{
  const char aidu2aidc_table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
  const int aidu2aidc_tablesize = sizeof(aidu2aidc_table) - 1;
  char *sp = buf + 8;
  aidu_t aidu = orig_aidu;
  aidu_t v;

  *(sp --) = '\0';
  while(sp >= buf)
  {
    /* FIXME: 能保證 aidu2aidc_tablesize 是 2 的冪次的話，
              這裡可以改用 bitwise operation 做 */
    v = aidu % aidu2aidc_tablesize;
    aidu = aidu / aidu2aidc_tablesize;
    *(sp --) = aidu2aidc_table[v];
  }
  return buf;
}

/* IMPORTANT:
 *   size of fn must be at least FNLEN bytes
 */
char *aidu2fn(char *fn, const aidu_t aidu)
{
  aidu_t type = ((aidu >> 44) & 0xf);
  aidu_t v1 = ((aidu >> 12) & 0xffffffff);
  aidu_t v2 = (aidu & 0xfff);

  if(fn == NULL)
    return NULL;
  snprintf(fn, FNLEN, "%c.%d.A.%03X", ((type == 0) ? 'M' : 'G'), (unsigned int)v1, (unsigned int)v2);
  return fn;
}

aidu_t aidc2aidu(const char *aidc)
{
  const char *sp = aidc;
  aidu_t aidu = 0;

  if(aidc == NULL)
    return 0;

  while(*sp != '\0' && /* ignore trailing spaces */ *sp != ' ')
  {
    aidu_t v = 0;
    /* FIXME: 查表法會不會比較快？ */
    if(*sp >= '0' && *sp <= '9')
      v = *sp - '0';
    else if(*sp >= 'A' && *sp <= 'Z')
      v = *sp - 'A' + 10;
    else if(*sp >= 'a' && *sp <= 'z')
      v = *sp - 'a' + 36;
    else if(*sp == '-')
      v = 62;
    else if(*sp == '_')
      v = 63;
    else if(*sp == '@')
      break;
    else
      return 0;
    aidu <<= 6;
    aidu |= (v & 0x3f);
    sp ++;
  }

  return aidu;
}

int search_aidu_in_bfile(const char *bfile, const aidu_t aidu)
{
  char fn[FNLEN];
  int fd;
  fileheader_t fhs[64];
  int len, i;
  int pos = 0;
  int found = 0;
  int lastpos = 0;

  if(aidu2fn(fn, aidu) == NULL)
    return -1;
  if((fd = open(bfile, O_RDONLY, 0)) < 0)
    return -1;

  while(!found && (len = read(fd, fhs, sizeof(fhs))) > 0)
  {
    len /= sizeof(fileheader_t);
    for(i = 0; i < len; i ++)
    {
      int l;
      if(strcmp(fhs[i].filename, fn) == 0 ||
         ((aidu & 0xfff) == 0 && (l = strlen(fhs[i].filename)) > 6 &&
          strncmp(fhs[i].filename, fn, l) == 0))
      {
        if(fhs[i].filemode & FILE_BOTTOM)
        {
          lastpos = pos;
        }
        else
        {
          found = 1;
          break;
        }
      }
      pos ++;
    }
  }
  close(fd);

  return (found ? pos : (lastpos ? lastpos : -1));
}

int search_aidu_in_board(SearchAIDResult_t *r, const char *bname, const aidu_t aidu)
{
  int n = -1;
  char dirfile[PATHLEN];

  if(r == NULL)
    return -1;
  r->n = -1;
  /* search bottom */
  {
    char bf[FNLEN];

    snprintf(bf, FNLEN, "%s.bottom", FN_DIR);
    setbfile(dirfile, bname, bf);
    if((n = search_aidu_in_bfile(dirfile, aidu)) >= 0)
    {
      r->where = AIDR_BOTTOM;
      r->n = n;
    }
  }
  /* else search board */
  if(r->n < 0)
  {
    setbfile(dirfile, bname, FN_DIR);
    if((n = search_aidu_in_bfile(dirfile, aidu)) >= 0)
    {
      r->where = AIDR_BOARD;
      r->n = n;
    }
  }
  /* else search digest */
  if(r->n < 0)
  {
    setbfile(dirfile, bname, fn_mandex);
    if((n = search_aidu_in_bfile(dirfile, aidu)) >= 0)
    {
      r->where = AIDR_DIGEST;
      r->n = n;
    }
  }
  return r->n;
}

int do_search_aid(SearchAIDResult_t *r)
{
  char aidc[100];
  char bname[IDLEN + 1] = "";
  aidu_t aidu = 0;
  char *sp;
  char *sp2;
  char *emsg = NULL;

  if(r == NULL)
    return -1;
  r->n = -1;
  if(!getdata(b_lines, 0, "搜尋" AID_DISPLAYNAME ": #", aidc, 15 + IDLEN, LCECHO))
  {
    move(b_lines, 0);
    clrtoeol();
    return -1;
  }

  if(currstat == RMAIL)
  {
    move(21, 0);
    clrtobot();
    move(22, 0);
    prints("此狀態下無法搜尋" AID_DISPLAYNAME);
    pressanykey();
    return -1;
  }

  sp = aidc;
  while(*sp == ' ')
    sp ++;
  while(*sp == '#')
    sp ++;
  aidu = aidc2aidu(sp);
  if((sp2 = strchr(sp, '@')) != NULL)
  {
    // assert(sizeof(bname) > IDLEN);
    strlcpy(bname, sp2 + 1, IDLEN+1);
    *sp2 = '\0';
  }
  else
    bname[0] = '\0';

  if(aidu > 0)
  {
    if(bname[0] != '\0')
    {
      if(!HasBoardPerm_bn(bname))
        return -1;
      search_aidu_in_board(r, bname, aidu);
      if(r->n >= 0)
      {
        if(enter_board(bname) < 0)
        {
          r->n = -1;
          emsg = "錯誤：無法進入指定的看板 %s";
        }
      }
    }
    else
    {
      search_aidu_in_board(r, currboard, aidu);
    }
  }

  if(r->n < 0)
  {
    if(aidu == 0)
      emsg = "不合法的" AID_DISPLAYNAME "，請確定輸入是正確的";
    else if(emsg == NULL)
    {
      if(bname[0] != '\0')
        emsg = "看板 %s 內找不到這個" AID_DISPLAYNAME "，可能是文章已經消失，或是找錯看板了";
      else
        emsg = "找不到這個" AID_DISPLAYNAME "，可能是文章已經消失，或是找錯看板了";
    }
    move(21, 0);
    clrtoeol();
    move(22, 0);
    prints(emsg, bname);
    pressanykey();
    r->n = -1;
    return r->n;
  }
  else
  {
    return r->n;
  }
}
