#include "PlayList.h"

#include "WimpLib:mem.h"
#include "WimpLib:Choices.h"
#include "WimpLib:Exception.h"
#include "WimpLib:Message.h"
#include "WimpLib:Task.h"
#include "WimpLib:Utils.h"

#include "DigitalCD.h"
#include "Media.h"
#include "Options.h"
#include "Player.h"
#include "Soundswi.h"

#ifndef NULL
#define NULL (void*) 0
#endif

void throw_PlayList_PlayList(PlayList* This, const PlayList_Cmds* pcmds, const char* pName, MediaObject* pObject)
{
	This->pcmds = pcmds;
	This->m_pObject = pObject;
	This->nr_tracks = 0;
	This->m_Volume = 0;
	This->m_pPlayer = NULL;
	This->m_pName = throw_mem_allocstring(pName);

	if (!DigitalCD.MenuPlayers)
		DigitalCD.MenuPlayers = throw_New_Menu(Msg_Lookup("PlayT"), (char*) This->m_pName);
	else
		throw_Menu_InsertItem(DigitalCD.MenuPlayers, (char*) This->m_pName, -1);

	throw_PlayLists_Add(This);
}

void PlayList_NotPlayList(PlayList* This)
{
	if (This->m_pPlayer)
	{
		This->pcmds->pDeletePlayer(This);
		This->m_pPlayer = NULL;
	}
	mem_free(This->m_pName);
	This->m_pName = NULL;
}

bool PlayList_Init(PlayList* This, bool identify)
{
	return This->pcmds->pInit(This, identify);
}

int PlayList_GetTrackList(PlayList* This, const void*** pptracks, bool list, unsigned int shuffle)
{
	// Wait till player & playlist are linked to minimize updates
	if (This->m_pPlayer)
		return This->pcmds->pGetTrackList(This, pptracks, list, shuffle);

	return 0;
}

const char* PlayList_GetText(PlayList* This, void* ptrack, EMetaId id)
{
	return This->pcmds->pGetText(This, ptrack, id);
}

bool PlayList_PrepTrack(PlayList* This, void* ptrack)
{
	if (This->pcmds->pPrepTrack != NULL)
		return This->pcmds->pPrepTrack(This, ptrack);

	return false;
}

bool PlayList_PlayTrack(PlayList* This, void* ptrack)
{
	if (!This->pcmds->pPlayTrack(This, ptrack)) return false;

	PlayList_SetVolume(This, This->m_Volume);

	return true;
}

Media_Status PlayList_GetStatus(PlayList* This)
{
	return This->pcmds->pGetStatus(This);
}

void PlayList_Seek(PlayList* This, EMedia_Seek set)
{
	if (This->m_pObject == NULL) return;

	Media_Seek(This->m_pObject, set);
}

void PlayList_SetLoopMode(PlayList* This, Media_LoopMode mode)
{
	This->pcmds->pSetLoopMode(This, mode);
}

void PlayList_RefreshParams(PlayList* This)
{
	This->pcmds->pSetConfig(This, Media_CfgParams, NULL);
}

void PlayList_SetVolume(PlayList* This, int volume)
{
	MediaVolume vol;

	This->m_Volume = volume;

	vol.raw = volume;
	vol.sysscaled = Sound_SystemScaleVolume(volume);
	This->pcmds->pSetConfig(This, Media_CfgVolume, &vol);
}

int PlayList_GetVolume(PlayList* This)
{
	This->m_Volume = This->pcmds->pGetConfig(This, Media_CfgVolume);

	return This->m_Volume;
}

void PlayList_Play(PlayList* This, uint64_t pos)
{
	This->pcmds->pPlay(This, pos);

	PlayList_SetVolume(This, This->m_Volume);
}

void PlayList_SetStop(PlayList* This, bool bInError)
{
	if (This->m_pObject == NULL) return;

	This->pcmds->pStop(This, bInError);
}

void PlayList_SetPause(PlayList* This, bool on)
{
	if (This->m_pObject == NULL) return;

	Media_Freeze(This->m_pObject, on);
}

void PlayList_GetPosition(PlayList* This, void* ptrack, MediaPosition* pos)
{
	This->pcmds->pGetPosition(This, ptrack, pos);
}

int PlayList_GetTrackNummer(PlayList* This, void* ptrack)
{
	return This->pcmds->pGetTrackNummer(This, ptrack);
}

int PlayList_GetModuleHandle(PlayList* This, void* ptrack)
{
	return This->pcmds->pGetModuleHandle(This, ptrack);
}

int PlayList_GetHardwareType(PlayList* This)
{
	if (This->m_pObject == NULL) return -1;

	return Media_GetConfig(This->m_pObject, Media_CfgHardwareType);
}

unsigned int PlayList_GetUpdates(PlayList* This)
{
	if (This->m_pObject == NULL) return 0;

	return Media_GetUpdates(This->m_pObject);
}

void PlayList_ReportError(PlayList* This)
{
	if (This->m_pObject == NULL) return;

	App_ReportOSError(&This->m_pObject->m_Err);
}

Player* throw_PlayList_EnsurePlayer(PlayList* This, bool bForce)
{
	Player* pPlayer = NULL;

	if (Players_Count() > 0)
		pPlayer = Players_Get(0);

	// Don't return new player in single mode at startup if one was already requested
	if (!bForce && Options()->Player.bSinglePanelMode && pPlayer)
		return pPlayer;

	// Request for primary player?
	if (!This)
	{
		int nr;

		if (Options()->Player.bSinglePanelMode)
		{
			if (pPlayer)
				nr = Player_GetNr(pPlayer);
			else
				nr = FitInRange(Choices_ReadInt("Player x", "Player", 0), 0, PlayLists_Count() - 1);
		}
		else
			nr = Options()->Iconbar.Select[0];

		This = PlayLists_Get(nr);
	}

	if (!This->m_pPlayer)
	{
		// Save current parameters
		if (Options()->Player.bSinglePanelMode)
		{
			if (Players_Count() > 0)
				Player_WriteProfile(Players_Get(0));
		}

		This->m_pPlayer = This->pcmds->pthrow_CreatePlayer(This);
		Player_Refresh(This->m_pPlayer, player_refresh_initlist);
	}

	if (Options()->Player.bSinglePanelMode)
	{
		int i;

		// Remove other players
		for (i = 0; i < PlayLists_Count(); i++)
		{
			PlayList* pList = PlayLists_Get(i);

			if (This != pList) pList->pcmds->pDeletePlayer(pList);
		}

		App_BroadcastPrimaryController();
	}

	return This->m_pPlayer;
}

/*----------------------------------------------------------------------
 *- List ---------------------------------------------------------------
 *----------------------------------------------------------------------*/

static List* PlayLists = NULL;

void throw_PlayLists_PlayLists(void)
{
	PlayLists = New_List();
}

void PlayLists_NotPlayLists(void)
{
	Delete_List(PlayLists);
	PlayLists = NULL;
}

int PlayLists_Count(void)
{
	return List_Count(PlayLists);
}

void throw_PlayLists_Add(PlayList* plist)
{
	List_InsertBefore(PlayLists, NULL, plist);
}

PlayList* PlayLists_Get(int i) throws(index)
{
	return (PlayList*) List_Get(PlayLists, i);
}

Player* PlayLists_GetPlayer(int i) throws(index)
{
	if (Options()->Player.bSinglePanelMode)
		return Players_Get(0);
	else
	{
		PlayList* plist = (PlayList*) List_Get(PlayLists, i);

		if (plist) return plist->m_pPlayer;
	}

	return NULL;
}

int PlayLists_Find(PlayList* plist)
{
	return List_Find(PlayLists, 0, plist);
}
