                      /*	
	HotKey plugin for Sonique 2.0
    Copyright (C) 2004  Jerome Kuptz

    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
*/
#define INITMID
#define _USE_MIA_
#include "MediaFilterHotKey.h"
#include "sq2_hotkey.h"
#include "Interface_Dialog.h"
#include "ioutils.h"
#include "mia.h"

/*
This whole plugin seems like an absolute hack on the system.  Sonique loads the dll, but 
we never insert ourselves into the chain.  This way the thread keeps running until Sonique 
is shutdown.  This method is not suggested, but it was the only way for this plugin to
currently exist. 
*/

// yup we use good old app commands to make this happen.
#ifndef WM_APPCOMMAND

#define MAKESONCOMMAND(x) ( ((int) x<< 16) | ((int) FSONAPPCOMMAND_MASK<< 16) | ((int) 0) )

#define WM_APPCOMMAND				  0x319
#define APPCOMMAND_EXIT                   3
#define APPCOMMAND_VOLUME_MUTE            8
#define APPCOMMAND_VOLUME_DOWN            9
#define APPCOMMAND_VOLUME_UP	          10
#define APPCOMMAND_MEDIA_NEXTTRACK		  11
#define APPCOMMAND_MEDIA_PREVIOUSTRACK	  12
#define APPCOMMAND_MEDIA_STOP			  13
#define APPCOMMAND_MEDIA_PLAY_PAUSE		  14
#define APPCOMMAND_MEDIA_OPEN			  16
#define FSONAPPCOMMAND_MASK			  0x8000

#endif


//TODO:I have no idea why the first entry in mods/keys arrays in the mml will not be shown in the pickers. Fix it?
const char ConfigDialogue[] =
" <?mml version=\"0.6\" ?>"
"<mml>"
"	<interface	unique_name=\"teamsonique/sonique2_hot_prefs/1.0\" >"
"		<window name=\"HotKeyPrefs\" offset=\"0,0,360,0\" alignment=\"0,0,0,1\" transparent=\"true\" >"
"			<ImageStyle name=\"background\">"
"				<color_rect color=\"MML_Mood_Color3\" alignment=\"entire\" blendmode=\"COPY|RGB\" />"
"			</ImageStyle>"
"			<window name=\"enableCheckbox\" type=\"button\" offset=\"10,9,24,23\" alignment=\"0,0,0,0\" transparent=\"true\" hitstyle=\"ACTIVE\" >"
"				<property hover=\"true\"/>"
"				<imagestyle name=\"state0\">"
"					<include source=\"MML_Mood_DlgCheckBoxState0\" />"
"				</imagestyle>"
"				<imagestyle name=\"state1\">"
"					<include source=\"MML_Mood_DlgCheckBoxState1\" />"
"				</imagestyle>"
"				<script name=\"OnInit\">"
"					SetToggle(true);"
"					SetButtonState( :sonique.players.default.settings.HotKey.Get(\"enabled\") );"
"				</script>"
"				<script name=\"OnClick\">"
"					if( GetButtonState() ) { :sonique.players.default.settings.HotKey.Set(\"enabled\", 1); } else { :sonique.players.default.settings.HotKey.Set(\"enabled\", 0); }"
"				</script>"
"			</window>  <!-- enableCheckbox -->"
"			<window name=\"enableTitle\" type=\"textdisplay\" offset=\"25,9,100,23\" alignment=\"0,0,0,0\" transparent=\"true\" >"
"				<property font_color=\"0xFFFFFFFF\" />"
"				<script name=\"OnInit\"> SetText(:language.Translate(\"settings/generic.enable\")); </script>"
"			</window>  <!-- enableTitle -->"
"			<window name=\"FunctionPresets\""
"							type=\"selectionlist\""
"							alignment=\"0,0,0,0\""
"							offset=\"5,30,200,142\""
"							transparent=\"true\">"
"				<property textshadow=\"false\" font_color1=\"#FFFFFF\" font_color2=\"#808080\" font_color3=\"#000000\" fade_duration=\"0\" selection_type=\"one\"/>"
"				<imagestyle name=\"background\" transparent=\"false\">"
"					<include source=\"MML_Mood_SelectionList_Background\" />"
"				</imagestyle>"
"				<imagestyle name=\"img_item_off\" transparent=\"false\">"
"					<include source=\"MML_Mood_SelectionList_Item_Off\" />"
"				</imagestyle>"
"				<imagestyle name=\"img_item_on\" transparent=\"false\">"
"					<include source=\"MML_Mood_SelectionList_Item_On\" />"
"				</imagestyle>"
"				<script name=\"OnInit\">"
"					SetAllItems(functions);"
"					ClearSelection();"
"					SetIndexSelected(0, true);"
"					LoadModkeys();"
"					:system.timer.SetTimer(\"SetKey\", 100, \"KeyList.SelectKey()\", this);"
"				</script>"
"				<script name=\"OnItemSelected\">"
"					ActiveFunction = $params[0];"
"					LoadModkeys();"
"					KeyList.SelectKey();"
"				</script>"
"			</window>  <!-- FunctionPresets -->"
"			<window name=\"KeyList\""
"							type=\"selectionlist\""
"							alignment=\"0,0,0,1\""
"							offset=\"250,30,350,-10\""
"							transparent=\"true\">"
"				<property textshadow=\"false\" font_color1=\"#FFFFFF\" font_color2=\"#808080\" font_color3=\"#000000\" fade_duration=\"0\" selection_type=\"one\"/>"
"				<imagestyle name=\"background\" transparent=\"false\">"
"					<include source=\"MML_Mood_SelectionList_Background\" />"
"				</imagestyle>"
"				<imagestyle name=\"img_item_off\" transparent=\"false\">"
"					<include source=\"MML_Mood_SelectionList_Item_Off\" />"
"				</imagestyle>"
"				<imagestyle name=\"img_item_on\" transparent=\"false\">"
"					<include source=\"MML_Mood_SelectionList_Item_On\" />"
"				</imagestyle>"
"				<script name=\"OnInit\">"
"					SetAllItems(keys);"
"				</script>"
"				<script name=\"SelectKey\">"
"					ClearSelection();"
"					$i = :sonique.players.default.settings.HotKey.Get(internFunc[ActiveFunction] | \"key\");"
"					SetIndexSelected($i, true);"
"				</script>"
"				<script name=\"OnItemSelected\">"
"					:sonique.players.default.settings.HotKey.Set(internFunc[ActiveFunction] | \"key\", $params[0]);"
"					:sonique.players.default.settings.HotKey.Set(\"reloadSettings\", 1);"
"				</script>"
"			</window>  <!-- KeyList -->"
"			<window name=\"ModKeysTitle\" type=\"textdisplay\" alignment=\"0,0,0,0\" offset=\"5,155,100,170\" transparent=\"true\" >"
"				<property font_color=\"0xFFFFFFFF\" />"
"				<script name=\"OnInit\">SetText(\"Mod keys\");</script>"
"			</window>  <!-- enableTitle -->"
"			<window name=\"AltMod\""
"							alignment=\"0,0,0.5,0\""
"							offset=\"5,175,-5,190\""
"							transparent=\"true\">"
"							>"
"				<imagestyle name=\"background\">"
"					<text color=\"#ffffff\" alignment=\"0,0,1,0\" offset=\"12,0,0,9\">Alt</text>"
"				</imagestyle>"
"				<window name=\"AltModCheckBox\""
"								type=\"button\""
"								alignment=\"0,0,0,0\""
"								offset=\"0,1,9,10\""
"								transparent=\"true\""
"								hitstyle=\"active\">"
"					<imagestyle name=\"state0\">"
"						<include source=\"MML_Mood_DlgCheckBoxState0\" />"
"					</imagestyle>"
"					<imagestyle name=\"state1\">"
"						<include source=\"MML_Mood_DlgCheckBoxState1\" />"
"					</imagestyle>"
"					<script name=\"OnInit\">"
"						SetToggle(true);"
"						MOD_Val = 1;"
"					</script>"
"					<script name=\"OnClick\">"
"						SaveModkeys();"
"					</script>"
"					<script name=\"GetMOD_Val\">"
"						return (MOD_Val * GetButtonState());"
"					</script>"
"				</window>"
"			</window>  <!-- AltMod -->"
"			<window name=\"CtrlMod\""
"							alignment=\"0,0,0.5,0\""
"							offset=\"5,195,-5,210\""
"							transparent=\"true\">"
"							>"
"				<imagestyle name=\"background\">"
"					<text color=\"#ffffff\" alignment=\"0,0,1,0\" offset=\"12,0,0,9\">Ctrl</text>"
"				</imagestyle>"
"				<window name=\"CtrlModCheckBox\""
"								type=\"button\""
"								alignment=\"0,0,0,0\""
"								offset=\"0,1,9,10\""
"								transparent=\"true\""
"								hitstyle=\"active\">"
"					<imagestyle name=\"state0\">"
"						<include source=\"MML_Mood_DlgCheckBoxState0\" />"
"					</imagestyle>"
"					<imagestyle name=\"state1\">"
"						<include source=\"MML_Mood_DlgCheckBoxState1\" />"
"					</imagestyle>"
"					<script name=\"OnInit\">"
"						SetToggle(true);"
"						MOD_Val = 2;"
"					</script>"
"					<script name=\"OnClick\">"
"						SaveModkeys();"
"					</script>"
"					<script name=\"GetMOD_Val\">"
"						return (MOD_Val * GetButtonState());"
"					</script>"
"				</window>"
"			</window>  <!-- CtrlMod -->"
"			<window name=\"ShiftMod\""
"							alignment=\"0,0,0.5,0\""
"							offset=\"55,175,-5,190\""
"							transparent=\"true\">"
"							>"
"				<imagestyle name=\"background\">"
"					<text color=\"#ffffff\" alignment=\"0,0,1,0\" offset=\"12,0,0,9\">Shift</text>"
"				</imagestyle>"
"				<window name=\"ShiftModCheckBox\""
"								type=\"button\""
"								alignment=\"0,0,0,0\""
"								offset=\"0,1,9,10\""
"								transparent=\"true\""
"								hitstyle=\"active\">"
"					<imagestyle name=\"state0\">"
"						<include source=\"MML_Mood_DlgCheckBoxState0\" />"
"					</imagestyle>"
"					<imagestyle name=\"state1\">"
"						<include source=\"MML_Mood_DlgCheckBoxState1\" />"
"					</imagestyle>"
"					<script name=\"OnInit\">"
"						SetToggle(true);"
"						MOD_Val = 4;"
"					</script>"
"					<script name=\"OnClick\">"
"						SaveModkeys();"
"					</script>"
"					<script name=\"GetMOD_Val\">"
"						return (MOD_Val * GetButtonState());"
"					</script>"
"				</window>"
"			</window>  <!-- ShiftMod -->"
"			<window name=\"WinMod\""
"							alignment=\"0,0,0.5,0\""
"							offset=\"55,195,-5,210\""
"							transparent=\"true\">"
"							>"
"				<imagestyle name=\"background\">"
"					<text color=\"#ffffff\" alignment=\"0,0,1,0\" offset=\"12,0,0,9\">Win</text>"
"				</imagestyle>"
"				<window name=\"WinModCheckBox\""
"								type=\"button\""
"								alignment=\"0,0,0,0\""
"								offset=\"0,1,9,10\""
"								transparent=\"true\""
"								hitstyle=\"active\">"
"					<imagestyle name=\"state0\">"
"						<include source=\"MML_Mood_DlgCheckBoxState0\" />"
"					</imagestyle>"
"					<imagestyle name=\"state1\">"
"						<include source=\"MML_Mood_DlgCheckBoxState1\" />"
"					</imagestyle>"
"					<script name=\"OnInit\">"
"						SetToggle(true);"
"						MOD_Val = 8;"
"					</script>"
"					<script name=\"OnClick\">"
"						SaveModkeys();"
"					</script>"
"					<script name=\"GetMOD_Val\">"
"						return (MOD_Val * GetButtonState());"
"					</script>"
"				</window>"
"			</window>  <!-- WinMod -->"
"			<script name=\"SaveModkeys\">"
"				$MOD_Val = AltMod.AltModCheckBox.GetMOD_Val()"
"								 + CtrlMod.CtrlModCheckBox.GetMOD_Val()"
"								 + ShiftMod.ShiftModCheckBox.GetMOD_Val()"
"								 + WinMod.WinModCheckBox.GetMOD_Val();"
"				:sonique.players.default.settings.HotKey.Set(internFunc[ActiveFunction] | \"mod\", $MOD_Val);"
"				:sonique.players.default.settings.HotKey.Set(\"reloadSettings\", 1);"
"			</script>"
"			<script name=\"LoadModkeys\">"
"				$MOD_Val = :sonique.players.default.settings.HotKey.Get(internFunc[ActiveFunction] | \"mod\");"
"				$alt = 0; $shift = 0; $ctrl = 0; $win = 0;"
"				if ($MOD_Val >= 8)"
"				{"
"					$win = 1;"
"					$MOD_Val -= 8;"
"				}"
"				if ($MOD_Val >= 4)"
"				{"
"					$shift = 1;"
"					$MOD_Val -= 4;"
"				}"
"				if ($MOD_Val >= 2)"
"				{"
"					$ctrl = 1;"
"					$MOD_Val -= 2;"
"				}"
"				if ($MOD_Val == 1)"
"				{"
"					$alt = 1;"
"				}"
"				AltMod.AltModCheckBox.SetButtonState($alt);"
"				ShiftMod.ShiftModCheckBox.SetButtonState($shift);"
"				CtrlMod.CtrlModCheckBox.SetButtonState($ctrl);"
"				WinMod.WinModCheckBox.SetButtonState($win);"
"			</script>"
"			<script name=\"OnInit\">"
"		  	keys = {\"A\",\"B\",\"C\",\"D\",\"E\",\"F\",\"G\",\"H\",\"I\",\"J\",\"K\",\"L\",\"M\",\"N\",\"O\",\"P\",\"Q\",\"R\",\"S\",\"T\",\"U\",\"V\",\"W\",\"X\",\"Y\",\"Z\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"0\",\"Left\",\"Up\",\"Right\",\"Down\",\"Space\"};"
"        functions = {\"Play/Pause\",\"Stop\",\"Next\",\"Previous\",\"Mute\",\"Volume Up\",\"Volume Down\"};"
"				internFunc = {\"play\", \"stop\", \"next\", \"prev\", \"mute\", \"volu\", \"vold\"};"
""
"				ActiveFunction = 0;"
"			</script>"
"		</window>"
"	</interface>"
"</mml>";

class HotKeyPrefsDialog : public DialogQueryBase
{
public:
	virtual bool	__stdcall	GetDialogSource( DialogSource& toHere ) const
	{
		PackageMemoryPtr pPackage;
		if( !pPackage->SetRecord( (void*)ConfigDialogue, strlenof(ConfigDialogue), "HotKeyPrefs.xml") )
			return false;

		toHere.wsInterfaceName	= "teamsonique/sonique2_hot_prefs/1.0";
		toHere.asSourceName		= "HotKeyPrefs.xml";
		toHere.wsWindowName		= "HotKeyPrefs";
		toHere.pPackage = pPackage;
		return true;
	}
	
	HotKeyPrefsDialog() { 	MGENERIC_CONSTRUCTOR(); }
	

	IMPLEMENT_REFERENCECOUNT()
	IMPLEMENT_QUERYMODULE()
	
};


//*************************************************************************
// non-virtuals functions
//*************************************************************************
SQ2_Audio_HotKey_dll::SQ2_Audio_HotKey_dll()
{
	MGENERIC_CONSTRUCTOR();
	m_bAbort = false;
	m_bReloadSettings = false;

	// start the thread
	m_Thread.Start( &WatcherThread, priority_normal, this, "HotKeyThread" );
}

SQ2_Audio_HotKey_dll::~SQ2_Audio_HotKey_dll()
{
	m_AbortSignal.SetSignal();
	m_bAbort = true; 
	m_bReloadSettings = false;
	m_Thread.WaitForDeath();
}


int32 SQ2_Audio_HotKey_dll::WatcherThread(void* param) 
{
	SQ2_Audio_HotKey_dll* blah = (SQ2_Audio_HotKey_dll*) param;
	blah->m_bReloadSettings = true;
	while	(blah->m_bReloadSettings)
	{
		blah->m_bReloadSettings = false;
		Watcher(param);
	}
	return 0;
}

char SQ2_Audio_HotKey_dll::GetKey(uint32 parCode)
{
	if (parCode < 26) // alpha
		return (char)(parCode + 'A');
	else if (parCode-26 < 10) // digit
		return (char)(parCode-26 + '0');
	else if (parCode-26-10 < 4) // arrows
		return (char)(parCode-26-10+VK_LEFT);
	else	// space
		return VK_SPACE;

}

int32 SQ2_Audio_HotKey_dll::Watcher(void* param) 
{
	PreferencesPtr pPrefs           = PreferencesPtr();
	RefPtr<MConsoleServer> pConsole = ConsoleServerPtr();
	uint8 i;

	
	enum {F_Play=0, F_Stop, F_Next, F_Prev, F_Mute, F_VolU, F_VolD, F_Count};
	UINT Mods[] = {MOD_ALT | MOD_SHIFT,		// play
									 MOD_ALT | MOD_SHIFT,		// stop
									 MOD_ALT | MOD_SHIFT,		// next
									 MOD_ALT | MOD_SHIFT,		// prev
									 MOD_ALT | MOD_SHIFT,		// mute
									 MOD_ALT | MOD_SHIFT,		// vold
									 MOD_ALT | MOD_SHIFT};	// volu
	
	
	char Keys[] = {'P', 'S', 'K', 'L', 'M', 'U', 'D'};
	wchar_t *FunctionName[] = {
		L"play", L"stop", L"next", L"prev", L"mute", L"volu", L"vold"};

	if (SUCCEEDED(pPrefs->OpenSection(L"sonique/gui/player/default/HotKey")))
	{
		WString wsMod("mod");
		WString wsKey("key");
		int32 locModTmp;

		WString locAttribName;
		for (i=0; i<F_Count; i++)
		{
			pPrefs->PrepInt(WString(FunctionName[i]) + wsMod, Mods[i]);
			pPrefs->PrepInt(WString(FunctionName[i]) + wsKey, Keys[i]-'A');

			if (SUCCEEDED(pPrefs->GetInt(WString(FunctionName[i]) + wsMod, locModTmp)))
				Mods[i] = locModTmp;
			if (SUCCEEDED(pPrefs->GetInt(WString(FunctionName[i]) + wsKey, locModTmp)))
				Keys[i] = GetKey(locModTmp);
		}

	} else {
		pConsole->Output(console_error, L"Could not open hotkey prefs\r\n");

	}

	ATOM Atoms[F_Count] = {
		GlobalAddAtom("SONIQUE2HOTKEYDLL_PLAY"),
		GlobalAddAtom("SONIQUE2HOTKEYDLL_STOP"),
		GlobalAddAtom("SONIQUE2HOTKEYDLL_NEXT"),
		GlobalAddAtom("SONIQUE2HOTKEYDLL_PREV"),
		GlobalAddAtom("SONIQUE2HOTKEYDLL_MUTE"),
		GlobalAddAtom("SONIQUE2HOTKEYDLL_VOLU"),
		GlobalAddAtom("SONIQUE2HOTKEYDLL_VOLD")};

	HWND hwnd = FindWindow("Sonique2 Window Class", NULL);
	MSG  msg; 
	BOOL bRet;

	SQ2_Audio_HotKey_dll* blah = (SQ2_Audio_HotKey_dll*) param;

	long locRes = TRUE;
//	WString locError;
	for (i=0; i<F_Count; i++)	
	{
		if (!RegisterHotKey(NULL, Atoms[i], Mods[i], Keys[i]))
		{
			WString locCurrentError;
			locCurrentError.print(L"Cannot register hotkey %s : %c with mods %i. ", FunctionName[i], Keys[i], Mods[i]);
			pConsole->FPrint(console_error, locCurrentError);
		}
	}

	long AppCommands[] = {
		APPCOMMAND_MEDIA_PLAY_PAUSE,
		APPCOMMAND_MEDIA_STOP,
		APPCOMMAND_MEDIA_NEXTTRACK,
		APPCOMMAND_MEDIA_PREVIOUSTRACK,
		APPCOMMAND_VOLUME_MUTE,
		APPCOMMAND_VOLUME_UP,
		APPCOMMAND_VOLUME_DOWN};
	
	while( true )
	{ 
		if ((blah->m_bAbort == true) || 
				(blah->m_bReloadSettings == true) || 
				(blah->m_AbortSignal.WaitForSignal(1) != signal_timeout))
			break;

		bRet = PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );

		if (bRet > 0) 
		{
			if (msg.message == WM_HOTKEY)
			{
				bool locNotFound = true;
				uint8 j=0;
				while ((locNotFound) && (j<F_Count))
				{
					if (msg.wParam == Atoms[j])
					{
						PostMessage(hwnd, WM_APPCOMMAND, NULL, MAKESONCOMMAND(AppCommands[j]));
						locNotFound = false;
					}

					j++;
				}
			}
			
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

	}

	for (i=0; i<F_Count; i++)	
	{
		UnregisterHotKey(NULL,Atoms[i]);
		GlobalDeleteAtom(Atoms[i]);
	}
	
	return 0;
}

//*************************************************************************
// non-virtuals functions
//*************************************************************************
MRESULT SQ2_Audio_HotKey_dll::CreateInstance( const MID& mInterface, void** ppToHere,  MObject* /*pOuter*/ )
{
	if( mInterface == MID_MEDIAFILTER_HOT|| mInterface == MID_MEDIAFILTER)
	{
		*ppToHere = (MMediaFilter*)new HotKeyFilter(this);
	}
	else if( mInterface == MID_DIALOG_QUERY_OBJECT )
	{
		DialogQueryBase* pDialogQuery = new HotKeyPrefsDialog();
		*ppToHere = pDialogQuery;
	} 
	else
		return GetBasicError(ERR_BASIC_UNKNOWNOBJECT);

	if( *ppToHere )
	{
		((MGeneric*)(*ppToHere))->AddRef();
		return M_OK;
	}

	return GetBasicError(ERR_BASIC_OUTOFMEMORY);
}


//*************************************************************************
// Main Exports
//*************************************************************************
extern "C" __declspec (dllexport) 
MRESULT RegisterMIA( bool bRegister, MMiaControl* pControl, const wchar_t* wcPath )
{
	//GFID_MEDIA_MISC  - change it from a DSP to a MISC plugin once the support is in place.
	if( SUCCEEDED(pControl->RegisterIFactory(bRegister, FID_SQ2_AUDIO_HOT, wcPath, "Global Hot Key")) &&
		SUCCEEDED(pControl->RegisterGenericFactory(bRegister, GFID_MEDIAFILTER_DSP, FID_SQ2_AUDIO_HOT, "Global Hot Key")) )
		return M_OK;

	return GetBasicError(ERR_BASIC_UNKNOWNERROR);
}

extern "C" __declspec (dllexport) 
MRESULT MIFactory(const MID& thisFID,MIFactory** ppToHere)
{
	if( thisFID == FID_SQ2_AUDIO_HOT )
	{
		SQ2_Audio_HotKey_dll* pHotKeyAudio = new SQ2_Audio_HotKey_dll;
		pHotKeyAudio->AddRef();
		*ppToHere = pHotKeyAudio;
		return M_OK;
	}
	
	return GetBasicError(ERR_BASIC_UNKNOWNOBJECT);
}


static int32 g_nAttachCount;
extern "C" __declspec (dllexport)
uint32 DLLAttach( int32 nAttach )
{
	if( nAttach == 1 )
	{
		// first attach event?
		if( AtomicIncrement( (long*)&g_nAttachCount ) == 1 )
		{
			if( !InitMIA() )
				return false;
		}
	}
	if( nAttach == -1 )
	{
		// last detach
		if( AtomicDecrement( (long*)&g_nAttachCount ) == 0 )
		{
			DeInitMIA();
		}
	}
		
	return true; // it worked
}
