/*  sexyPSF - PSF1 player
 *  Copyright (C) 2002-2004 xodnizel
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "audacious/plugin.h"
#include "audacious/output.h"
#include "libaudacious/util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "driver.h"

#define CMD_SEEK	0x80000000
#define CMD_STOP	0x40000000

#define uint32 u32
#define int16 short

static volatile uint32 command;
static volatile int playing=0;

static PSFINFO *PSFInfo=NULL;

extern InputPlugin sexypsf_ip;
static char *fnsave=NULL;

InputPlugin *get_iplugin_info(void)
      {
         sexypsf_ip.description = "sexyPSF PSF1 Player 0.4.8";
         return &sexypsf_ip;
      }
/*
static void sexypsf_init(void)
{
}
static void sexypsf_about(void)
{
}
static void sexypsf_config(void)
{
}
*/

static int sexypsf_xmms_testfile(char *fn)
{
 char buf[4];
 char *tmps;
 static const char *teststr="psflib";
 FILE *fp;
 if(!strncasecmp(fn,"http://",7)) return(0); // Don't handle http://blahblah

 /* Filter out psflib files */
 if(strlen(teststr) < strlen(fn))
 {
  tmps=fn+strlen(fn);
  tmps-=strlen(teststr);
  if(!strcasecmp(tmps,teststr))
   return(0);
 }

 if(!(fp=fopen(fn,"rb"))) return(0);
 if(fread(buf,1,4,fp)!=4) {fclose(fp);return(0);}
 fclose(fp);
 if(memcmp(buf,"PSF\x01",4))	// Only allow for PSF1 files for now.
   return 0;
 return(1);
}

static char *MakeTitle(PSFINFO *info)
{
 char *ret;

 ret=malloc( (info->game?strlen(info->game):0) + (info->title?strlen(info->title):0) + 3 + 1);
 sprintf(ret,"%s - %s",info->game?info->game:"",info->title?info->title:"");

 return(ret);
}

static void SI(void)
{
 char *tmp;
 tmp=MakeTitle(PSFInfo);
 sexypsf_ip.set_info(tmp,PSFInfo->length,44100*2*2*8,44100,2);
 free(tmp);
}

static GThread *dethread;
void sexypsf_update(unsigned char *Buffer, long count)
{
 int mask = ~((((16 / 8) * 2)) - 1);

 while(count>0)
 {
  int t=sexypsf_ip.output->buffer_free() & mask;
  if(t>count)
  {
   produce_audio(sexypsf_ip.output->written_time(), FMT_S16_NE, 2, count, Buffer, NULL);
  }
  else
  {
   if(t)
    produce_audio(sexypsf_ip.output->written_time(), FMT_S16_NE, 2, t, Buffer, NULL);
   usleep((count-t)*1000*5/441/2);
  }
  count-=t;
  Buffer+=t;
 }
 if(command&CMD_SEEK)
 {
  int t=(command&~(CMD_SEEK|CMD_STOP))*1000;

  if(sexypsf_seek(t))
   sexypsf_ip.output->flush(t);
  else	// Negative time!  Must make a C time machine.
  {
   sexypsf_stop();
   return;
  }
  command&=~CMD_SEEK;
 }
 if(command&CMD_STOP)
 {
  sexypsf_stop();
 }
}
static volatile int nextsong=0;

static void *sexypsf_playloop(void *arg)
{
 dofunky:

 sexypsf_execute();

 /* We have reached the end of the song. Now what... */
 sexypsf_ip.output->buffer_free();
 sexypsf_ip.output->buffer_free();

 while(!(command&CMD_STOP)) 
 {
  if(command&CMD_SEEK)
  {
   int t=(command&~(CMD_SEEK|CMD_STOP))*1000;
   sexypsf_ip.output->flush(t);
   if(!(PSFInfo=sexypsf_load(fnsave)))
    break;
   sexypsf_seek(t); 
   command&=~CMD_SEEK;
   goto dofunky;
  }
  if(!sexypsf_ip.output->buffer_playing()) break;
  usleep(2000);
 }
 sexypsf_ip.output->close_audio();
 if(!(command&CMD_STOP)) nextsong=1;
 g_thread_exit(NULL);
 return(NULL);
}

static int paused;
static void sexypsf_xmms_play(char *fn)
{
 if(playing)
  return;
 nextsong=0;
 paused = 0;
 if(!sexypsf_ip.output->open_audio(FMT_S16_NE, 44100, 2))
 {
  puts("Error opening audio.");
  return;
 }
 fnsave=malloc(strlen(fn)+1);
 strcpy(fnsave,fn);
 if(!(PSFInfo=sexypsf_load(fn)))
 {
  sexypsf_ip.output->close_audio();
  nextsong=1;
 }
 else
 {
  command=0;
  SI();
  playing=1;
  dethread = g_thread_create((GThreadFunc)sexypsf_playloop,NULL,TRUE,NULL);
 }
}

static void sexypsf_xmms_stop(void)
{
 if(!playing) return;

 if(paused)
  sexypsf_ip.output->pause(0);
 paused = 0;

 command=CMD_STOP;
 g_thread_join(dethread);
 playing = 0;

 if(fnsave)
 {
  free(fnsave);
  fnsave=NULL;
 } 
 sexypsf_freepsfinfo(PSFInfo);
 PSFInfo=NULL;
}

static void sexypsf_xmms_pause(short p)
{
 if(!playing) return;
 sexypsf_ip.output->pause(p);
 paused = p;
}

static void sexypsf_xmms_seek(int time)
{
 if(!playing) return;
 command=CMD_SEEK|time;
}

static int sexypsf_xmms_gettime(void)
{
 if(nextsong)
  return(-1);
 if(!playing) return(0);
 return sexypsf_ip.output->output_time();
}

static void sexypsf_xmms_getsonginfo(char *fn, char **title, int *length)
{
  PSFINFO *tmp;

  if((tmp=sexypsf_getpsfinfo(fn)))
  {
   *length=tmp->length;
   *title=MakeTitle(tmp);
   sexypsf_freepsfinfo(tmp);
  }
}

InputPlugin sexypsf_ip =
{
 0,
 0,
 "Plays PSF1 files.",
 0,
 0,
 0,
 sexypsf_xmms_testfile,
 0,
 sexypsf_xmms_play,
 sexypsf_xmms_stop,
 sexypsf_xmms_pause,
 sexypsf_xmms_seek,
 0,
 sexypsf_xmms_gettime,
 0,
 0,
 0,
 0,
 0,
 0,
 0,
 sexypsf_xmms_getsonginfo,
 0,
 0
};
