/* 
 * Copyright (c) 1997 Guylhem Aznar <guylhem@oeil.qc.ca>
 * Copyright (C) 1994 Mark Boyns <boyns@sdsu.edu>
 * Copyright (C) 1994 Mark Scott <mscott@mcd.mot.com>
 * Copyright (C) 1994 Szijarto Szabolcs <saby@sch.bme.hu>
 * Copyright (C) 1994 Robert Nation
 *
 * 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; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * afterstep includes:
 */
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#define MODULE_X_INTERFACE

#include "../../configure.h"
#include "../../include/aftersteplib.h"
#include "../../include/afterstep.h"
#include "../../include/style.h"
#include "../../include/screen.h"
#include "../../include/module.h"

/*
 * rplay includes:
 */
#ifdef HAVE_RPLAY
#include <rplay.h>
#endif

#define BUILTIN_STARTUP		MAX_MESSAGES
#define BUILTIN_SHUTDOWN	MAX_MESSAGES+1
#define BUILTIN_UNKNOWN		MAX_MESSAGES+2
#define MAX_BUILTIN		3

/* globals */
char *MyName = NULL;
ScreenInfo Scr;
Display *dpy;			/* which display are we talking to */
int screen;
static int fd_width;
static int fd[2];
static int x_fd;
char audio_play_cmd_line[MAXLINELENGTH], audio_play_dir[MAXLINELENGTH];
time_t audio_delay = 0;		/* seconds */
#ifdef HAVE_RPLAY
int rplay_fd = -1;
#endif

/* Event mask - we want all events */
#define mask_reg MAX_MASK

/* prototypes */
void Loop (int *);
void process_message (unsigned long, unsigned long *);
void DeadPipe (int);
void ParseOptions (const char *);
void done (int);
int audio_play (short);

/* define the message table */
char *messages[MAX_MESSAGES + MAX_BUILTIN] =
{
  "toggle_paging",
  "new_page",
  "new_desk",
  "add_window",
  "raise_window",
  "lower_window",
  "configure_window",
  "focus_change",
  "destroy_window",
  "iconify",
  "deiconify",
  "window_name",
  "icon_name",
  "res_class",
  "res_name",
  "end_windowlist",
  "icon_location",
  "map",
  "shade",
  "unshade",
  "lockonsend",
  "new_background",
/* add builtins here */
  "startup",
  "shutdown",
  "unknown",
};

/* define the sound table  */
char *sound_table[MAX_MESSAGES + MAX_BUILTIN];

#ifdef HAVE_RPLAY
/* define the rplay table */
RPLAY *rplay_table[MAX_MESSAGES + MAX_BUILTIN];
#endif

void usage(void)
{
  printf("Usage:\n"
	 "%s [--version] [--help]\n", MyName);
  exit(0);
}

/* error_handler:
 * catch X errors, display the message and continue running.
 */
int error_handler (Display *disp, XErrorEvent *event)
{
  fprintf (stderr, "%s: internal error, error code %d, request code %d, minor code %d.\n", 
           MyName, event->error_code, event->request_code, event->minor_code);
    return 0;
}

int
main (int argc, char **argv)
{
  int i;
  char *global_config_file = NULL;
  char *display_name = NULL;

  /* Save our program name - for error messages */
  SetMyName(argv[0]);

  i = ProcessModuleArgs( argc, argv, &(global_config_file), NULL, NULL, usage);

  /* Dead pipe == AS died */
  signal (SIGPIPE, DeadPipe);
  signal (SIGQUIT, DeadPipe);
  signal (SIGSEGV, DeadPipe);
  signal (SIGTERM, DeadPipe);

  x_fd = ConnectX(&Scr, display_name, PropertyChangeMask );
  XSetErrorHandler (error_handler);

  /* connect to AfterStep */
  fd_width = GetFdWidth ();
  fd[0] = fd[1] = ConnectAfterStep(mask_reg);

  audio_play_dir[0] = '\0';
  audio_play_cmd_line[0] = '\0';

  LoadConfig(global_config_file,"audio", ParseOptions );

  /*
   * Play the startup sound.
   */
  audio_play (BUILTIN_STARTUP);
  SendInfo (fd, "Nop", 0);
  Loop (fd);

  return 0;
}

/***********************************************************************
 *
 *  Procedure:
 *	ParseOptions - read the sound configuration file.
 *
 ***********************************************************************/
void
ParseOptions (const char *config_file)
{
  FILE *fp;
  char *buf;
  char *message;
  char *sound;
  char *p, *tline;
  int i, found, len;
#ifdef HAVE_RPLAY
  char host[128];
  int volume = RPLAY_DEFAULT_VOLUME;
  int priority = RPLAY_DEFAULT_PRIORITY;
#endif

  if ((fp = fopen (config_file, "r")) == NULL)
    {
      done (1);
    }
  /*
   * Intialize all the sounds.
   */
  for (i = 0; i < MAX_MESSAGES + MAX_BUILTIN; i++)
    {
      sound_table[i] = NULL;
#ifdef HAVE_RPLAY
      rplay_table[i] = NULL;
#endif
    }
#ifdef HAVE_RPLAY
  strcpy (host, rplay_default_host ());
#endif

  buf = (char *) safemalloc (MAXLINELENGTH);
  len = strlen (MyName);
  while ((tline = fgets (buf, MAXLINELENGTH, fp)) != NULL)
    {
      /*
       * Search for *ASAudio.
       */
      if ((*tline == '*') && (!mystrncasecmp (tline + 1, MyName, len)))
	{
	  tline += len + 1;

	  /* strip leading whitespace */
	  for (; isspace (*tline); tline++);

	  /* strip trailing whitespace */
	  for (p = tline + strlen (tline); p > tline && isspace (*(p - 1)); p--);
	  *p = '\0';

	  /* get command */
	  p = tline;

	  if (!mystrncasecmp (p, "PlayCmd", 7))
	    {
	      for (p += 7; isspace (*p); p++);
	      strcpy (audio_play_cmd_line, p);
	    }
	  else if (!mystrncasecmp (p, "Path", 4))
	    {
	      for (p += 4; isspace (*p); p++);
	      strcpy (audio_play_dir, p);
	    }
	  else if (!mystrncasecmp (p, "Delay", 5))
	    {
	      for (p += 5; isspace (*p); p++);
	      audio_delay = atoi (p);
	    }
#ifdef HAVE_RPLAY
	  /*
	   * Check for rplay configuration options.
	   */
	  else if (!mystrncasecmp (p, "RplayHost", 9))
	    {
	      for (p += 9; isspace (*p); p++);
	      if (*p != '\0')
		{
		  /*
		   * Check for environment variables like $HOSTDISPLAY.
		   */
		  if (*p == '$')
		    {
		      char *c1, *c2;
		      c2 = host;
		      for (c1 = (char *) getenv (p + 1); *c1 && (*c1 != ':'); c1++)
			{
			  *c2++ = *c1;
			}
		      *c2 = '\0';
		    }
		  else
		    {
		      strcpy (host, p);
		    }
		}
	    }
	  else if (!mystrncasecmp (p, "RplayVolume", 11))
	    {
	      for (p += 11; isspace (*p); p++);
	      volume = atoi (p);
	    }
	  else if (!mystrncasecmp (p, "RplayPriority", 13))
	    {
	      for (p += 13; isspace (*p); p++);
	      priority = atoi (p);
	    }
#endif
	  /*
	   * Audio <message_type> <audio_file>
	   */
	  else
	    {
	      message = p;
	      for (; *p != '\0' && !isspace (*p); p++);
	      sound = p;
	      if (*p != '\0')
		{
		  *p = '\0';
		  for (p++; isspace (*p); p++);
		  sound = p;
		}

	      if (!message || !*message || !sound || !*sound)
		{
		  continue;
		}
	      found = 0;

	      for (i = 0; !found && i < MAX_MESSAGES + MAX_BUILTIN; i++)
		{
		  if (!mystrncasecmp (message, messages[i], strlen (message)))
		    {
#ifdef HAVE_RPLAY
		      rplay_table[i] = rplay_create (RPLAY_PLAY);
		      rplay_set (rplay_table[i], RPLAY_APPEND,
				 RPLAY_SOUND, sound,
				 RPLAY_PRIORITY, priority,
				 RPLAY_VOLUME, volume,
				 NULL);
#endif


		      sound_table[i] = mystrdup (sound);
		      found++;
		    }
		}
	    }
	}
    }
  free (buf);
  fclose (fp);

#ifdef HAVE_RPLAY
  /*
   * Builtin rplay support is enabled when AudioPlayCmd == builtin-rplay.
   */
  if (!mystrcasecmp (audio_play_cmd_line, "builtin-rplay"))
    {
      rplay_fd = rplay_open (host);
      if (rplay_fd < 0)
	{
	  rplay_perror ("rplay_open");
	  done (1);
	}
    }
#endif
}

/***********************************************************************
 *
 *  Procedure:
 *	Loop - wait for data to process
 *
 ***********************************************************************/
static int last_time = 0;
void Loop (int *fd)
{
	time_t now = 0;
	unsigned long code;
	ASMessage * asmsg = NULL;

	while (1) {
		asmsg = CheckASMessage( fd[1], -1 );
		if ( asmsg ) {
			/*
			 * Ignore messages that occur during the delay
			 * period.
			 */
			now = time (0);
			if (now < (last_time + audio_delay)) {
				continue;
			}

			if (asmsg->header[0] == START_FLAG) {

				/*
				 * code will equal the number of shifts in the
				 * base-2 header[1] number.  Could use log here
				 * but this should be fast enough.
				 */
				code = -1;
				while (asmsg->header[1]) {
					code++;
					asmsg->header[1] >>= 1;
				}

				/*
				 * Play the sound.
				 */
				if (code >= 0 && code < MAX_MESSAGES) 
				{
				   if (sound_table[code] != False)
				      { audio_play (code);
				        last_time = now;
			              }
			            } 
			        else 
			            {
				    audio_play (BUILTIN_UNKNOWN);
				    last_time = now;
				    }

				DestroyASMessage(asmsg);
			}
		}
	}
}


/***********************************************************************
 *
 *  Procedure:
 *	SIGPIPE handler - SIGPIPE means afterstep is dying
 *
 ***********************************************************************/
void
DeadPipe (int nonsense)
{
  done (0);
}

/***********************************************************************
 *
 *  Procedure:
 *	done - common exit point for Audio.
 *
 ***********************************************************************/
void
done (int n)
{
  audio_play (BUILTIN_SHUTDOWN);
  /* printf("shutdown \n"); */
  exit (n);
}

/***********************************************************************
 *
 * Procedure:
 *
 *    audio_play - actually plays sound from lookup table
 *
 **********************************************************************/
int
audio_play (short sound)
{
  static char buf[MAXLINELENGTH];

#ifdef HAVE_RPLAY
  if (rplay_fd != -1)
    {
      if (rplay_table[sound])
	{
	  if (rplay (rplay_fd, rplay_table[sound]) < 0)
	    {
	      rplay_perror ("rplay");
	    }
	}
      return 0;
    }
#endif

  if (sound_table[sound])
    {
      memset (buf, 0, MAXLINELENGTH);

      /*
       * Don't use audio_play_dir if it's NULL or if the sound file
       * is an absolute pathname.
       */
      if (audio_play_dir[0] == '\0' || sound_table[sound][0] == '/')
	{
	  sprintf (buf, "%s %s", audio_play_cmd_line, sound_table[sound]);
	}
      else
	{
	  sprintf (buf, "%s %s/%s", audio_play_cmd_line, audio_play_dir,
		   sound_table[sound]);
	}
      return system (buf);
    }

  return (sound_table[sound] != NULL);
}
