
#include<windows.h>
#include"vismod.h"

#include"resource.h"

#include<gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib,"gdiplus.lib")

VisModule visMod;

int _nLevel=0;

Image *gifImage=0;
GUID *m_pDimensionIDs;
UINT m_FrameCount;
PropertyItem* m_pItem;

HANDLE threadHandle;
bool threadShouldEnd=false;
bool threadClosed=false;

int m_iCurrentFrame = 0;

ULONG_PTR gdiplusToken;

DWORD WINAPI ThreadProc(
  LPVOID lpParameter   // thread data
)
{
	while(!threadShouldEnd)
	{
		//Change Active frame
		GUID Guid = FrameDimensionTime;
		gifImage->SelectActiveFrame(&Guid,m_iCurrentFrame);

		//repaint
		HDC hdc=GetDC(visMod.hVisDlg);
		Graphics g(hdc);

		RECT rcClient;
		GetClientRect(visMod.hVisDlg,&rcClient);

		g.DrawImage(gifImage,rcClient.left,rcClient.top,rcClient.right-rcClient.left,rcClient.bottom-rcClient.top);
		FrameRect(hdc,&rcClient,(HBRUSH)GetStockObject(BLACK_BRUSH));
		ReleaseDC(visMod.hVisDlg,hdc);

		if(_nLevel!=0)
		{
			UINT delay=((UINT*)m_pItem[0].value)[m_iCurrentFrame]*10;
			delay= delay < 5 ? 100 : delay;

			int delta=_nLevel>140?-50:_nLevel>135?-30:_nLevel>130?-20:_nLevel>125?-10:_nLevel>120?0:_nLevel>110?10:_nLevel>100?30:_nLevel>80?40:50;
			Sleep(delay+delta);

			m_iCurrentFrame = (++ m_iCurrentFrame) % m_FrameCount;
		}else
		{
			Sleep(100);
		}
	}
	threadClosed=true;

	return 0;
}

void LoadGifImage()
{
	
	gifImage = new Image(L"vis.gif");
	int height=((float)100/(float)gifImage->GetWidth())*(float)gifImage->GetHeight();
	SetWindowPos(visMod.hVisDlg,0,0,0,100,height,SWP_NOMOVE|SWP_NOACTIVATE);


	//First of all we should get the number of frame dimensions
	//Images considered by GDI+ as:
	//frames[animation_frame_index][how_many_animation];
	UINT count = gifImage->GetFrameDimensionsCount();

	//Now we should get the identifiers for the frame dimensions 
	m_pDimensionIDs =new GUID[count];
	gifImage->GetFrameDimensionsList(m_pDimensionIDs, count);

	//For gif image , we only care about animation set#0
	WCHAR strGuid[39];
	StringFromGUID2(m_pDimensionIDs[0], strGuid, 39);
	m_FrameCount = gifImage->GetFrameCount(&m_pDimensionIDs[0]);

	//PropertyTagFrameDelay is a pre-defined identifier 
	//to present frame-delays by GDI+
	UINT TotalBuffer = gifImage->GetPropertyItemSize(PropertyTagFrameDelay);
	m_pItem = (PropertyItem*)malloc(TotalBuffer);
	gifImage->GetPropertyItem(PropertyTagFrameDelay,TotalBuffer,m_pItem);

	GUID Guid = FrameDimensionTime;
	gifImage->SelectActiveFrame(&Guid,0);

}

void FreeResources()
{
	if(gifImage)
		delete gifImage;
	delete []m_pDimensionIDs;
	free(m_pItem);

	CloseHandle(threadHandle);
}

void InitVisMod()
{
	//Init GDI+
	GdiplusStartupInput gdiplusStartupInput;
	Status state = GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	visMod.hVisDlg=CreateDialog(visMod.hDllInstance,MAKEINTRESOURCE(IDD_DIALOG1),0,visMod.MainDlgProc);
	LoadGifImage();
	DWORD threadId;
	threadHandle=CreateThread(0,0,&ThreadProc,0,0,&threadId);	
	ShowWindow(visMod.hVisDlg,SW_SHOW);
}

BOOL VisDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) // handle window messages!
{
	switch (msg) {
		case WM_PAINT:
			{
				PAINTSTRUCT ps; 
				HDC hdc=BeginPaint(hwnd,&ps);

				Graphics g(hdc);

				RECT rcClient;
				GetClientRect(hwnd,&rcClient);

				if(gifImage)
					g.DrawImage(gifImage,rcClient.left,rcClient.top,rcClient.right-rcClient.left,rcClient.bottom-rcClient.top);
				
				FrameRect(hdc,&rcClient,(HBRUSH)GetStockObject(BLACK_BRUSH));
				EndPaint(hwnd,&ps);
			}
			break;
		case WM_ERASEBKGND:
			return -1;
		case WM_CLOSE:
			threadShouldEnd=true;
			while(!threadClosed)
			{
				Sleep(500);
			}
			break;
		default:
			return FALSE;

		}

	return TRUE;
}

void ReadAudioSamples(LPWAVEHDR pWaveHdr,int nLevel,int bpm)
{
	_nLevel=bpm;
}

void Quit()
{
	FreeResources();
	GdiplusShutdown(gdiplusToken);
}

extern "C" __declspec(dllexport) VisModule* GetVisMod()
{
	visMod.InitVisMod=InitVisMod;
	visMod.VisDlgProc=VisDlgProc;
	visMod.ReadAudioSamples=ReadAudioSamples;
	visMod.Quit=Quit;

	return &visMod;
}