#include "PListExt.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "WimpLib:Exception.h"
#include "WimpLib:Keyboard.h"
#include "WimpLib:mem.h"
#include "WimpLib:Message.h"
#include "WimpLib:Menu.h"
#include "WimpLib:Task.h"
#include "WimpLib:Timer.h"
#include "WimpLib:Utils.h"
#include "WimpLib:Window.h"

#include "Cmds.h"
#include "DCDswi.h"
#include "DigitalCD.h"
#include "Player.h"
#include "PlayList.h"
#include "SDswi.h"

struct PListExt
{
	PlayList  m_PlayList;
	char*     m_pTitle;
	char**    m_pTracks;
};

static PListExt* App_pPListExt = NULL;

#define Icon_LoopMode    24
#define Icon_ShuffleMode 25
#define Icon_IntroScan   26
#define Icon_ListMode    27
#define Icon_SetTrack    28

static int ExtPlayer_CheckCommand(void* handle, CmdID id)
{
	PListExt* This = handle;
	Player* pPlayer = This->m_PlayList.m_pPlayer;
	int state = 0;

	if (pPlayer)
	{
		switch(id)
		{
			case Cmd_Ctrl_Pause:
			case Cmd_Ctrl_RestartSection:
			case Cmd_Ctrl_RestartTrack:
			case Cmd_Ctrl_Rewind:
			case Cmd_Ctrl_Forward:
			case Cmd_Ctrl_PrevSection:
			case Cmd_Ctrl_NextSection:
			case Cmd_Ctrl_PrevLoop:
			case Cmd_Ctrl_NextLoop:
			case Cmd_Ctrl_Loop_NormalPlay:
			case Cmd_Ctrl_Loop_OnList:
			case Cmd_Ctrl_Loop_OnTrack:
			case Cmd_Ctrl_Loop_EndTrack:
			case Cmd_Ctrl_ToggleShuffle:
			case Cmd_Ctrl_ToggleProgram:
			case Cmd_Ctrl_ToggleCDProgram:
			case Cmd_Ctrl_ToggleIntroscan:
			case Cmd_Ctrl_PrevTimer:
			case Cmd_Ctrl_NextTimer:
				state = 0;
			break;
			default:
				state = Player_CheckCommand(pPlayer, id);
		}
	}
	else
		state = App_CheckCommand(pPlayer, id);

	return state;
}

static bool ExtPlayer_ExecCommand(void* handle, CmdID id)
{
	PListExt* This = handle;
	Player* pPlayer = This->m_PlayList.m_pPlayer;
	bool bRet = true;

	if (pPlayer)
		bRet = Player_ExecCommand(pPlayer, id);
	else
		bRet = App_ExecCommand(pPlayer, id);

	return bRet;
}

static const CmdHandler ExtPlayer_CmdHandler =
{ ExtPlayer_CheckCommand
, ExtPlayer_ExecCommand
};

static EListenerAction ExtPlayer_EventHandler(void* handle, const Event* e)
{
	Player* pPlayer = handle;
	PListExt* This = (PListExt*) Player_GetPlayList(pPlayer);

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			if (m->but & EBut_Menu)
			{
				if  (m->i != Icon_SetTrack)
				{
					CmdHandler_OpenMenu(&ExtPlayer_CmdHandler, This, "Ctrl_Snd");

					return EListenerAction_StopEvent;
				}
				return EListenerAction_ContinueEvent;
			}

			switch(m->i)
			{
				case Icon_LoopMode:
				case Icon_ShuffleMode:
				case Icon_IntroScan:
				case Icon_ListMode:
				{
					return EListenerAction_StopEvent;
				}
				break;
			}

			return EListenerAction_ContinueEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			// Ignore key presses if shortcuts are disabled
			// unless a full screen plug-in forwards them to use
			if (!Player_AllowsShortcuts(pPlayer)
			&&  (DCDUtils_GetFullScreenPlugIn() == -1))
				return EListenerAction_ContinueEvent;

			if (CmdHandler_KeyPressed(&ExtPlayer_CmdHandler, This, "Ctrl_Snd", key->code))
				return EListenerAction_StopEvent;

			return EListenerAction_ContinueEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static void ExtPlayer_ReloadSkin(Player* pPlayer)
{
	try
	{
		throw_Player_ReloadSkin(pPlayer);
		throw_Window_RegisterEventHandler(Player_GetWindow(pPlayer, 0), ExtPlayer_EventHandler, pPlayer, true);
		throw_Window_RegisterEventHandler(Player_GetWindow(pPlayer, 1), ExtPlayer_EventHandler, pPlayer, true);
	}
	catch
	{
		App_ReportException();
	}
	catch_end
}

static Player* throw_PListExt_CreatePlayer(PlayList* pPlayList)
{
	Player* pPlayer = throw_New_Player(pPlayList, "", (FPlayer_ReloadSkin) ExtPlayer_ReloadSkin, 0);

	try
	{
		Player_SetCmdHandler(pPlayer, &ExtPlayer_CmdHandler, pPlayList);
		throw_Window_RegisterEventHandler(Player_GetWindow(pPlayer, 0), ExtPlayer_EventHandler, pPlayer, true);
		throw_Window_RegisterEventHandler(Player_GetWindow(pPlayer, 1), ExtPlayer_EventHandler, pPlayer, true);

		pPlayList->m_pPlayer = pPlayer;

		Player_SetLoopMode(pPlayer, loop_section_loop);
		Player_SetShuffleMode(pPlayer, 0);
		Player_SetIntroScan(pPlayer, false);
		Player_SetListMode(pPlayer, false);
		Player_Refresh(pPlayer, player_refresh_tracklist);
	}
	catch
	{
		pPlayList->m_pPlayer = NULL;
		Delete_Player(pPlayer);
		throw_current();
	}
	catch_end

	return pPlayer;
}

static void PListExt_DeletePlayer(PlayList* pPlayList)
{
	Player* pPlayer = pPlayList->m_pPlayer;

	if (!pPlayer) return;

	Window_DeRegisterEventHandler(Player_GetWindow(pPlayer, 0), ExtPlayer_EventHandler, pPlayer);
	Window_DeRegisterEventHandler(Player_GetWindow(pPlayer, 1), ExtPlayer_EventHandler, pPlayer);
	Delete_Player(pPlayList->m_pPlayer);
	pPlayList->m_pPlayer = NULL;
}

/*-------------------------------------------------------------------------------*/

static bool PListExt_Init(PlayList* pPlayList, bool identify)
{
	IGNORE(pPlayList);
	IGNORE(identify);

	return true;
}

static int PListExt_GetTrackList(PlayList* pPlayList, const void*** pptracks, bool bList, unsigned int shuffle)
{
	int i;

	IGNORE(bList);
	IGNORE(shuffle);

	*pptracks = mem_alloc(pPlayList->nr_tracks * sizeof(void*));
	if (!*pptracks)
	{
		App_ReportError("NoMem");
		return 0;
	}

	for (i = 0; i < pPlayList->nr_tracks; i++)
		(*pptracks)[i] = (void*) (i + 1);

	return pPlayList->nr_tracks;
}

static const char* PListExt_GetText(PlayList* pPlayList, void* ptrack, EMetaId id)
{
	PListExt* This = (PListExt*) pPlayList;
	const char* text = NULL;

	if ((id == EMetaId_StreamName) || (id == EMetaId_StreamTitle))
	{
		if (ptrack)
		{
			int i = ((int) ptrack) - 1;
			text = This->m_pTracks[i];
		}
		else text = This->m_pTitle;
	}
	else if (id == EMetaId_StreamAlbum)
		text = This->m_pTitle;

	if (!text) text = &nil;

	return text;
}

static void PListExt_Play(PlayList* pPlayList, uint64_t pos)
{
	IGNORE(pPlayList);
	IGNORE(pos);
}

static void PListExt_Stop(PlayList* pPlayList, bool bInError)
{
	IGNORE(pPlayList);
	IGNORE(bInError);
}

static bool PListExt_PlayTrack(PlayList* pPlayList, void* ptrack)
{
	Player* pPlayer = pPlayList->m_pPlayer;

	if (!pPlayer) return false;

	Player_SetHardwareType(pPlayer, Media_HType_External);

	DCDUtils_SetIntInfo(Player_GetDCDUtilsHandle(pPlayer), DCD_Info_Source, ((int) ptrack) - 1);

	return true;
}

static Media_Status PListExt_GetStatus(PlayList* pPlayList)
{
	Player* pPlayer = pPlayList->m_pPlayer;

	if (!pPlayer) return status_empty;

	return Player_GetStatus(pPlayer);
}

static void PListExt_SetLoopMode(PlayList* pPlayList, Media_LoopMode mode)
{
	// nothing to do
	IGNORE(pPlayList);
	IGNORE(mode);
}

static void PListExt_GetPosition(PlayList* pPlayList, void* ptrack, MediaPosition* pos)
{
	IGNORE(pPlayList);
	IGNORE(ptrack);

	pos->driver.start = 0;
	pos->driver.pos   = 0;
	pos->driver.end   = 1;
	pos->volume.pos      = 0;
	pos->volume.len      = 60*60*100;
	pos->volume.sections = 1;
	pos->section.pos   = pos->volume.pos;
	pos->section.len   = pos->volume.len;
	pos->section.nr    = 0;
}

static int PListExt_GetTrackNummer(PlayList* pPlayList, void* ptrack)
{
	IGNORE(pPlayList);
	IGNORE(ptrack);

	return -1; // Let player do the job
}

static void PListExt_SetConfig(PlayList* pPlayList, Media_Config type, const void* value)
{
	Player* pPlayer = pPlayList->m_pPlayer;

	if (!pPlayer) return;

	if (type == Media_CfgVolume)
	{
		MediaVolume* pvol = (MediaVolume*) value;

		DCDUtils_SetIntInfo(Player_GetDCDUtilsHandle(pPlayer), DCD_Info_MainVolume, pvol->raw >> 8);
		DCDUtils_SetIntInfo(Player_GetDCDUtilsHandle(pPlayer), DCD_Info_ScaleVolume, pvol->raw >> 8);
	}
}

static unsigned int PListExt_GetConfig(PlayList* pPlayList, Media_Config type)
{
	switch(type)
	{
		case PlayList_CfgAutoStart:
		{
			return false;
		}
		break;
		case PlayList_CfgSeekable:
		{
			return false;
		}
		break;
		case Media_CfgVolume:
		{
			return pPlayList->m_Volume;
		}
		break;
		default:
			return 0xffffffff;
	}
}

/*-------------------------------------------------------------------------------*/

static const PlayList_Cmds PListExt_Cmds =
{
	  PListExt_Init
	, PListExt_GetTrackList
	, PListExt_GetText
	, NULL
	, PListExt_PlayTrack
	, PListExt_GetStatus
	, PListExt_SetLoopMode
	, PListExt_Play
	, PListExt_Stop
	, PListExt_GetPosition
	, PListExt_GetTrackNummer
	, PListExt_GetTrackNummer
	, PListExt_SetConfig
	, PListExt_GetConfig
	, PlayList_GetUpdates
	, throw_PListExt_CreatePlayer
	, PListExt_DeletePlayer
};

static void Delete_PListExt(PListExt* This)
{
	if (This)
	{
		int i;

		mem_free(This->m_pTitle);
		if (This->m_pTracks)
		{
			for(i = 0; i < This->m_PlayList.nr_tracks; i++)
				mem_free(This->m_pTracks[i]);
			mem_free(This->m_pTracks);
		}

		PlayList_NotPlayList(&This->m_PlayList);

		mem_free(This);
	}
}

static PListExt* throw_New_PListExt(void)
{
	PListExt* This = throw_mem_calloc(1, sizeof(*This));
	int i;

	try
	{
		throw_PlayList_PlayList(&This->m_PlayList, &PListExt_Cmds, Msg_Lookup("Play2"), NULL);

		// set track lists
		This->m_PlayList.nr_tracks = 1 + SoundDriver_GetDriversCount();

		This->m_pTitle = throw_mem_allocstring(Msg_Lookup("ExtT"));
		This->m_pTracks = throw_mem_calloc(This->m_PlayList.nr_tracks, sizeof(char*));

		This->m_pTracks[0] = throw_mem_allocstring(Msg_Lookup("Ext0"));
		for(i = 1; i < This->m_PlayList.nr_tracks; i++)
			This->m_pTracks[i] = throw_mem_allocstring(SoundDriver_GetDriver(i));
	}
	catch
	{
		Delete_PListExt(This);
		throw_current();
	}
	catch_end

	return This;
}

PListExt* PListExt_Get(void)
{
	if (!App_pPListExt) App_pPListExt = throw_New_PListExt();

	return App_pPListExt;
}

void PListExt_Release(void)
{
	if (App_pPListExt)
	{
		Delete_PListExt(App_pPListExt);
		App_pPListExt = NULL;
	}
}
