/* 
xmms-projectM v0.99 - xmms-projectm.sourceforge.net
--------------------------------------------------

Lead Developers:  Carmelo Piccione (cep@andrew.cmu.edu) &
                  Peter Sperl (peter@sperl.com)

We have also been advised by some professors at CMU, namely Roger B. Dannenberg.
http://www-2.cs.cmu.edu/~rbd/    
  
The inspiration for this program was Milkdrop by Ryan Geiss. Obviously. 

This code is distributed under the GPL.


THANKS FOR THE CODE!!!
-------------------------------------------------
The base for this program was andy@nobugs.org's XMMS plugin tutorial
http://www.xmms.org/docs/vis-plugin.html

We used some FFT code by Takuya OOURA instead of XMMS' built-in fft code
fftsg.c - http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html

For font rendering we used GLF by Roman Podobedov
glf.c - http://astronomy.swin.edu.au/~pbourke/opengl/glf/

and some beat detection code was inspired by Frederic Patin @
www.gamedev.net/reference/programming/features/beatdetection/

*/


#include <stdio.h>
#include <audacious/plugin.h>
#include <string.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <audacious/util.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <audacious/auddrct.h>
#include <math.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <projectM/projectM.h>
#include <projectM/console_interface.h>
#include "sdltoprojectM.h"
#include "video_init.h"

#include <config.h>

#define CONFIG_FILE "/config"
#define PRESETS_DIR "/presets"
#define FONTS_DIR "/fonts"

// Forward declarations 
static void projectM_xmms_init(void); 
static void projectM_cleanup(void);
static void projectM_about(void);
static void projectM_configure(void);
static void projectM_playback_start(void);
static void projectM_playback_stop(void);
static void projectM_render_pcm(gint16 pcm_data[2][512]);
static void projectM_render_freq(gint16 pcm_data[2][256]);
void read_config();


//extern preset_t * active_preset;

// Callback functions
VisPlugin projectM_vtable = {
  .description = "projectM v0.99",       // description
  .num_pcm_chs_wanted = 2, // # of PCM channels for render_pcm()
  .num_freq_chs_wanted = 0, // # of freq channels wanted for render_freq()
  .init = projectM_xmms_init,     // Called when plugin is enabled
  .cleanup = projectM_cleanup,        // Called when plugin is disabled
  .about = projectM_about,          // Show the about box
  .configure = projectM_configure,      // Show the configure box
  .playback_start = projectM_playback_start, // Called when playback starts
  .playback_stop = projectM_playback_stop,  // Called when playback stops
  .render_pcm = projectM_render_pcm,      // Render the PCM data, must return quickly
  .render_freq = projectM_render_freq     // Render the freq data, must return quickly
};

VisPlugin *projectM_vplist[] = { &projectM_vtable, NULL };

DECLARE_PLUGIN(projectm, NULL, NULL, NULL, NULL, NULL, NULL, projectM_vplist,NULL);

// Our worker thread
SDL_Thread *worker_thread;

SDL_mutex *mutex;

SDL_sem *sem;

SDL_Event event;

SDL_Surface *screen;
//SDL_RenderTarget *RenderTarget = NULL;
//GLuint RenderTargetTextureID;

projectM_t *globalPM = NULL;

int maxsamples=512;

int texsize=512;
int gx=32,gy=24;
int wvw=640,wvh=480;
int fvw=1280,fvh=960;
int fps=30, fullscreen=0;
char *disp;

int disable_projectm(void) {
	projectM_vtable.disable_plugin(&projectM_vtable);
	return 0;
}

int get_xmms_title(void) {
	static char check_title = 1;
	static int last_pos;
	static char *last_title = NULL;
	int pos;
	char *title = NULL;

	//Nice optimization, but we want the title no matter what so I can display it when the song changes
#if 0
	if(!(globalPM->showtitle%2)) {
		/* Repeat less often when not showing title */
		return 1000;
	}
#endif

	pos = audacious_drct_pl_get_pos();
	/* Only check every 1 second for title change, otherwise check pos */
	if(check_title || pos != last_pos) {
		title = audacious_drct_pl_get_title(pos);
		if(title && (!last_title || strcmp(last_title,title))) {
			globalPM->title = title;
			globalPM->drawtitle = 1;
			g_free(last_title);
			last_title = title;
		} else if(title && last_title != title) {
			/* New copy of last title */
			g_free(title);
		}
		check_title = !check_title;
	}
	last_pos = pos;
	/* Repeat every 500ms */
	return 500;
}

void worker_func()
{ 
  char projectM_data[PATH_MAX];
 
  SDL_TimerID title_timer = NULL;

 
  read_config();
 
  init_display(wvw,wvh,fullscreen);

  SDL_WM_SetCaption("projectM v0.99", "projectM v0.99");
 

  /** Initialise projectM */
    
  globalPM = (projectM_t *)malloc( sizeof( projectM_t ) );

  projectM_reset( globalPM );
  
  globalPM->fullscreen = fullscreen;
  globalPM->renderTarget->texsize = texsize;
  globalPM->gx=gx;
  globalPM->gy=gy;
  globalPM->fps=fps;
  globalPM->renderTarget->usePbuffers=0;
  
  strcpy(projectM_data, PROJECTM_DATADIR);
  strcpy(projectM_data+strlen(PROJECTM_DATADIR), FONTS_DIR);
  projectM_data[strlen(PROJECTM_DATADIR)+strlen(FONTS_DIR)]='\0';
  
  globalPM->fontURL = (char *)malloc( sizeof( char ) * 512 );
  strcpy( globalPM->fontURL, projectM_data );
  
  strcpy(projectM_data+strlen(PROJECTM_DATADIR), PRESETS_DIR);
  projectM_data[strlen(PROJECTM_DATADIR)+strlen(PRESETS_DIR)]='\0';
  
  globalPM->presetURL = (char *)malloc( sizeof( char ) * 512 );
  strcpy( globalPM->presetURL, projectM_data );
  
  
  projectM_init( globalPM );
  
  projectM_resetGL( globalPM, wvw, wvh );

  title_timer = SDL_AddTimer(500, (SDL_NewTimerCallback) get_xmms_title, NULL);
   
    /** Initialise the thread */
 
    SDL_SemTryWait(sem);
 while ( SDL_SemTryWait(sem) ) {
        projectMEvent evt;
        projectMKeycode key;
        projectMModifier mod;

        /** Process SDL events */
        SDL_Event event;
        while ( SDL_PollEvent( &event ) ) {
            /** Translate into projectM codes and process */
            evt = sdl2pmEvent( event );
            key = sdl2pmKeycode( event.key.keysym.sym );
            mod = sdl2pmModifier( event.key.keysym.mod );

            if ( evt == PROJECTM_KEYDOWN ) {                 
     

                if(key == SDLK_f)
                {
                    int w, h;
                    if (fullscreen == 0) {
                        w = fvw;
                        h = fvh;
                    } else {
                        w = wvw;
                        h = wvh;
                    }
                    globalPM->fullscreen = fullscreen ^= 1;
                    resize_display(w, h, fullscreen);
                    projectM_resetGL( globalPM, w, h ); 
                }
                else  key_handler(globalPM,evt,key,mod);

            }
            else if ( evt == PROJECTM_VIDEORESIZE )
            {
                wvw=event.resize.w;
                wvh=event.resize.h;
                resize_display(wvw, wvh, 0);
                projectM_resetGL( globalPM, wvw, wvh ); 

            } 
            else if ( evt == PROJECTM_VIDEOQUIT ) {

	(void) g_idle_add ((GSourceFunc) disable_projectm, NULL);
	/*				if(quit_timer == NULL)
	                quit_timer = SDL_AddTimer(500, disable_projectm, NULL);*/
            }

        }
             

        /** Render the new frame */

	renderFrame( globalPM );
 
        SDL_GL_SwapBuffers();
      }

 
		
  printf("Worker thread: Exiting\n"); 
  if(title_timer) SDL_RemoveTimer(title_timer);
  g_free(globalPM->title);
  free(globalPM->presetURL);
  free(globalPM->fontURL);
  free(globalPM);
  close_display();
}

static void projectM_xmms_init(void) 
{
  
  printf("projectM plugin: Initializing\n");

  SDL_EnableUNICODE(1); 
  
  mutex = SDL_CreateMutex();

  sem = SDL_CreateSemaphore(1);

  worker_thread = SDL_CreateThread ((void *) worker_func, NULL);
 
}



static void projectM_cleanup(void)
{
   
    SDL_SemPost(sem);
    SDL_WaitThread(worker_thread, NULL);

    SDL_DestroySemaphore(sem);
    printf("Destroy Semaphore\n");
    SDL_DestroyMutex(mutex);
    printf("Destroy Mutex\n");
  
    printf("projectM plugin: Cleanup completed\n");
}
static void projectM_about(void)
{
  printf("projectM plugin: About\n");
}
static void projectM_configure(void)
{
  printf("projectM plugin: Configure\n");
}
static void projectM_playback_start(void)
{
  printf("projectM plugin: Playback starting\n");
}
static void projectM_playback_stop(void)
{
  printf("projectM plugin: Playback stopping\n");
}
static void projectM_render_pcm(gint16 pcm_data[2][512])
{

    if (0 < SDL_SemValue(sem)) return;
  	SDL_mutexP(mutex);
       
        addPCM16Data(pcm_data,512);
	 
	SDL_mutexV(mutex);
	
}

static void projectM_render_freq(gint16 freq_data[2][256])
{
  printf("NO GOOD\n");
 }


void read_config()
{

   int n;
   
   char num[80];
   FILE *in; 
   FILE *out;

   char* home;
   char projectM_home[PATH_MAX];
   char projectM_config[PATH_MAX];

   strcpy(projectM_config, PROJECTM_DATADIR);
   strcpy(projectM_config+strlen(PROJECTM_DATADIR), CONFIG_FILE);
   projectM_config[strlen(PROJECTM_DATADIR)+strlen(CONFIG_FILE)]='\0';

   home=getenv("HOME");
   strcpy(projectM_home, home);
   strcpy(projectM_home+strlen(home), "/.projectM/config");
   projectM_home[strlen(home)+strlen("/.projectM/config")]='\0';

  
 if ((in = fopen(projectM_home, "r")) != 0) 
   {
     printf("reading ~/.projectM/config \n");
   }
 else
   {
     printf("trying to create ~/.projectM/config \n");

     strcpy(projectM_home, home);
     strcpy(projectM_home+strlen(home), "/.projectM");
     projectM_home[strlen(home)+strlen("/.projectM")]='\0';
     mkdir(projectM_home,0755);

     strcpy(projectM_home, home);
     strcpy(projectM_home+strlen(home), "/.projectM/config");
     projectM_home[strlen(home)+strlen("/.projectM/config")]='\0';
     
     if((out = fopen(projectM_home,"w"))!=0)
       {
	
	 if ((in = fopen(projectM_config, "r")) != 0) 
	   {
	     
	     while(fgets(num,80,in)!=NULL)
	       {
		 fputs(num,out);
	       }
	     fclose(in);
	     fclose(out);
	    

	     if ((in = fopen(projectM_home, "r")) != 0) 
	       { printf("created ~/.projectM/config successfully\n");  }
	     else{printf("This shouldn't happen, using implementation defualts\n");return;}
	   }
	 else{printf("Cannot find projectM default config, using implementation defaults\n");return;}
       }
     else
       {
	 printf("Cannot create ~/.projectM/config, using default config file\n");
	 if ((in = fopen(projectM_config, "r")) != 0) 
	   { printf("Successfully opened default config file\n");}
	 else{ printf("Using implementation defaults, your system is really messed up, I'm suprised we even got this far\n");	   return;}
	   
       }

   }



     fgets(num, 80, in);  fgets(num, 80, in);  fgets(num, 80, in);
     if(fgets(num, 80, in) != NULL) sscanf (num, "%d", &texsize);  

     fgets(num, 80, in);
     if(fgets(num, 80, in) != NULL) sscanf (num, "%d", &gx);  

     fgets(num, 80, in);
     if(fgets(num, 80, in) != NULL) sscanf (num, "%d", &gy);   

     fgets(num, 80, in);
     if(fgets(num, 80, in) != NULL) sscanf (num, "%d", &wvw);  

     fgets(num, 80, in);
     if(fgets(num, 80, in) != NULL) sscanf (num, "%d", &wvh);  

     fgets(num, 80, in);
     if(fgets(num, 80, in) != NULL) sscanf (num, "%d", &fvw);  

     fgets(num, 80, in);
     if(fgets(num, 80, in) != NULL) sscanf (num, "%d", &fvh);  

     fgets(num, 80, in);
     if(fgets(num, 80, in) != NULL) sscanf (num, "%d", &fps);

     fgets(num, 80, in);
     if(fgets(num, 80, in) != NULL) sscanf (num, "%d", &fullscreen);
     /*
     fgets(num, 80, in);
     fgets(num, 80, in);
     
     n=0;
     while (num[n]!=' ' && num[n]!='\n' && n < 80 && num[n]!=EOF)
       {
	 	 disp[n]=num[n];
		 n++;
       }
     disp[n]=0;

    
     // sprintf(disp,"%s",num );
      setenv("DISPLAY",disp,1);
      printf("%s %d\n", disp,strlen(disp));
      setenv("LD_PRELOAD", "/usr/lib/tls/libGL.so.1.0.4496", 1);
     */
   fclose(in); 
  
} 
