#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "appenv.h"
#include "app_procs.h"
#include "batch.h"
#include "procedural_db.h"


static int  batch_is_cmd   (char              *cmd);
static void batch_run_cmd  (char              *cmd);
static void batch_run_cmds (FILE              *fp);
static void batch_read     (gpointer           data,
			    gint               source,
			    GdkInputCondition  condition);


void
batch_init ()
{
  extern char **batch_cmds;

  FILE *fp;
  int read_from_stdin;
  int i;

  read_from_stdin = FALSE;
  for (i = 0; batch_cmds[i]; i++)
    {
      if (strcmp (batch_cmds[i], "-") == 0)
	{
	  if (!read_from_stdin)
	    {
	      g_print ("reading batch commands from stdin\n");
	      gdk_input_add (STDIN_FILENO, GDK_INPUT_READ, batch_read, NULL);
	      read_from_stdin = TRUE;
	    }
	}
      else if (batch_is_cmd (batch_cmds[i]))
	{
	  batch_run_cmd (batch_cmds[i]);
	}
      else
	{
	  fp = fopen (batch_cmds[i], "r");
	  if (!fp)
	    g_print ("unable to open batch file: \"%s\"\n", batch_cmds[i]);

	  batch_run_cmds (fp);
	  fclose (fp);
	}
    }
}


static int
batch_is_cmd (char *cmd)
{
  int paren_level;

  if (!cmd)
    return FALSE;

  cmd = strchr (cmd, '(');
  if (!cmd)
    return FALSE;
  cmd += 1;

  paren_level = 1;

  while (*cmd)
    {
      if (*cmd == ')')
	paren_level -= 1;
      else if (*cmd == '(')
	paren_level += 1;
      cmd++;
    }

  return (paren_level == 0);
}

static void
batch_run_cmd (char *cmd)
{
  ProcRecord *proc;
  Argument *args;
  Argument *vals;
  char *cmdname;
  char *t;
  int i;

  cmd = strchr (cmd, '(');
  if (!cmd)
    return;
  cmd += 1;

  cmdname = cmd;
  cmd = strtok (cmd, " )");
  if (!cmd)
    return;

  t = cmdname;
  while (*t)
    {
      if (*t == '-')
	*t = '_';
      t++;
    }

  proc = procedural_db_lookup (cmdname);
  if (!proc)
    {
      g_print ("could not find procedure: \"%s\"\n", cmdname);
      return;
    }

  /* (gimp-procedural-db-dump "/tmp/pdb_dump") */
  args = g_new (Argument, proc->num_args);

  for (i = 0; i < proc->num_args; i++)
    {
      args[i].arg_type = proc->args[i].arg_type;

      switch (proc->args[i].arg_type)
	{
	case PDB_INT32:
	case PDB_INT16:
	case PDB_INT8:
	  cmd = strtok (NULL, " \t)");
	  if (!cmd)
	    goto error;

	  args[i].value.pdb_int = atoi (cmd);
	  break;
	case PDB_FLOAT:
	  cmd = strtok (NULL, " \t)");
	  if (!cmd)
	    goto error;

	  args[i].value.pdb_float = atof (cmd);
	  break;
	case PDB_STRING:
	  cmd = strtok (NULL, "\"");
	  if (!cmd)
	    goto error;

	  args[i].value.pdb_pointer = g_strdup (cmd);
	  break;
	case PDB_INT32ARRAY:
	case PDB_INT16ARRAY:
	case PDB_INT8ARRAY:
	case PDB_FLOATARRAY:
	case PDB_STRINGARRAY:
	  g_print ("procedures taking array arguments are currently not supported as batch operations\n");
	  goto error;
	case PDB_COLOR:
	  g_print ("procedures taking color arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_REGION:
	  g_print ("procedures taking region arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_DISPLAY:
	  g_print ("procedures taking display arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_IMAGE:
	  g_print ("procedures taking image arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_LAYER:
	  g_print ("procedures taking layer arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_CHANNEL:
	  g_print ("procedures taking channel arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_DRAWABLE:
	  g_print ("procedures taking drawable arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_SELECTION:
	  g_print ("procedures taking selection arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_BOUNDARY:
	  g_print ("procedures taking boundary arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_PATH:
	  g_print ("procedures taking path arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_STATUS:
	  g_print ("procedures taking status arguments are currently not supported as batch operations\n");
	  goto error;
	  break;
	case PDB_END:
	  break;
	}
    }

  vals = procedural_db_execute (proc->name, args);
  switch (vals[0].value.pdb_int)
    {
    case PDB_EXECUTION_ERROR:
      g_print ("batch command: %s experienced an execution error.\n", cmdname);
      break;
    case PDB_CALLING_ERROR:
      g_print ("batch command: %s experienced a calling error.\n", cmdname);
      break;
    case PDB_SUCCESS:
      g_print ("batch command: %s executed successfully.\n", cmdname);
      break;
    }

  return;

error:
  g_print ("Unable to run batch command: %s because of bad arguments.\n", cmdname);
}

static void
batch_run_cmds (FILE *fp)
{
}

static void
batch_read (gpointer          data,
	    gint              source,
	    GdkInputCondition condition)
{
  static GString *string;
  char buf[32], *t;
  int nread, done;

  if (condition & GDK_INPUT_READ)
    {
      do {
	nread = read (source, &buf, sizeof (char) * 31);
      } while ((nread == -1) && ((errno == EAGAIN) || (errno == EINTR)));

      if ((nread == 0) && (!string || (string->len == 0)))
	app_exit (FALSE);

      buf[nread] = '\0';

      if (!string)
	string = g_string_new ("");

      t = buf;
      if (string->len == 0)
	{
	  while (*t)
	    {
	      if (isspace (*t))
		t++;
	      else
		break;
	    }
	}

      g_string_append (string, t);

      done = FALSE;

      while (*t)
	{
	  if ((*t == '\n') || (*t == '\r'))
	    done = TRUE;
	  t++;
	}

      if (done && batch_is_cmd (string->str))
	{
	  batch_run_cmd (string->str);
	  g_string_truncate (string, 0);
	}
    }
}
