/* Freebirth
 * Copyright (C) 1999 topher lafata <topher@topher.com>,
 *		      Jake Donham <jake@bitmechanic.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */



#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>

#ifndef __SAMPLE_PRODUCER_H
  #include "sample_producer.h"
#endif

#ifndef __SEQ_H
 #include "sequencer.h"
#endif

extern seq *sequencer;

void topo_sort(sample_producer *node, GList **order) {
  if (g_list_find(*order, (gpointer)node))
    return;
  else {
    sample_producer **children;
    for (children = node->get_children(node);
	 *children;
	 children++) {
      topo_sort(*children, order);
    }
    *order = g_list_append(*order, (gpointer)node);
  }
}

void expand_seq(char **tmpl, char *prefix, FILE *file) {
  for (; *tmpl; tmpl++)
      fprintf(file, "%s%s\n", prefix, *tmpl);
}

void break_func(void) { }

void expand(sample_producer *node, char **tmpl, GList *order,
	    char *prefix, FILE *file) {
  sample_producer **children = node->get_children(node);
  int node_i = g_list_index(order, (gpointer)node);
  for (; *tmpl; tmpl++) {
    char buf[1024];
    char *sp = *tmpl, *dp = buf;
    dp += sprintf(dp, prefix);

    while (*sp) {
      if (*sp == '$') {
	switch (*++sp) {
	  char nbuf[8], *np;

	  /*
	    case '^':
	    break_func();
	    sp++;
	    break;
	  */

	case 'i':
	  np = nbuf;
	  while (isdigit(*++sp))
	    *np++ = *sp;
	  *np = '\0';
	  dp += sprintf(dp, "samp%d",
			g_list_index(order, (gpointer)children[atoi(nbuf)]));
	  break;

	case 'n':
	  dp += sprintf(dp, "n%d", node_i);
	  sp++;
	  break;

	case 'o':
	  dp += sprintf(dp, "samp%d", node_i);
	  sp++;
	  break;

	case 't':
	  dp += sprintf(dp, "sp[%d]", node_i);
	  sp++;
	  break;
	}
      }
      else
	*dp++ = *sp++;
    }
    *dp++ = '\n';
    *dp = '\0';
    fprintf(file, buf);
  }
}

void fuse_loops(sample_producer *left, sample_producer *right, FILE *file) {
  GList *order = NULL;
  GList *gl;
  int i, sampL, sampR;

  topo_sort(left, &order);
  topo_sort(right, &order);

  fprintf(file, "/* generated file -- don't edit */\n");
  fprintf(file, "#include <unistd.h>\n");
  fprintf(file, "#include <math.h>\n");
  fprintf(file, "#include \"freebirth.h\"\n");
  fprintf(file, "\n");

  fprintf(file, "sample_producer *sp[%d];\n", g_list_length(order));
  fprintf(file, "\n");

  fprintf(file, "int play_buffer(gpointer data, gint source, GdkInputCondition condition)\n");
  fprintf(file, "{\n");
  fprintf(file, "  int i;\n");
  fprintf(file, "  short buffer[TBASS_BUFF_SIZE * 2];\n");
  fprintf(file, "  \n");

  for (gl = order, i=0; gl; gl = g_list_next(gl), i++)
    fprintf(file, "  sample samp%d;\n", i);
  fprintf(file, "\n");

  expand_seq(seq_get_header(sequencer), "  ", file);
  fprintf(file, "\n");
  for (gl = order; gl; gl = g_list_next(gl)) {
    sample_producer *node = (sample_producer *)gl->data;
    expand(node, node->get_header(node), order, "  ", file);
    fprintf(file, "\n");
  }

  fprintf(file, "  for (i = 0; i < TBASS_BUFF_SIZE; i++) {\n");
  expand_seq(seq_get_code(sequencer), "    ", file);
  fprintf(file, "\n");
  for (gl = order; gl; gl = g_list_next(gl)) {
    sample_producer *node = (sample_producer *)gl->data;
    expand(node, node->get_code(node), order, "    ", file);
    if (g_list_next(gl)) fprintf(file, "\n");
  }

  sampL = g_list_index(order, (gpointer)left);
  sampR = g_list_index(order, (gpointer)right);
  fprintf(file, "    if ((samp%d < CLIP) && (samp%d >-CLIP))\n", sampL, sampL);
  fprintf(file, "      buffer[2 * i] = (short)samp%d;\n", sampL);
  fprintf(file, "    else if (samp%d > 0)\n", sampL);
  fprintf(file, "      buffer[2 * i] = (short)MIX_MAX_AMP-(CLIP_A/(CLIP_B+samp%d));\n", sampL);
  fprintf(file, "    else\n");
  fprintf(file, "      buffer[2 * i] = (short)-(MIX_MAX_AMP-(CLIP_A/(CLIP_B-samp%d)));\n", sampL);
  fprintf(file, "    if ((samp%d < CLIP) && (samp%d >-CLIP))\n", sampR, sampR);
  fprintf(file, "      buffer[2 * i + 1] = (short)samp%d;\n", sampR);
  fprintf(file, "    else if (samp%d > 0)\n", sampR);
  fprintf(file, "      buffer[2 * i + 1] = (short)MIX_MAX_AMP-(CLIP_A/(CLIP_B+samp%d));\n", sampR);
  fprintf(file, "    else\n");
  fprintf(file, "      buffer[2 * i + 1] = (short)-(MIX_MAX_AMP-(CLIP_A/(CLIP_B-samp%d)));\n", sampR);
  fprintf(file, "  }\n");
  fprintf(file, "\n");

  fprintf(file, "  write(fd, buffer, 2 * TBASS_BUFF_SIZE * sizeof(short));\n");
  fprintf(file, "\n");

  expand_seq(seq_get_footer(sequencer), "  ", file);
  fprintf(file, "\n");
  for (gl = order; gl; gl = g_list_next(gl)) {
    sample_producer *node = (sample_producer *)gl->data;
    expand(node, node->get_footer(node), order, "  ", file);
    fprintf(file, "\n");
  }
  fprintf(file, "  return 1;\n");
  fprintf(file, "}\n");
}

/* dummy so fusebirth links */
int play_buffer()
{
  return 0;
}
