// Winamp test visualization library v1.0
// Copyright (C) 1997-1998, Justin Frankel/Nullsoft
// Feel free to base any plugins on this "framework"...

#include <windows.h>
#include <Math.h>
#include <mmsystem.h>

#include "Global.h"

#define WINDOW_TITLE "CMSC 427 - Disco Stu"

HDC m_hDC;
HGLRC m_hRC;

extern edict_t *ginny;

void CriticalFrame();
void RunFrame();
void GoFullScreen();

DWORD end_tick;

char szAppName[] = "SimpleVis"; // Our window class, etc

// configuration declarations
int config_x=50, config_y=50;	// screen X position and Y position, repsectively
void config_read(struct winampVisModule *this_mod);		// reads the configuration
void config_write(struct winampVisModule *this_mod);	// writes the configuration
void config_getinifn(struct winampVisModule *this_mod, char *ini_file); // makes the .ini file filename

// returns a winampVisModule when requested. Used in hdr, below
winampVisModule *getModule(int which);

// "member" functions
void config(struct winampVisModule *this_mod); // configuration dialog
int init(struct winampVisModule *this_mod);	   // initialization for module
int render1(struct winampVisModule *this_mod);  // rendering for module 1
int render2(struct winampVisModule *this_mod);  // rendering for module 2
int render3(struct winampVisModule *this_mod);  // rendering for module 3
void quit(struct winampVisModule *this_mod);   // deinitialization for module

// our window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
HWND hMainWnd; // main window handle

// Module header, includes version, description, and address of the module retriever function
winampVisHeader hdr = { VIS_HDRVER, WINDOW_TITLE, getModule };

// first module (oscilliscope)
winampVisModule mod1 =
{
	"Disco Stu",
	NULL,	// hwndParent
	NULL,	// hDllInstance
	0,		// sRate
	0,		// nCh
	15,		// latencyMS
	15,		// delayMS
	2,		// spectrumNch
	2,		// waveformNch
	{ 0, },	// spectrumData
	{ 0, },	// waveformData
	config,
	init,
	render1, 
	quit
};

// this is the only exported symbol. returns our main header.
// if you are compiling C++, the extern "C" { is necessary, so we just #ifdef it
#ifdef __cplusplus
extern "C" {
#endif
__declspec( dllexport ) winampVisHeader *winampVisGetHeader()
{
	return &hdr;
}
#ifdef __cplusplus
}
#endif

// getmodule routine from the main header. Returns NULL if an invalid module was requested,
// otherwise returns either mod1, mod2 or mod3 depending on 'which'.
winampVisModule *getModule(int which)
{
	switch (which)
	{
		case 0: return &mod1;
		//case 1: return &mod2;
		//case 2: return &mod3;
		default:return NULL;
	}
}

// configuration. Passed this_mod, as a "this" parameter. Allows you to make one configuration
// function that shares code for all your modules (you don't HAVE to use it though, you can make
// config1(), config2(), etc...)
void config(struct winampVisModule *this_mod)
{
	MessageBox(this_mod->hwndParent,
   "Disco Stu\n"
	"A CMSC 417 Term Project\n"
   "Written and compiled by:\n"
   "   Beth Weinstein\n"
   "   Ben Stewart\n"
   "   Nawfel Tricha\n", WINDOW_TITLE, MB_OK);
}

// initialization. Registers our window class, creates our window, etc. Again, this one works for
// both modules, but you could make init1() and init2()...
// returns 0 on success, 1 on failure.
int init(struct winampVisModule *this_mod)
{
	config_read(this_mod);

	// Register our window class
	   {	
		WNDCLASS wc;
		memset(&wc,0,sizeof(wc));
      wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
		wc.lpfnWndProc = WndProc;				// our window procedure
		wc.hInstance = this_mod->hDllInstance;	// hInstance of DLL
		wc.lpszClassName = szAppName;			// our window class name
	
		if (!RegisterClass(&wc)) 
		   {
			MessageBox(this_mod->hwndParent,"Error registering window class", WINDOW_TITLE, MB_OK);
			return 1;
		   }
	   }

   GoFullScreen();
   hMainWnd = CreateWindowEx (WS_EX_TOPMOST,
                              szAppName, WINDOW_TITLE, 
                              WS_POPUP,
                              0, 0, 640, 480,
                              NULL, NULL, this_mod->hDllInstance, NULL );
	if (!hMainWnd) 
	   {
		MessageBox(this_mod->hwndParent,"Error creating window", WINDOW_TITLE, MB_OK);
		return 1;
	   }

   m_hDC = GetDC(hMainWnd);
   EnableOpenGL(hMainWnd, &m_hDC, &m_hRC);
   ShowWindow(hMainWnd, SW_SHOW);
   // NT: Set high priority to our window:
	SetForegroundWindow(hMainWnd);
	SetFocus(hMainWnd);
   
   // NT: Turn off mouse:
	ShowCursor (FALSE);
   		
   InitGL();

   // NT: Start clock in .5 secs:
   end_tick = eng.time + 500;
 	return 0;
}

// render function for oscilliscope. Returns 0 if successful, 1 if visualization should end.
#define INTERPOLATIONS .5f
int render1(struct winampVisModule *this_mod)
{
   DWORD current_tick = timeGetTime();
   // NT: 
   // Poll every 50 msecs.
   // Get system time, store it in eng.time.
   // Call any think functions if their time has come:
   if (current_tick > end_tick)
      {
      end_tick = current_tick + 50;
      eng.time = current_tick;
      RunFrame();
      }
   // Run the physics at every frame:
   CriticalFrame();

   // Interpolate frames:
   eng.interpolation += INTERPOLATIONS;
   // Render frame:
   RenderFrame(this_mod);
	if (eng.interpolation >= 1)
      eng.interpolation = 0;
   return 0;
}

// cleanup (opposite of init()). Destroys the window, unregisters the window class
void quit(struct winampVisModule *this_mod)
{
   DisableOpenGL(hMainWnd, m_hDC, m_hRC);
	config_write(this_mod);		// write configuration
   ChangeDisplaySettings(NULL,0);						// If So Switch Back To The Desktop
	ShowCursor(TRUE);									// Show Mouse Pointer
	DestroyWindow(hMainWnd); // delete our window
	UnregisterClass(szAppName,this_mod->hDllInstance); // unregister window class
}


// window procedure for our window
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_CREATE:		return 0;
		case WM_ERASEBKGND: return 0;
		return 0;
		case WM_DESTROY: PostQuitMessage(0); return 0;
		case WM_KEYDOWN: // pass keyboard messages to main winamp window (for processing)
			{	// get this_mod from our window's user data
				//winampVisModule *this_mod = (winampVisModule *) GetWindowLong(hwnd,GWL_USERDATA);
				//PostMessage(this_mod->hwndParent,message,wParam,lParam);
            switch (wParam)
               {
               case VK_F1:
                  r_mode ^= RENDER_HELP;
                  break;
               case VK_F2:
                  r_effect ^= EFFECT_SPECTRUM;
                  break;
               case VK_F3:
                  r_effect ^= EFFECT_FREQUENCY;
                  break;
               case VK_F4:
                  if (r_mode & RENDER_CONSOLE)
                     {
                     r_mode &= ~RENDER_CONSOLE;
                     r_mode |= RENDER_CLEAR_ACC;
                     }
                  else
                     {
                     r_mode |= RENDER_CONSOLE;
                     r_mode &= ~RENDER_CLEAR_ACC;
                     }                  
                  break;
               case VK_F5:
                  r_mode ^= RENDER_HUD;
                  break;
               case VK_F6:
                  r_mode ^= RENDER_FLUSH_ENGINE;
                  break;
               case VK_F7:
                  r_mode ^= RENDER_DSP_LIGHT;
                  break;
               case VK_F8:
                  if (ginny)
                     ginny->state ^= ENT_ST_VISIBLE;   
                  break;
               case VK_ESCAPE:
                  PostQuitMessage(0); return 0;
                  break;
               }            
			}
		return 0;
		case WM_MOVE:
			{	// get config_x and config_y for configuration
				RECT r;
				GetWindowRect(hMainWnd,&r);
				config_x = r.left;
				config_y = r.top;
			}
		return 0;
	}
	return DefWindowProc(hwnd,message,wParam,lParam);
}


void config_getinifn(struct winampVisModule *this_mod, char *ini_file)
{	// makes a .ini file in the winamp directory named "plugin.ini"
	char *p;
	GetModuleFileName(this_mod->hDllInstance,ini_file,MAX_PATH);
	p=ini_file+strlen(ini_file);
	while (p >= ini_file && *p != '\\') p--;
	if (++p >= ini_file) *p = 0;
	strcat(ini_file, "plugin.ini");
}


void config_read(struct winampVisModule *this_mod)
{
	char ini_file[MAX_PATH];
	config_getinifn(this_mod,ini_file);
	config_x = GetPrivateProfileInt(this_mod->description,"Screen_x",config_x,ini_file);
	config_y = GetPrivateProfileInt(this_mod->description,"Screen_y",config_y,ini_file);
}

void config_write(struct winampVisModule *this_mod)
{
	char string[32];
	char ini_file[MAX_PATH];

	config_getinifn(this_mod,ini_file);

	wsprintf(string,"%d",config_x);
	WritePrivateProfileString(this_mod->description,"Screen_x",string,ini_file);
	wsprintf(string,"%d",config_y);
	WritePrivateProfileString(this_mod->description,"Screen_y",string,ini_file);
}


void EngineError(char *format, ...)
{
   static bool bQuit = false;
	va_list		argptr;
	static char		string[1024];
   if (bQuit)
      return;
	va_start (argptr, format);
	vsprintf (string, format,argptr);
	va_end (argptr);

   DisableOpenGL(hMainWnd, m_hDC, m_hRC);
   ChangeDisplaySettings(NULL,0);						// If So Switch Back To The Desktop
	ShowCursor(TRUE);									// Show Mouse Pointer

   MessageBox (hMainWnd, string, "Plug-In: ERROR", MB_OK);
   bQuit = true;
   PostQuitMessage( 0 );
}

void GoFullScreen()
{
   DEVMODE dmScreenSettings;								
   memset (&dmScreenSettings, 0, sizeof(DEVMODE));
   dmScreenSettings.dmSize = sizeof(DEVMODE);		
   dmScreenSettings.dmPelsWidth	= 640;
   dmScreenSettings.dmPelsHeight	= 480;			
   dmScreenSettings.dmBitsPerPel	= 16;				
   dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

   if (ChangeDisplaySettings (&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
      {
      EngineError("SYS: Unable to allocate fullscreen device context. Quitting...\n");
      return;
      }
}
