#include "snd.h"

/* -------- GLOBAL FIND -------- */
/* 
 * the dialog stays around until dismissed by "Done".  Each click on "Find" runs the search again
 */

typedef struct {int n; int chans; int inc; chan_info **cps; snd_fd **fds; snd_fd **afs;} gfd;

static int prepare_global_search (chan_info *cp, void *g0)
{
  gfd *g = (gfd *)g0;
  g->cps[g->n] = cp;
  g->fds[g->n] = init_sample_read(cp->cursor+1,cp,READ_FORWARD);
  g->n++;
  return(0);
}

static int run_global_search (snd_state *ss, gfd *g)
{
  /* return 0 until success or all eof */
  /* if success, n=winner (as aref index), if eofs, n=-1 */
  int i,j,k;
  if (g->afs) next_samples_for_eval(g->afs);
  for (i=0;i<g->chans;i++)
    {
      if (g->cps[i])
	{
	  snd_search_fd = g->fds[i];     /* need global snd_fd for parser "y" evaluation */
	  next_sample_1(snd_search_fd);  /* sets current value to current fs->data and post-increments */
	  if (sop_eval(ss->search_tree))
	    {
	      g->n = i;
	      return(1);
	    }
	  if (read_sample_eof(snd_search_fd))
	    {
	      g->fds[i] = free_snd_fd(snd_search_fd);
	      g->cps[i] = NULL;
	      k=0;
	      for (j=0;j<g->chans;j++) 
		{
		  if (g->cps[i]) 
		    {
		      k=1;
		      break;
		    }
		}
	      if (k == 0) /* all at eof */
		{
		  g->n = -1;
		  return(1);
		}
	    }
	}
    }
  g->inc++;
  return(0);
}

static char search_no_luck[128];

char *global_search(snd_state *ss)
{
  /* set up snd_fd for each active channel, 
   * tick each one forward until a match is found, 
   * update cursor/graph and report success (if any) in associated info window
   * subsequent runs (if no new text) repeat the search from the current locations
   */
  int chans,i,redisplay,passes = 0;
  gfd *fd;
  chan_info *cp;
  chans = active_channels(ss,1);
  search_no_luck[0] = '\0';
  if (chans > 0)
    {
      fd = (gfd *)calloc(1,sizeof(gfd));
      fd->n = 0;
      fd->inc = 1;
      fd->chans = chans;
      fd->fds = (snd_fd **)calloc(chans,sizeof(snd_fd *));
      fd->cps = (chan_info **)calloc(chans,sizeof(chan_info *));
      map_over_chans(ss,prepare_global_search,(void *)fd);
      fd->afs = get_chans_needed_by_eval(ss,NULL,ss->search_tree,-1,READ_FORWARD);
      while (!(run_global_search(ss,fd)))
	{
	  passes++;
	  if (passes>=10000)
	    {
	      check_for_event(ss);
	      if (ss->stopped_explicitly) break;
	      passes = 0;
	      fd->n = -1;
	    }
	}
      if (fd->n == -1)
	{
	  if (ss->stopped_explicitly)
	    sprintf(search_no_luck,"search stopped");
	  else sprintf(search_no_luck,"%s: %s",ss->search_expr,snd_string_not_found);
	  /* printed by find_ok_callback in snd-xmenu.c */
	}
      else
	{
	  /* fd->n is winner, fd->inc is how far forward we searched from current cursor loc */
	  cp = fd->cps[fd->n];
	  cp->cursor += fd->inc;
	  /* now in its own info window show find state, and update graph if needed */
          cp->cursor_on = 1;
	  show_cursor_info(cp);
	  if ((cp->cursor >= graph_low_SAMPLE(cp)) && (cp->cursor <= graph_high_SAMPLE(cp))) 
	    redisplay  = CURSOR_IN_VIEW;
	  else redisplay = CURSOR_IN_MIDDLE;
	  handle_cursor(cp,redisplay);
	}
      ss->stopped_explicitly = 0;
      for (i=0;i<chans;i++) if (fd->cps[i]) free_snd_fd(fd->fds[i]);
      free(fd->fds);
      free(fd->cps);
      if (fd->afs) {free_chans_for_eval(fd->afs); free(fd->afs); fd->afs = NULL;}
      free(fd);
    }
  return(search_no_luck);
}

static int cursor_find(snd_info *sp, chan_info *cp, sop *tree, int count, int end_sample)
{
  /* count>0 -> search forward, else back */
  int i,c,inc,passes=0;
  snd_fd **afs;
  snd_state *ss;
  ss = sp->state;
  c=count;
  if (count > 0) 
    {
      i=cp->cursor+1; 
      inc = 1;
    }
  else 
    {
      i=cp->cursor-1;
      c=-c;
      inc = -1;
      end_sample--;
    }
  snd_search_fd = init_sample_read(i,cp,(count>0) ? READ_FORWARD : READ_BACKWARD);
  afs = get_chans_needed_by_eval(sp->state,cp,sp->search_tree,i,(count>0) ? READ_FORWARD : READ_BACKWARD);
  if (count > 0) snd_search_fd->direction = 1; else snd_search_fd->direction = -1;
  while ((c>0) && (i != end_sample) && (!read_sample_eof(snd_search_fd)))
    {
      if (count > 0)
	next_sample_1(snd_search_fd);
      else previous_sample_1(snd_search_fd);
      if (afs) 
	{
	  if (count > 0)
	    next_samples_for_eval(afs);
	  else previous_samples_for_eval(afs);
	}
      if (sop_eval(sp->search_tree)) {c--; if (c == 0) break;}
      i+=inc;
      passes++;
      if (passes>=10000)
	{
	  check_for_event(ss);
	  if (ss->stopped_explicitly) break;
	  passes = 0;
	}
    }
  ss->stopped_explicitly = 0;
  snd_search_fd = free_snd_fd(snd_search_fd);
  if (afs) {free_chans_for_eval(afs); free(afs); afs = NULL;}
  if (c != 0) return(-1); /* impossible sample number, so => failure */
  return(i);
}

static void get_find_expression(snd_info *sp, int count)
{
  /* clear previous ? */
  search_no_luck[0] = '\0';
  text_set_string(snd_widget(sp,W_snd_info),search_no_luck);
  make_button_label(snd_widget(sp,W_snd_info_label),snd_string_find_p);
  sp->minibuffer_on = 1;
  goto_minibuffer(sp);
  sp->searching = count;
}

int cursor_search(chan_info *cp, int count)
{
  int samp;
  snd_info *sp;
  char *s1,*s2;
  sp = cp->sound;
  if (sp->searching)
    {
      if (!sp->search_tree) return(CURSOR_IN_VIEW); /* no search expr */
      if (count > 0)
	samp = cursor_find(sp,cp,sp->search_tree,count,current_ed_samples(cp));
      else samp = cursor_find(sp,cp,sp->search_tree,count,0);
      if (samp == -1) 
	{ 
	  sprintf(search_no_luck,"%s: %s",sp->search_expr,snd_string_not_found);
	  text_set_string(snd_widget(sp,W_snd_info),search_no_luck);
	  return(CURSOR_IN_VIEW);
	}
      else
	{
	  sprintf(search_no_luck,"%s: y = %s %s %s (%d)",
		  sp->search_expr,
		  s1 = prettyf(sample(samp,cp),2),
		  snd_string_at,
		  s2 = prettyf((double)samp/(double)snd_SRATE(cp),2),
		  samp);
	  text_set_string(snd_widget(sp,W_snd_info),search_no_luck);
	  free(s1);
	  free(s2);
	}
      cursor_moveto(cp,samp);
      if ((cp->cursor >= graph_low_SAMPLE(cp)) && (cp->cursor <= graph_high_SAMPLE(cp))) 
	return(CURSOR_IN_VIEW);
      else return(CURSOR_IN_MIDDLE);
    }
  else get_find_expression(sp,count);
  return(CURSOR_IN_VIEW);
}

