#include "fm.h"
#include "regex.h"
#include <signal.h>
#include <errno.h>

static void
set_mark(Line *l, char *first, Line *tl, char *last)
{
  int pos, epos;

  for (; !(last >= tl->lineBuf && last <= tl->lineBuf + tl->len) ; tl = tl->prev)
    if (!tl->prev || tl == l) {
      last = tl->lineBuf + tl->len;
      break;
    }

  for (pos = first - l->lineBuf ; l ; l = l->next, pos = 0) {
    for (epos = l == tl ? last - tl->lineBuf : l->len ; pos < epos ;)
      l->propBuf[pos++] |= PE_MARK;

    if (l == tl)
      break;
  }
}

#ifdef USE_ROMAJI
/* Migemo: romaji --> kana+kanji in regexp */
static int migemo_pid;
static int migemo_failure;
static FILE *migemor, *migemow;
#ifdef MANY_CHARSET
static const char *migemo_cs;
#endif

void
init_romaji_filter(int pid)
{
  if (!pid || pid == migemo_pid) {
    migemo_pid = 0;
    migemo_failure = !use_romaji_search;

    if (migemor)
      fclose(migemor);

    if (migemow)
      fclose(migemow);

    migemor = migemow = NULL;
#ifdef MANY_CHARSET
    migemo_cs = NULL;
#endif
  }
}

static int
open_migemo(char *cmd)
{
  int fdr[2];
  int fdw[2];
  int pid;
#ifdef MANY_CHARSET
  char *av0;
#endif

  if (migemo_failure)
    return 0;

#ifdef MANY_CHARSET
  av0 = cmd;
  SKIP_BLANKS(av0);
  av0 = allocStr(av0, strcspn(av0, " \t\r\n"));
  migemo_cs = lookup_process_charset(av0);
#endif

  if (pipe(fdr) < 0)
    goto err0;

  if (pipe(fdw) < 0)
    goto err1;

  flush_tty();
  /* migemow:fdw[1] -|-> fdw[0]=0 {migemo} fdr[1]=1 -|-> fdr[0]:migemor */

  if ((pid = fork()) < 0)
    goto err2;
  else if (!pid) {
    /* child */
    forkCleanup();
    close(fdr[0]);
    close(fdw[1]);
    dup2(fdw[0], 0);
    dup2(fdr[1], 1);

    if (fdw[0] > 1)
      close(fdw[0]);

    if (fdr[1] > 1)
      close(fdr[1]);

    execl("/bin/sh", "sh", "-c", cmd, NULL);
    fprintf(stderr, "%s: %s\n", cmd, strerror(errno));
    exit(1);
  }

  close(fdr[1]);
  close(fdw[0]);
  migemor = fdopen(fdr[0], "r");
  migemow = fdopen(fdw[1], "w");
  migemo_pid = pid;
  return 1;
err2:
  close(fdw[0]);
  close(fdw[1]);
err1:
  close(fdr[0]);
  close(fdr[1]);
err0:
  migemo_failure = 1;
  return 0;
}

static char *
migemostr(char *str)
{
  Str tmp = NULL;

  if ((!migemor || !migemow) && !open_migemo(romaji_filter))
    return str;

#ifdef MANY_CHARSET
  str = conv_str2isoStr(str, "@", migemo_cs)->ptr;
#endif
  fprintf(migemow, "%s\n", str);
  fflush(migemow);
  tmp = Strfgets(migemor);
  Strchop(tmp);

  if (tmp->length == 0)
    goto err;

#ifdef MANY_CHARSET
  tmp = conv_Str2mbStr(tmp, NULL, "@", migemo_cs);
#endif
  return tmp->ptr;
err:
  /* XXX: backend migemo is not working? */
  init_romaji_filter(0);
  migemo_failure = TRUE;
  return str;
}
#endif				/* USE_MIGEMO */

int
forwardSearch(Buffer *buf, char *str, int across_line_p)
{
  char *p, *ep, *mb, *first, *last;
  Line *l, *begin, *tl;
  int wrapped = FALSE, how = 0, res, tty_in_p = FALSE;
  int pos, tpos;
  Phase0Env *p0env;
#ifdef MANY_CHARSET
  int e;
  mb_wchar_t wc;
#endif

  if (!buf->firstLine)
    return SR_NOTFOUND;

#ifdef USE_ROMAJI
  if (use_romaji_search)
    str = migemostr(str);
#endif

#ifdef MANY_CHARSET
  pos = strlen(str);
  str = tty_mbs_apply_convv(str, &pos);
#endif
  p0env = buf->async_buf ? buf->async_buf->p2env->p1env->p0env : &main_p0env;

  if ((p = regexCompile(str, IgnoreCase)) != NULL) {
    disp_err_message(p, FALSE);
    return SR_NOTFOUND;
  }

  for (l = begin = buf->currentLine, pos = buf->pos ;;) {
    how &= ~RE_FLAG_BOF;

    if (pos < l->len) {
#ifdef MANY_CHARSET
      e = mb_mem_to_wchar_internal(&l->lineBuf[pos], l->len - pos, wc);
      pos += e > 0 ? e : 1;
#else
      ++pos;
#ifdef JP_CHARSET
      if (l->propBuf[pos] & PC_KANJI2)
	++pos;
#endif
#endif
    }
    else if (wrapped && l == begin)	/* no match */
      goto failure;
    else if ((l = l->next) || (l = getNextLine(buf, &tty_in_p)))
      pos = 0;
    else {
      if (WrapSearch && !wrapped && !tty_in_p) {
	l = buf->firstLine;
	pos = 0;
	wrapped = TRUE;
	how |= RE_FLAG_BOF;
      }
      else
	goto failure;
    }

    for (how &= ~RE_FLAG_REUSE, tl = l, tpos = pos, mb = NULL ;;
	 how |= RE_FLAG_REUSE, how &= ~RE_FLAG_BOF, tl = tl->next, tpos = 0) {
      if (tl->next && !tl->next->real_linenumber)
	how &= ~RE_FLAG_EOL;
      else
	how |= RE_FLAG_EOL;

      p = tl->lineBuf + tpos;
      ep = tl->lineBuf + tl->len;
      res = regexSearch(&p, &ep, how | (tl->next ? 0 : RE_FLAG_EOF));

      if (!mb)
	mb = p;

      if (!res || ep < tl->lineBuf + tl->len || !tl->next ||
	  (how & RE_FLAG_EOL && !across_line_p)) {
	matchedPosition(&first, &last);

	if (first && last) {
	  if (wrapped && l == begin && buf->pos == first - l->lineBuf)
	    /* exactly same match */
	    goto failure;

	  while (!(first >= l->lineBuf && first <= l->lineBuf + l->len))
	    l = l->next;

	  while (first >= l->lineBuf + l->len && l->next) {
	    l = l->next;
	    first = l->lineBuf;
	  }

	  buf->pos = first - l->lineBuf;
	  buf->currentLine = l;
	  gotoLine(buf, l->linenumber);
	  arrangeCursor(buf);
	  set_mark(l, first, tl, last);
	  return SR_FOUND | (wrapped ? SR_WRAPPED : 0);
	}
	else
	  break;
      }
    }

    pos = mb - l->lineBuf;
  }
failure:
  return SR_NOTFOUND;
}

int
backwardSearch(Buffer *buf, char *str, int across_line_p)
{
  char *p, *ep, *first, *last;
  Line *l, *begin, *tl;
  int wrapped = FALSE, how = 0;
  int pos, nextpos, tpos, iseol, isbol;
#ifdef MANY_CHARSET
  size_t b, e;
#endif

  if (!buf->firstLine)
    return SR_NOTFOUND;

#ifdef USE_ROMAJI
  if (use_romaji_search)
    str = migemostr(str);
#endif

#ifdef MANY_CHARSET
  pos = strlen(str);
  str = tty_mbs_apply_convv(str, &pos);
#endif

  if ((p = regexCompile(str, IgnoreCase)) != NULL) {
    disp_err_message(p, FALSE);
    return SR_NOTFOUND;
  }

  for (iseol = 0, l = begin = buf->currentLine, pos = buf->pos, isbol = !pos ;;) {
    if (iseol || ((wrapped && l == begin) ? pos > buf->pos : pos > 0)) {
      nextpos = pos;

      if (!iseol) {
#ifdef MANY_CHARSET
	e = pos;
	b = e - 1;
	mb_mem_to_wchar(l->lineBuf, &b, &e);
	pos = b;
#else
	--pos;
#ifdef JP_CHARSET
	if (l->propBuf[pos] & PC_KANJI2)
	  --pos;
#endif
#endif
      }

      iseol = 0;

      if (!l->prev && !pos)
	how |= RE_FLAG_BOF;
      else
	how &= ~RE_FLAG_BOF;

      how &= ~(RE_FLAG_REUSE | RE_FLAG_EOL);
      p = l->lineBuf + pos;
      ep = l->lineBuf + nextpos;

      if (regexSearch(&p, &ep, how) && ep >= l->lineBuf + nextpos)
	for (how |= RE_FLAG_REUSE, how &= ~RE_FLAG_BOF, tl = l, tpos = nextpos ;; tl = tl->next, tpos = 0) {
	  if (tl->next && !tl->next->real_linenumber)
	    how &= ~RE_FLAG_EOL;
	  else
	    how |= RE_FLAG_EOL;

	  p = tl->lineBuf + tpos;
	  ep = tl->lineBuf + tl->len;

	  if (regexSearch(&p, &ep, how | (tl->next ? 0 : RE_FLAG_EOF)) &&
	      ep >= tl->lineBuf + tl->len && tl->next &&
	      !(how & RE_FLAG_EOL && !across_line_p))
	    continue;

	  matchedPosition(&first, &last);

	  if (first && last) {
	    if (wrapped && l == begin && buf->pos == first - l->lineBuf)
	      /* exactly same match */
	      goto failure;

	    if (first - l->lineBuf >= l->len && l->next) {
	      buf->pos = 0;
	      buf->currentLine = l = l->next;
	    }
	    else {
	      buf->pos = first - l->lineBuf;
	      buf->currentLine = l;
	    }

	    gotoLine(buf, l->linenumber);
	    arrangeCursor(buf);
	    set_mark(l, first, tl, last);
	    return SR_FOUND | (wrapped ? SR_WRAPPED : 0);
	  }
	  else
	    break;
	}
    }
    else if (wrapped && l == begin)
      goto failure;
    else if ((l = l->prev)) {
      pos = l->len;
      iseol = !isbol;
      isbol = 0;
    }
    else if (WrapSearch) {
      l = buf->lastLine;
      pos = l->len;
      wrapped = TRUE;
      iseol = !isbol;
      isbol = 0;
    }
    else
      goto failure;
  }
failure:
  return SR_NOTFOUND;
}
