/*
 * Stonage: a puzzle game
 * Copyright (C) 2001 Luc Saillard <luc@saillard.org>
 * 
 * Functions to play module using the mikmod library.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 */

#include "stonage.h"
#include "music.h"

#ifdef ENABLE_SOUND

#include <errno.h>
#include <unistd.h>
#include <mikmod.h>
#include <pthread.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include "game.h"

static MODULE *module=NULL;
static pthread_t sound_thread;
static int sound_initialised=0;

static volatile int want_to_stop_this_music;
pthread_mutex_t mutex_module_access;

/* This is a hack, because when oss play sound, mikmod wait
 * unlimit time that the user release the lock on the device.
 * With, this hack, if the device is blocked, we don't wait more than 5
 * secondes.
 */
#define HACK_OSS_OPEN

#ifdef HACK_OSS_OPEN
#include <setjmp.h>
#include <signal.h>

static jmp_buf sound_setjmp_buffer;

static void cant_initialize_sound(int num)
{
  longjmp(sound_setjmp_buffer,1);
}

#endif

/**
 *
 * This is the tread which play music.
 *
 */
static void *Music_Thread(void *arg)
{
  Player_Start(module);
  want_to_stop_this_music=0;
  while (1)
   {
     while(Player_Active())
      {
	/* we're playing */
	usleep(10000);
	MikMod_Update();
      }
     /* howto detect, if this is the end of the module, and we want another music,
      * or if the call a Music_Stop(), and we wait that this thread exit. */
     if (want_to_stop_this_music)
       break;
     Music_Free();
     Music_Load_Module_Random("music");
     Player_Start(module);
   }
  return NULL;
}


/**
 *
 * Initialise Sound System
 *
 */
int Music_Init(void)
{
  MikMod_RegisterAllDrivers();
  MikMod_RegisterAllLoaders();
#ifdef HACK_OSS_OPEN
  if (setjmp(sound_setjmp_buffer)) {
     /* If we get here, mikmod can't be initialized */
     return 0;
  }
  signal(SIGALRM,cant_initialize_sound);
  alarm(5); /* Wait 5 secs */
#endif
  if (MikMod_Init(""))
   {
     fprintf(stderr,"Could not initialize sound, reason: %s\n",
	 MikMod_strerror(MikMod_errno));
     return 0;
   }
#ifdef HACK_OSS_OPEN
  signal(SIGALRM,SIG_IGN);
#endif
  pthread_mutex_init(&mutex_module_access,NULL);
  sound_initialised=1;
  return 1;
}


/**
 *
 * Free ressource use by sound system
 *
 */
void Music_Free(void)
{
  pthread_mutex_lock(&mutex_module_access);
  if (module)
    Player_Free(module);
  module=NULL;
  pthread_mutex_unlock(&mutex_module_access);
}

/**
 *
 * Release the sound system layer
 * 
 */
void Music_Release(void)
{
  if (sound_initialised)
    MikMod_Exit();
  pthread_mutex_destroy(&mutex_module_access);
  sound_initialised=0;
}

/**
 *
 * Play Music
 *
 */
int
Music_Start(void)
{
  trace (" ");
  if (sound_initialised && module)
   {
     if (pthread_create(&sound_thread,NULL,Music_Thread,NULL))
      {
	fprintf(stderr,"Music_Start: Error when creating thread\n");
	return 0;
      }
     return 1;
   }
  return 0;
}

/**
 *
 * Stop Playing Music
 *
 */
void
Music_Stop(void)
{
  pthread_mutex_lock(&mutex_module_access);
  if (module && sound_initialised)
    Player_Stop();
  want_to_stop_this_music=1;
  pthread_mutex_unlock(&mutex_module_access);
  if (sound_thread)
    pthread_join(sound_thread,NULL);
}

/**
 *
 * load a new module in memory
 *
 *
 */
int
Music_Load_Module(char *path)
{
  if (sound_initialised)
   {
     module=Player_Load(path,64,0);
     if (module==NULL)
       return 0;
     return 1;
   }
  return 0;
}

/**
 *
 * load a new mp3 in memory
 *
 */
int
Music_Load_Mp3(char *path)
{
  if (sound_initialised)
   {
   }
  return 0;
}



/**
 *
 * Load a random module from a directory.
 * 
 */

int
Music_Load_Module_Random(const char *directory)
{
  int n,i,j,res;
  DIR *d;
  struct dirent *dir;
  char path[1024];
  char realdirectory[1024];

  i=n=0;

  while (datadir_path[i])
   {
     snprintf(realdirectory,1023,"%s/%s",datadir_path[i],directory);
     trace("Trying to open directory %s",realdirectory);

     d=opendir(realdirectory);
     if (d!=NULL)
       break;
     trace(_("Can't open directory %s (%s)"),realdirectory,strerror(errno));
     i++;
   }
  if (datadir_path[i]==NULL)
   {
     trace("Found no valid directory to load modules");
     return 0;
   }

  while ((dir=readdir(d))!=NULL)
   {
     if (dir->d_name[0]=='.')
       continue;
     /* We don't accept Makefile has a file module :) */
     if (strstr(dir->d_name,"Makefile"))
       continue;
     if (strstr(dir->d_name,".ym"))
       continue;

#ifdef ENABLE_DEBUG_TRACE
     snprintf(path,1023,"%s/%s",realdirectory,dir->d_name);
     trace("%s",path);
#endif
     n++;
   }

  rewinddir(d);

  j=1 + RANDOM()%n;

  trace("Module N°%d/%d",j,n);

  res=0;
  while (j && (dir=readdir(d))!=NULL)
   {
     if (dir->d_name[0]=='.')
       continue;
     /* We don't accept Makefile has a file module :) */
     if (strstr(dir->d_name,"Makefile"))
       continue;
     if (strstr(dir->d_name,".ym"))
       continue;
#ifdef ENABLE_DEBUG_TRACE
     snprintf(path,1023,"%s/%s",realdirectory,dir->d_name);
#endif
     if (--j==0)
      {
	snprintf(path,1023,"%s/%s",realdirectory,dir->d_name);
	trace("%s",path);
	if (strstr(path,".mp3")==NULL)
	  res = Music_Load_Module(path);
	else
	  res = Music_Load_Mp3(path);
      }
   }
  closedir(d);

  return res;


}

#else /* #if ENABLE_SOUND */

int Music_Init(void) { return 1; }
void Music_Free(void) {}
void Music_Release(void) {}
int Music_Start(void) { return 1; }
void Music_Stop(void) {}
int Music_Load_Module(char *path) {return 1;}
int Music_Load_Mp3(char *path) { return 1; }
int Music_Load_Module_Random(const char *directory) {return 1;}


#endif /* #if ENABLE_SOUND */

