/*
**	SongInfoWin.c
**
**	Copyright (C) 1993,94,95,96 Bernardo Innocenti
**
**	Handle Song Information panel.
*/

#include <exec/nodes.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/xmodule.h>

#include "XModulePriv.h"
#include "Gui.h"


/* Gadgets IDs */

enum
{
	GD_SongInfoGroup0,
		GD_SongInfoGroup1,
			GD_SongList,
			GD_SongInfoGroup2,
				GD_NewSong,
				GD_OpenSong,
				GD_SaveSong,
				GD_DelSong,
		GD_SongInfoGroup3,
			GD_SongName,
			GD_AuthorName,
		GD_SongInfoGroup4,
			GD_SongInfoGroup5,
				GD_Tempo,
				GD_Speed,
				GD_Restart,
			GD_SongInfoGroup6,
				GD_SongLength,
				GD_Patterns,
				GD_Tracks,
		GD_SongInfoGroup7,
			GD_TotalSize,
			GD_InstSize,

	SongInfo_CNT
};




/* Local function Prototypes */

static void SongInfoPostOpen	(void);

static void SongListClicked		(struct WinUserData *wud);
static void SongNameClicked		(struct WinUserData *wud);
static void AuthorNameClicked	(struct WinUserData *wud);
static void TempoClicked		(struct WinUserData *wud);
static void SpeedClicked		(struct WinUserData *wud);
static void RestartClicked		(struct WinUserData *wud);
static void NewSongClicked		(struct WinUserData *wud);
static void OpenSongClicked		(struct WinUserData *wud);
static void DelSongClicked		(struct WinUserData *wud);
static void SaveSongClicked		(struct WinUserData *wud);

static void SongInfoMiMergeSongs	(void);
static void SongInfoMiJoinSongs		(void);
static void SongInfoMiClearSong		(void);



static struct NewMenu SongInfoNewMenu[] =
{
	NM_TITLE, (STRPTR)MSG_SONG_MEN, NULL, 0, NULL, NULL,
	NM_ITEM, (STRPTR)MSG_MERGE_SONGS_MEN, "M", 0, 0L, (APTR)SongInfoMiMergeSongs,
	NM_ITEM, (STRPTR)MSG_JOIN_SONGS_MEN, "J", 0, 0L, (APTR)SongInfoMiJoinSongs,
	NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
	NM_ITEM, (STRPTR)MSG_CLEAR_MEN, "K", 0, 0L, (APTR)SongInfoMiClearSong,
	NM_END, NULL, NULL, 0, 0L, NULL
};



static ULONG SongInfoArgs[] =
{
	HGROUP_KIND, 0,
		LISTVIEW_KIND,	(ULONG)SongListClicked,		0,	NULL, TAG_DONE,
		VGROUP_KIND, 0,
			BUTTON_KIND,	(ULONG)NewSongClicked,	MSG_UNDERSCORE_NEW_GAD,	TAG_DONE,
			BUTTON_KIND,	(ULONG)OpenSongClicked,	MSG_OPEN_GAD,			TAG_DONE,
			BUTTON_KIND,	(ULONG)SaveSongClicked,	MSG_SAVE_GAD,			TAG_DONE,
			BUTTON_KIND,	(ULONG)DelSongClicked,	MSG_DEL_GAD,			TAG_DONE,
			ENDGROUP_KIND,
		ENDGROUP_KIND,
	VGROUP_KIND,	BBFT_RIDGE,
		STRING_KIND,	(ULONG)SongNameClicked,		MSG_SONG_NAME_GAD,		31,	TAG_DONE,
		STRING_KIND,	(ULONG)AuthorNameClicked,	MSG_AUTHOR_NAME_GAD,	63,	TAG_DONE,
		ENDGROUP_KIND,
	HGROUP_KIND,	BBFT_RIDGE,
		VGROUP_KIND, 0,
			INTEGER_KIND,	(ULONG)TempoClicked,	MSG_DEF_TEMPO_GAD,	3,	TAG_DONE,
			INTEGER_KIND,	(ULONG)SpeedClicked,	MSG_DEF_SPEED_GAD,	3,	TAG_DONE,
			INTEGER_KIND,	(ULONG)RestartClicked,	MSG_RESTART_GAD,	3,	TAG_DONE,
			ENDGROUP_KIND,
		VGROUP_KIND, 0,
			NUMBER_KIND, MSG_LENGHT_GAD,		3, TAG_DONE,
			NUMBER_KIND, MSG_NUM_PATTS_GAD,		3, TAG_DONE,
			NUMBER_KIND, MSG_NUM_TRACKS_GAD,	3, TAG_DONE,
			ENDGROUP_KIND,
		ENDGROUP_KIND,
	VGROUP_KIND, BBFT_RIDGE,
		NUMBER_KIND, MSG_TOT_MOD_SIZE_GAD,	7, TAG_DONE,
		NUMBER_KIND, MSG_TOT_INST_SIZE_GAD,	7, TAG_DONE,
		ENDGROUP_KIND,
	ENDGROUP_KIND
};



XDEF LONG SongInfoWinTags[] =
{
	XMWIN_NewMenu,		(LONG)SongInfoNewMenu,
	XMWIN_LayoutArgs,	(LONG)SongInfoArgs,
	XMWIN_GCount,		SongInfo_CNT,
	XMWIN_Title,		MSG_SONGINFO_TITLE,
	XMWIN_WindowFlags,	WFLG_CLOSEGADGET,
	XMWIN_IDCMPFlags,	NUMBERIDCMP | STRINGIDCMP | LISTVIEWIDCMP | INTEGERIDCMP | BUTTONIDCMP | IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_MENUPICK,
	XMWIN_PostOpenFunc,	(LONG)SongInfoPostOpen,
	XMWIN_DropIconFunc,	(LONG)ToolBoxDropIcon,
	XMWIN_HelpNode,		(LONG)"SongInfo",
	TAG_DONE
};



static void SongInfoPostOpen (void)
{
	UpdateSongInfoList();
	UpdateSongInfo();
}



/**********************/
/* SongInfo Functions */
/**********************/


GLOBALCALL void DetatchSongInfoList (void)

/* Detatches the SongInfo list from the listview gadget.
 * Call UpdateSongInfoList() to re-attach it when you are done modifying it.
 */
{
	struct WinUserData *wud = WDescr[WID_SONGINFO].Wud;

	if (wud && wud->Win)
		GT_SetGadgetAttrs (wud->Gadgets[GD_SongList], wud->Win, NULL,
			GTLV_Labels, ~0,
			TAG_DONE);
}



GLOBALCALL void UpdateSongInfoList (void)

/* Updates the SongInfo listview gadget */
{
	struct WinUserData *wud = WDescr[WID_SONGINFO].Wud;

	if (wud && wud->Win)
	{
#ifdef OS30_ONLY
		ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
#else
		/* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */

		/* Try to get the shared semaphore */
		if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
			/* Check if we can get the exclusive version */
			if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
				/* Oh well, wait for the shared lock */
				ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);
#endif /* OS30_ONLY */

		if (XModuleBase->xm_CurrentSong)
		{
			struct SongInfo *tmp = (struct SongInfo *)XModuleBase->xm_Songs.mlh_Head;
			ULONG songnum = 0;

			/* Find current song number */

			while (tmp != XModuleBase->xm_CurrentSong)
			{
				tmp = (struct SongInfo *)tmp->Link.ln_Succ;
				songnum++;
			}

			GT_SetGadgetAttrs (wud->Gadgets[GD_SongList], wud->Win, NULL,
				GTLV_Labels,		&XModuleBase->xm_Songs,
				GTLV_Selected,		songnum,
				GTLV_MakeVisible,	songnum,
				TAG_DONE);
		}

		ReleaseSemaphore (&XModuleBase->xm_BaseLock);
	}
}



GLOBALCALL void UpdateSongInfo (void)

/* Update information on the current song */
{
	struct WinUserData *wud = WDescr[WID_SONGINFO].Wud;
	struct SongInfo *si;
	DB(ULONG millisecs);

	if (wud && wud->Win)
		if (si = xmLockActiveSong (SM_SHARED))
		{
			SetGadgets (wud,
				GD_SongName,	si->Title,
				GD_AuthorName,	si->Author,
				GD_Tempo,		si->GlobalTempo,
				GD_Speed,		si->GlobalSpeed,
				GD_SongLength,	si->Length,
				GD_Patterns,	si->NumPatterns,
				GD_Tracks,		si->MaxTracks,
				GD_Restart,		si->RestartPos,
				GD_TotalSize,	CalcSongSize (si),
				GD_InstSize,	CalcInstSize (si),
				-1);

			/* TODO */
			DB(millisecs = CalcSongTime (si));
			DB(xmDisplayMessage (XMDMF_DEBUG, "Song Time: %02ld:%02ld", millisecs / 60000, (millisecs / 1000) % 60));

			ReleaseSemaphore (&si->Lock);
		}

	UpdateInstrList();
	UpdatePatternList();
}



/********************/
/* SongInfo Gadgets */
/********************/


static void SongListClicked (struct WinUserData *wud)
{
	WORD i;
	struct SongInfo *tmp;

	ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);

	tmp = (struct SongInfo *)XModuleBase->xm_Songs.mlh_Head;

	for (i = IntuiMsg.Code; (i > 0) && tmp ; i--)
		tmp = (struct SongInfo *)tmp->Link.ln_Succ;

	if (tmp) ObtainSemaphoreShared (&tmp->Lock);
	ReleaseSemaphore (&XModuleBase->xm_BaseLock);
	if (tmp)
	{
		xmActivateSong (tmp);
		ReleaseSemaphore (&tmp->Lock);
	}
}



static void SongNameClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong(SM_EXCLUSIVE))
	{
		DetatchSongInfoList();

		SetAttrs (si,
			SNGA_Title,	GetString (wud->Gadgets[GD_SongName]),
			TAG_DONE);

		ReleaseSemaphore (&si->Lock);

		UpdateSongInfoList();
	}
}



static void AuthorNameClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong(SM_EXCLUSIVE))
	{
		SetAttrs (si,
			SNGA_Author,	GetString (wud->Gadgets[GD_AuthorName]),
			TAG_DONE);

		ReleaseSemaphore (&si->Lock);
	}
}



static void TempoClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong(SM_EXCLUSIVE))
	{
		SetAttrs (si,
			SNGA_GlobalTempo,	GetNumber (wud->Gadgets[GD_Tempo]),
			TAG_END);
		GT_SetGadgetAttrs (wud->Gadgets[GD_Tempo], wud->Win, NULL,
			GTIN_Number, si->GlobalTempo,
			TAG_DONE);

		ReleaseSemaphore (&si->Lock);
	}
}



static void RestartClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong(SM_EXCLUSIVE))
	{
		SetAttrs (si,
			SNGA_RestartPos, GetNumber (wud->Gadgets[GD_Restart]),
			TAG_END);
		GT_SetGadgetAttrs (wud->Gadgets[GD_Restart], wud->Win, NULL,
			GTIN_Number, si->RestartPos,
			TAG_DONE);

		ReleaseSemaphore (&si->Lock);
	}
}



static void SpeedClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong(SM_EXCLUSIVE))
	{
		SetAttrs (si,
			SNGA_GlobalSpeed, GetNumber (wud->Gadgets[GD_Speed]),
			TAG_END);
		GT_SetGadgetAttrs (wud->Gadgets[GD_Speed], wud->Win, NULL,
			GTIN_Number, si->GlobalSpeed,
			TAG_DONE);

		ReleaseSemaphore (&si->Lock);
	}
}



static void NewSongClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmCreateSong (
		SNGA_ReadyToUse,	TRUE,
		XMSNG_AddToList,	-1,
		XMSNG_Active,		TRUE,
		TAG_DONE))
		ReleaseSemaphore (&si->Lock);	/* This is for XMSNG_AddToList */
	else
		DisplayBeep (NULL);
}



static void OpenSongClicked (struct WinUserData *wud)
{
	StartFileRequest (FREQ_LOADMOD, ToolBoxOpenModule);
}



static void DelSongClicked (struct WinUserData *wud)
{
	if (ShowRequestArgs (MSG_DISCARD_CURRENT_SONG, MSG_YES_OR_NO, NULL))
	{
		struct SongInfo *si;

		if (si = xmLockActiveSong (SM_EXCLUSIVE))
			xmDeleteSong (si);

		if (!XModuleBase->xm_CurrentSong)
			NewSongClicked(wud);
	}
}


static void SaveSongClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	LockWindows();

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		LastErr = xmSaveModuleA (si, si->Path, NULL, NULL);
		ReleaseSemaphore (&si->Lock);
	}

	UnlockWindows();
}



/******************/
/* SongInfo Menus */
/******************/

static void SongInfoMiMergeSongs (void)
{
	struct SongInfo *si1, *si2, *si3;

	LockWindows();

	ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);

	if (si1 = XModuleBase->xm_CurrentSong)
	{
		si2 = (struct SongInfo *)si1->Link.ln_Succ;

		if (si2->Link.ln_Succ)
		{
			/* Risking a deadlock??  Hmmm... No!
			 * Shared locks do not prevent other tasks from
			 * locking these two songs.
			 */
			ObtainSemaphoreShared (&si1->Lock);
			ObtainSemaphoreShared (&si2->Lock);
			ReleaseSemaphore (&XModuleBase->xm_BaseLock);

			si3 = MergeSongs (si1, si2);

			ReleaseSemaphore (&si1->Lock);
			ReleaseSemaphore (&si2->Lock);

			if (si3) xmAddSongA (si3, si2, NULL);
		}
		else
		{
			ReleaseSemaphore (&XModuleBase->xm_BaseLock);
			xmDisplayMessageA (XMDMF_ERROR | XMDMF_USECATALOG,
				(APTR)MSG_MERGE_REQUIRES_TWO_SONGS, NULL);
		}
	}
	else
	{
		ReleaseSemaphore (&XModuleBase->xm_BaseLock);
		DisplayBeep (Scr);
	}

	ReleaseSemaphore (&XModuleBase->xm_BaseLock);
	UnlockWindows();
}



static void SongInfoMiJoinSongs (void)
{
	struct SongInfo *si1, *si2, *si3;

	LockWindows();

	ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);

	if (si1 = XModuleBase->xm_CurrentSong)
	{
		si2 = (struct SongInfo *)si1->Link.ln_Succ;

		if (si2->Link.ln_Succ)
		{
			/* Risking a deadlock??  Hmmm... No!
			 * Shared locks do not prevent other tasks from
			 * locking these two songs.
			 */
			ObtainSemaphoreShared (&si1->Lock);
			ObtainSemaphoreShared (&si2->Lock);
			ReleaseSemaphore (&XModuleBase->xm_BaseLock);

			si3 = JoinSongs (si1, si2);

			ReleaseSemaphore (&si1->Lock);
			ReleaseSemaphore (&si2->Lock);

			xmAddSongA (si3, si2, NULL);
		}
		else
		{
			ReleaseSemaphore (&XModuleBase->xm_BaseLock);
			xmDisplayMessageA (XMDMF_ERROR | XMDMF_USECATALOG,
				(APTR)MSG_JOIN_REQUIRES_TWO_SONGS, NULL);
		}
	}
	else
	{
		ReleaseSemaphore (&XModuleBase->xm_BaseLock);
		DisplayBeep (Scr);
	}

	UnlockWindows();
}



static void SongInfoMiClearSong (void)
{
	NewWindow (WID_CLEAR);
}

