/*
  Copyright (C) 2011 Birunthan Mohanathas (www.poiru.net)

  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 "StdAfx.h"
#include "PlayerFoobar.h"

CPlayer* CPlayerFoobar::c_Player = NULL;
extern HINSTANCE g_Instance;

/*
** CPlayerFoobar
**
** Constructor.
**
*/
CPlayerFoobar::CPlayerFoobar() : CPlayer(),
	m_Window(),
	m_FooWindow(),
	m_MaximizeOnStart(false)
{
	Initialize();
}

/*
** ~CPlayerFoobar
**
** Constructor.
**
*/
CPlayerFoobar::~CPlayerFoobar()
{
	c_Player = NULL;
	Uninitialize();
}

/*
** Create
**
** Creates a shared class object.
**
*/
CPlayer* CPlayerFoobar::Create()
{
	if (!c_Player)
	{
		c_Player = new CPlayerFoobar();
	}

	return c_Player;
}

/*
** Initialize
**
** Create receiver window.
**
*/
void CPlayerFoobar::Initialize()
{
	// Create windows class
	WNDCLASS wc = {0};
	wc.hInstance = g_Instance;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = L"NowPlayingFoobarClass";
	RegisterClass(&wc);

	// Create window
	m_Window = CreateWindow(L"NowPlayingFoobarClass",
							L"ReceiverWindow",
							WS_DISABLED,
							CW_USEDEFAULT,
							CW_USEDEFAULT,
							CW_USEDEFAULT,
							CW_USEDEFAULT,
							NULL,
							NULL,
							g_Instance,
							this);

	m_FooWindow = FindWindow(L"foo_rainmeter_class", NULL);
	if (m_FooWindow)
	{
		int version = (int)SendMessage(m_FooWindow, WM_USER, 0, FOO_GETVERSION);
		if (version < 100)
		{
			const WCHAR* error = L"Your copy of the foo_rainmeter.dll plugin for foobar2000 is outdated.\nDownload the latest version from foo-rainmeter.googlecode.com and try again.";
			MessageBox(NULL, error, L"Rainmeter", MB_OK | MB_ICONERROR | MB_TOPMOST);
			m_FooWindow = NULL;
		}
		else
		{
			m_Initialized = true;
			SendMessage(m_FooWindow, WM_USER, (WPARAM)m_Window, FOO_SETCALLBACK);
		}
	}
}

/*
** Uninitialize
**
** Destroy reciever window.
**
*/
void CPlayerFoobar::Uninitialize()
{
	if (m_FooWindow)
	{
		SendMessage(m_FooWindow, WM_USER, 0, FOO_REMOVECALLBACK);
	}

	DestroyWindow(m_Window);
	UnregisterClass(L"NowPlayingFoobarClass", g_Instance);
}

/*
** WndProc
**
** Window procedure for the reciever window.
**
*/
LRESULT CALLBACK CPlayerFoobar::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	static CPlayerFoobar* player;

	switch (msg)
	{
	case WM_CREATE:
		{
			// Get pointer to the CPlayerFoobar class from the CreateWindow call
			LPVOID params = ((CREATESTRUCT*)lParam)->lpCreateParams;
			player = (CPlayerFoobar*)params;
			return 0;
		}

	case WM_USER:
		switch (lParam)
		{
		case FOO_GETVERSION:
			return 100;

		case FOO_STATECHANGE:
			{
				PLAYSTATE ps = (PLAYSTATE)wParam;
				if (ps == PLAYER_STOPPED)
				{
					player->ClearData();
				}
				else
				{
					player->m_State = ps;
				}
			}
			break;

		case FOO_TIMECHANGE:
			player->m_Position = (UINT)wParam;
			break;

		case FOO_VOLUMECHANGE:
			player->m_Volume = (UINT)wParam;
			break;

		case FOO_PLAYERSTART:
			player->m_Initialized = true;
			player->m_FooWindow = (HWND)wParam;

			if (player->m_MaximizeOnStart)
			{
				SendMessage(player->m_FooWindow, WM_USER, 0, FOO_SHOWPLAYER);
				player->m_MaximizeOnStart = false;
			}
			break;

		case FOO_PLAYERQUIT:
			player->m_Initialized = false;
			player->ClearData();
			break;
		}
		return 0;

	case WM_COPYDATA:
		{
			PCOPYDATASTRUCT cds = (PCOPYDATASTRUCT)lParam;

			if (cds->dwData == FOO_TRACKCHANGE)
			{
				if (player->m_State != PLAYER_PLAYING)
				{
					player->m_State = PLAYER_PLAYING;
				}

				// In the format "TITLE ARTIST ALBUM LENGTH RATING" (seperated by \t)
				WCHAR buffer[1024];
				MultiByteToWideChar(CP_UTF8, 0, (char*)cds->lpData, cds->cbData, buffer, 1024);
				player->m_Artist = buffer;

				WCHAR* token = wcstok(buffer, L"\t");
				if (token)
				{
					if (wcscmp(token, L" ") == 0)
					{
						player->m_Title.clear();
					}
					else
					{
						player->m_Title = token;
					}
				}
				token = wcstok(NULL, L"\t");
				if (token)
				{
					if (wcscmp(token, L" ") == 0)
					{
						player->m_Artist.clear();
					}
					else
					{
						player->m_Artist = token;
					}
				}
				token = wcstok(NULL, L"\t");
				if (token)
				{
					if (wcscmp(token, L" ") == 0)
					{
						player->m_Album.clear();
					}
					else
					{
						player->m_Album = token;
					}
				}
				token = wcstok(NULL, L"\t");
				if (token)
				{
					player->m_Duration = _wtoi(token);
				}
				token = wcstok(NULL, L"\t");
				if (token)
				{
					player->m_Rating = _wtoi(token);
				}
				token = wcstok(NULL, L"\t");
				if (token && wcscmp(token, player->m_FilePath.c_str()) != 0)
				{
					// If different file
					++player->m_TrackCount;
					player->m_FilePath = token;
					player->m_Position = 0;

					if (player->m_Measures & MEASURE_COVER || player->m_InstanceCount == 0)
					{
						player->FindCover();
					}

					if (player->m_Measures & MEASURE_LYRICS)
					{
						player->FindLyrics();
					}
				}
				token = wcstok(NULL, L"\t");
				if (token)
				{
					switch (_wtoi(token))
					{
					case 0:
						player->m_Shuffle = player->m_Repeat = false;
						break;

					case 1:
						player->m_Shuffle = true;
						player->m_Repeat = false;
						break;

					case 2:
						player->m_Shuffle = false;
						player->m_Repeat = true;
						break;
					}
				}
			}
		}
		return 0;

	default:
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
}

/*
** UpdateData
**
** Called during each update of the main measure.
**
*/
void CPlayerFoobar::UpdateData()
{
}

/*
** Pause
**
** Handles the Pause bang.
**
*/
void CPlayerFoobar::Pause()
{
	SendMessage(m_FooWindow, WM_USER, 0, FOO_PAUSE);
}

/*
** Play
**
** Handles the Play bang.
**
*/
void CPlayerFoobar::Play()
{
	SendMessage(m_FooWindow, WM_USER, 0, (m_State == PLAYER_PAUSED) ? FOO_PLAYPAUSE : FOO_PLAY);
}

/*
** Stop
**
** Handles the Stop bang.
**
*/
void CPlayerFoobar::Stop() 
{
	SendMessage(m_FooWindow, WM_USER, 0, FOO_STOP);
}

/*
** Next
**
** Handles the Next bang.
**
*/
void CPlayerFoobar::Next() 
{
	SendMessage(m_FooWindow, WM_USER, 0, FOO_NEXT);
}

/*
** Previous
**
** Handles the Previous bang.
**
*/
void CPlayerFoobar::Previous() 
{
	SendMessage(m_FooWindow, WM_USER, 0, FOO_PREVIOUS);
}

/*
** SetPosition
**
** Handles the SetPosition bang.
**
*/
void CPlayerFoobar::SetPosition(int position)
{
	SendMessage(m_FooWindow, WM_USER, position, FOO_SETPOSITION);
}

/*
** SetShuffle
**
** Handles the SetShuffle bang.
**
*/
void CPlayerFoobar::SetShuffle(bool state)
{
	m_Shuffle = state;
	m_Repeat = state ? !m_Shuffle : m_Shuffle;
	SendMessage(m_FooWindow, WM_USER, state ? 1 : 0, FOO_SETORDER);
}

/*
** SetRepeat
**
** Handles the SetRepeat bang.
**
*/
void CPlayerFoobar::SetRepeat(bool state)
{
	m_Repeat = state;
	m_Shuffle = state ? !m_Repeat : m_Repeat;
	SendMessage(m_FooWindow, WM_USER, state ? 2 : 0, FOO_SETORDER);
}

/*
** SetVolume
**
** Handles the SetVolume bang.
**
*/
void CPlayerFoobar::SetVolume(int volume) 
{
	SendMessage(m_FooWindow, WM_USER, volume, FOO_SETVOLUME);
}

/*
** ClosePlayer
**
** Handles the ClosePlayer bang.
**
*/
void CPlayerFoobar::ClosePlayer()
{
	SendMessage(m_FooWindow, WM_USER, 0, FOO_QUITPLAYER);
}

/*
** OpenPlayer
**
** Handles the OpenPlayer bang.
**
*/
void CPlayerFoobar::OpenPlayer(std::wstring& path)
{
	if (!m_Initialized)
	{
		if (path.empty())
		{
			// Gotta figure out where foobar2000 is located at
			HKEY hKey;
			RegOpenKeyEx(HKEY_CLASSES_ROOT,
						 L"Applications\\foobar2000.exe\\shell\\open\\command",
						 0,
						 KEY_QUERY_VALUE,
						 &hKey);

			DWORD size = 512;
			WCHAR* data = new WCHAR[size];
			DWORD type = 0;

			if (RegQueryValueEx(hKey,
								NULL,
								NULL,
								(LPDWORD)&type,
								(LPBYTE)data,
								(LPDWORD)&size) == ERROR_SUCCESS)
			{
				if (type == REG_SZ && data[0] == L'\"')
				{
					path = data;
					path.erase(0, 1);	// Get rid of the leading quote
					std::wstring::size_type pos = path.find_first_of(L'\"');

					if (pos != std::wstring::npos)
					{
						path.resize(pos);	// Get rid the last quote and everything after it
						ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW);
						path = path;
					}
					else
					{
						path.clear();
					}
				}
			}

			delete [] data;
			RegCloseKey(hKey);
		}
		else
		{
			ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW);
			m_MaximizeOnStart = true;
		}
	}
	else
	{
		SendMessage(m_FooWindow, WM_USER, 0, FOO_SHOWPLAYER);
	}
}