#include "EditCDDesc.h"

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

#include "WimpLib:Caret.h"
#include "WimpLib:Desktop.h"
#include "WimpLib:Display.h"
#include "WimpLib:Exception.h"
#include "WimpLib:File.h"
#include "WimpLib:Hourglass.h"
#include "WimpLib:mem.h"
#include "WimpLib:Menu.h"
#include "WimpLib:Message.h"
#include "WimpLib:Slider.h"
#include "WimpLib:Task.h"
#include "WimpLib:Template.h"
#include "WimpLib:Window.h"
#include "WimpLib:WList.h"
#include "WimpLib:WStringList.h"

#include "CDDesc.h"
#include "DigitalCD.h"
#include "Options.h"
#include "PrefList.h"
#include "Setup.h"
#include "DocEvents.h"
#include "UserEvents.h"

#define Prop_AlbumId     0x00001
#define Prop_Album       0x00002
#define Prop_Date        0x00004
#define Prop_Box         0x00008
#define Prop_Artist      0x00010
#define Prop_Collective  0x00020
#define Prop_PrefList    0x00040
#define Prop_AllProps        0x0000007f
#define Prop_SingleObject    0x80000000
#define Prop_RefreshContents 0x40000000
#define Prop_RefreshState    0x20000000

typedef unsigned int Props;

#define EIcon_ColorMask   0xf0000000
#define EIcon_ColorNormal 0x00000000
#define EIcon_ColorDiffer 0x20000000

#define EIcon_FillColorMask   0xf0000020
#define EIcon_FillColorNormal 0x10000000
#define EIcon_FillColorDiffer 0x20000020

typedef struct
{
	HWind             m_wnd;
	List*             m_pDescs;
	const CDDesc*     m_pRefDesc;
	Props             m_AllowedProps;
	Props             m_DifferingProps;
	Props             m_ModifiedProps;
	unsigned int      m_flags;
	struct
	{
		unsigned long id;
		const char*   album;
		const char*   date;
		const char*   box;
		const char*   artist;
		const char*   collective;
	} m_ref;
	CPrefList*        m_pPrefList;
	WStringList*      m_pWCollectiveList;
	WStringList*      m_pWArtistList;
	WStringList*      m_pWBoxList;
	WStringList*      m_pWAlbumList;
} EditCDDesc;

static EditCDDesc TheEditCDDesc = {HWind_None};

#define Icon_Set          0
#define Icon_Cancel       1
#define Icon_PrefList     2

#define Icon_AlbumId      4
#define Icon_Album        6
#define Icon_Date         8
#define Icon_Box         10
#define Icon_Artist      12
#define Icon_Collective  14

/**
 * Sets the reference desc to use for building the properties.
 * If the reference desc is not contained in the list of edited descs
 * the first one from the list is used as reference.
 *
 * @param  pRef  Pointer to the reference desc to use.
 */
static void EditCDDesc_SetRefDesc(EditCDDesc* This, const CDDesc* pRef)
{
	This->m_pRefDesc = NULL;

	if (pRef)
	{
		if (List_Find(This->m_pDescs, 0, pRef) != -1)
			This->m_pRefDesc = pRef;
	}

	if (!This->m_pRefDesc && (List_Count(This->m_pDescs) > 0))
		This->m_pRefDesc = List_Get(This->m_pDescs, 0);
}

/**
 * Refreshes the state of buttons
 */
static void EditCDDesc_RefreshButtons(EditCDDesc* This)
{
	bool b;

	b = !This->m_ModifiedProps;

	Icon_SetDimmed(This->m_wnd, Icon_Set, b);
}

/**
 * Refreshes the window contents for given properties
 *
 * @param  props  bitlist of properties to refresh
 *                + Prop_RefreshContents is the properties values are to be updated
 *                + Prop_RefreshState is the properties common/allowed status are to be updated.
 */

static void EditCDDesc_Refresh(EditCDDesc* This, Props props)
{
	bool b;

	if (props & Prop_AlbumId)
	{
		b = (This->m_AllowedProps & Prop_AlbumId) == 0;

		if (props & Prop_RefreshContents)
		{
			if (b)
				Icon_SetData(This->m_wnd, Icon_AlbumId, NULL);
			else
				Icon_Printf(This->m_wnd, Icon_AlbumId, "%ld", This->m_ref.id);
		}

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_AlbumId - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Album, b);
		}
	}

	if (props & Prop_Album)
	{
		b = (This->m_AllowedProps & Prop_Album) == 0;

		if (props & Prop_RefreshContents)
			WStringList_SetIconData(This->m_pWAlbumList, This->m_ref.album);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Album - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Album, b);
			Icon_SetState(This->m_wnd, Icon_Album
			             , (This->m_DifferingProps & Prop_Album)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Date)
	{
		b = (This->m_AllowedProps & Prop_Date) == 0;

		if (props & Prop_RefreshContents)
		{
			if (b)
				Icon_SetData(This->m_wnd, Icon_Date, NULL);
			else
				Icon_SetData(This->m_wnd, Icon_Date, This->m_ref.date);
		}

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Date - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Date, b);
			Icon_SetState(This->m_wnd, Icon_Date
			             , (This->m_DifferingProps & Prop_Date)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Box)
	{
		b = (This->m_AllowedProps & Prop_Box) == 0;

		if (props & Prop_RefreshContents)
			WStringList_SetIconData(This->m_pWBoxList, This->m_ref.box);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Box - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Box, b);
			Icon_SetState(This->m_wnd, Icon_Box
			             , (This->m_DifferingProps & Prop_Box)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Artist)
	{
		b = (This->m_AllowedProps & Prop_Artist) == 0;

		if (props & Prop_RefreshContents)
			WStringList_SetIconData(This->m_pWArtistList, This->m_ref.artist);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Artist - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Artist, b);
			Icon_SetState(This->m_wnd, Icon_Artist
			             , (This->m_DifferingProps & Prop_Artist)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Collective)
	{
		b = (This->m_AllowedProps & Prop_Collective) == 0;

		if (props & Prop_RefreshContents)
			WStringList_SetIconData(This->m_pWCollectiveList, This->m_ref.collective);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Collective - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Collective, b);
			Icon_SetState(This->m_wnd, Icon_Collective
			             , (This->m_DifferingProps & Prop_Collective)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_PrefList)
	{
		b = (This->m_AllowedProps & Prop_PrefList) == 0;

		Icon_SetDimmed(This->m_wnd, Icon_PrefList, b);
	}
}

/**
 * Builds the properties initial values and allowed status.
 */

static void EditCDDesc_BuildProperties(EditCDDesc* This)
{
	const CDDesc* pDesc;
	bool b;

	This->m_AllowedProps = Prop_AllProps;
	This->m_ModifiedProps = 0;

	if (List_Count(This->m_pDescs) > 1)
	{
		This->m_AllowedProps &= ~(Prop_AlbumId | Prop_PrefList);
	}
	else This->m_AllowedProps |= Prop_SingleObject;

	pDesc = This->m_pRefDesc;

	b = (This->m_AllowedProps & Prop_AlbumId) == 0;
	This->m_ref.id = b ? 0 : CDDesc_GetId(pDesc);

	b = (This->m_AllowedProps & Prop_Album) == 0;
	throw_mem_setstring(&This->m_ref.album, b ? NULL : CDDesc_GetMetaString(pDesc, EMetaId_StreamAlbum));

	b = (This->m_AllowedProps & Prop_Date) == 0;
	throw_mem_setstring(&This->m_ref.date, b ? NULL : CDDesc_GetMetaString(pDesc, EMetaId_StreamDate));

	b = (This->m_AllowedProps & Prop_Box) == 0;
	throw_mem_setstring(&This->m_ref.box, b ? NULL : CDDesc_GetMetaString(pDesc, EMetaId_StreamBox));

	b = (This->m_AllowedProps & Prop_Artist) == 0;
	throw_mem_setstring(&This->m_ref.artist, b ? NULL : CDDesc_GetMetaString(pDesc, EMetaId_StreamArtist));

	b = (This->m_AllowedProps & Prop_Collective) == 0;
	throw_mem_setstring(&This->m_ref.collective, b ? NULL : CDDesc_GetMetaString(pDesc, EMetaId_StreamCollective));

	EditCDDesc_Refresh(This, Prop_RefreshContents | Prop_AllProps);

	EditCDDesc_RefreshButtons(This);
}

/**
 * Takes note of the properties for which the values are not identical in all descs.
 */

static void EditCDDesc_BuildCommonStatus(EditCDDesc* This)
{
	if (This->m_AllowedProps & Prop_SingleObject)
	{
		This->m_DifferingProps = 0;
	}
	else
	{
		ListNode* pNode;
		const CDDesc* pDesc;
		Props commons = This->m_AllowedProps;

		// Check all allowed/non-modified properties to see
		// if their values correspond to the initial values.
		pNode = NULL;

		while (commons && (pNode = List_GetSuccessor(This->m_pDescs, pNode)) != NULL)
		{
			pDesc = List_GetNodeData(This->m_pDescs, pNode);

			if (commons & Prop_Album)
			{
				if (strcmp(nvl(This->m_ref.album), CDDesc_GetMetaString(pDesc, EMetaId_StreamAlbum)))
					commons &= ~Prop_Album;
			}

			if (commons & Prop_Date)
			{
				if (strcmp(nvl(This->m_ref.date), CDDesc_GetMetaString(pDesc, EMetaId_StreamDate)))
					commons &= ~Prop_Date;
			}

			if (commons & Prop_Box)
			{
				if (strcmp(nvl(This->m_ref.box), CDDesc_GetMetaString(pDesc, EMetaId_StreamBox)))
					commons &= ~Prop_Box;
			}

			if (commons & Prop_Artist)
			{
				if (strcmp(nvl(This->m_ref.artist), CDDesc_GetMetaString(pDesc, EMetaId_StreamArtist)))
					commons &= ~Prop_Artist;
			}

			if (commons & Prop_Collective)
			{
				if (strcmp(nvl(This->m_ref.collective), CDDesc_GetMetaString(pDesc, EMetaId_StreamCollective)))
					commons &= ~Prop_Collective;
			}
		}

		This->m_DifferingProps = This->m_AllowedProps & ~commons;
	}

	EditCDDesc_Refresh(This, Prop_RefreshState | Prop_AllProps);
}

/**
 * Takes note of the properties which were modified.
 */

static void EditCDDesc_CheckChanges(EditCDDesc* This, Props props)
{
	Props modifs = 0;
	Props updated;

	props &= This->m_AllowedProps;

	// Check values with references
	if (props & Prop_Album)
	{
		if (strcmp(nvl(This->m_ref.album), Icon_GetData(This->m_wnd, Icon_Album)))
			modifs |= Prop_Album;
	}

	if (props & Prop_Date)
	{
		if (strcmp(nvl(This->m_ref.date), Icon_GetData(This->m_wnd, Icon_Date)))
			modifs |= Prop_Date;
	}

	if (props & Prop_Box)
	{
		if (strcmp(nvl(This->m_ref.box), Icon_GetData(This->m_wnd, Icon_Box)))
			modifs |= Prop_Box;
	}

	if (props & Prop_Artist)
	{
		if (strcmp(nvl(This->m_ref.artist), Icon_GetData(This->m_wnd, Icon_Artist)))
			modifs |= Prop_Artist;
	}

	if (props & Prop_Collective)
	{
		if (strcmp(nvl(This->m_ref.collective), Icon_GetData(This->m_wnd, Icon_Collective)))
			modifs |= Prop_Collective;
	}

	// Any change compared to old status?
	updated = (modifs ^ This->m_ModifiedProps) & props;

	if (This->m_AllowedProps & Prop_SingleObject)
	{
		// reflect even reversion to original values
		if (updated)
		{
			This->m_ModifiedProps ^= updated;

			EditCDDesc_RefreshButtons(This);
		}
	}
	else
	{
		// reflect new changes but there is no reversion
		// to original value since any change affected old values
		updated &= modifs;

		if (updated)
		{
			This->m_ModifiedProps |= updated;
			This->m_DifferingProps &= ~updated;
			EditCDDesc_Refresh(This, Prop_RefreshState | updated);

			EditCDDesc_RefreshButtons(This);
		}
	}
}

static void EditCDDesc_ClearDescs(EditCDDesc* This)
{
	List_Clear(This->m_pDescs);
}

static bool EditCDDesc_OnOk(EditCDDesc* This)
{
	ListNode* pNode;
	const CDDesc* pDesc;
	int i = 0;
	int max = List_Count(This->m_pDescs);
	CCaret caret;

	// Checks
	pDesc = This->m_pRefDesc;
	caret.pos.w = This->m_wnd;
	caret.height = -1;
	caret.index = 0;

	if (This->m_AllowedProps & Prop_Date)
	{
		const char* date = Icon_GetData(This->m_wnd, Icon_Date);
		bool checked = false;

		caret.pos.i = Icon_Date;
		caret.index = strlen(date);

		if (date[0])
		{
			if ((date[0] >= '0') && (date[0] <= '9')
			&&  (date[1] >= '0') && (date[1] <= '9')
			&&  (date[2] >= '0') && (date[2] <= '9')
			&&  (date[3] >= '0') && (date[3] <= '9'))
			{
				if (date[4])
				{
					if ((date[4] == '-')
					&&  (date[5] >= '0') && (date[5] <= '1')
					&&  (date[6] >= '0') && (date[6] <= '9')
					&&  ((date[5] < '1') || (date[6] <= '2')))
					{
						int month = (date[5] - '0') * 10 + (date[6] - '0');

						if ((month >= 1) && (month <= 12))
						{
							if (date[7])
							{
								if ((date[7] == '-')
								&&  (date[8] >= '0') && (date[8] <= '3')
								&&  (date[9] >= '0') && (date[9] <= '9')
								&&  ((date[8] < '3') || (date[9] <= '1')))
								{
									int day = (date[8] - '0') * 10 + (date[9] - '0');

									if ((day >= 1)
									&&  (day <= 31)
									&&  ((month != 2) || (day <= 29))
									&&  (   (day < 31) || (month == 1) || (month == 3) || (month == 5)
									     || (month == 7) || (month == 8) || (month == 10) || (month == 12)))
									{
										checked = true;
									}
								}
							}
							else
							{
								checked = true;
							}
						}
					}
				}
				else
				{
					checked = true;
				}
			}
		}
		else
		{
			checked = true;
		}

		if (checked == false)
		{
			Caret_Set(&caret);
			App_ReportError("PrErr0");
			return false;
		}
	}

	// Set
	bool off = !WLib_Hourglass_IsOn();
	if (off) WLib_Hourglass_On();
	pNode = NULL;

	CDDescList* pDoc = CDDescList_Get();
	CDDescList_StartUpdate(pDoc);

	while ((pNode = List_GetSuccessor(This->m_pDescs, pNode)) != NULL)
	{
		CDDesc* pDesc = List_GetNodeData(This->m_pDescs, pNode);

		WLib_Hourglass_Percentage((100 * i++)/max);

		bool bModified = false;

		if (This->m_ModifiedProps & Prop_Album)
			bModified |= throw_CDDesc_SetMetaString(pDesc, EMetaId_StreamAlbum, Icon_GetData(This->m_wnd, Icon_Album));

		if (This->m_ModifiedProps & Prop_Date)
			bModified |= throw_CDDesc_SetMetaString(pDesc, EMetaId_StreamDate, Icon_GetData(This->m_wnd, Icon_Date));

		if (This->m_ModifiedProps & Prop_Box)
			bModified |= throw_CDDesc_SetMetaString(pDesc, EMetaId_StreamBox, Icon_GetData(This->m_wnd, Icon_Box));

		if (This->m_ModifiedProps & Prop_Artist)
			bModified |= throw_CDDesc_SetMetaString(pDesc, EMetaId_StreamArtist, Icon_GetData(This->m_wnd, Icon_Artist));

		if (This->m_ModifiedProps & Prop_Collective)
			bModified |= throw_CDDesc_SetMetaString(pDesc, EMetaId_StreamCollective, Icon_GetData(This->m_wnd, Icon_Collective));

		if (PrefList_IsModified(This->m_pPrefList))
		{
			PrefList_GetPrefList(This->m_pPrefList, CDDesc_GetPrefList(pDesc));
			bModified = true;
		}

		if (bModified) CDDesc_RefreshViews(pDesc, This, bModified);
	}

	CDDescList_EndUpdate(pDoc);

	if (off) WLib_Hourglass_Off();

	// Reset settings
	EditCDDesc_BuildProperties(This);
	EditCDDesc_BuildCommonStatus(This);
	PrefList_Reload(This->m_pPrefList);

	return true;
}

static void EditCDDesc_OnCancel(EditCDDesc* This)
{
	// Reset settings
	EditCDDesc_BuildProperties(This);
	EditCDDesc_BuildCommonStatus(This);
	PrefList_Reload(This->m_pPrefList);
}

static EListenerAction EditCDDesc_Listener(void* handle, const Event* e)
{
	EditCDDesc* This = handle;

	switch(e->Type)
	{
		case EEvent_OptionParentalLock:
		{
			if (Options()->Player.bParentalLock)
			{
				EditCDDesc_OnCancel(This);
				Window_Close(This->m_wnd);
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction EditCDDesc_EventHandler(void* handle, const Event* e)
{
	EditCDDesc* This = handle;

	switch(e->Type)
	{
		case EEvent_WindowClose:
		{
			PrefList_Hide(This->m_pPrefList);

			return EListenerAction_ContinueEvent;
		}
		break;
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			if (m->but & EBut_Menu)
				return EListenerAction_StopEvent;

			switch(m->i)
			{
				case Icon_Set:
				{
					if (EditCDDesc_OnOk(This)
					&&  (m->but & EBut_Select))
					{
						Window_Close(This->m_wnd);
					}
				}
				break;
				case Icon_Cancel:
				{
					EditCDDesc_OnCancel(This);
					if (m->but & EBut_Select)
					{
						Window_Close(This->m_wnd);
					}
				}
				break;
				case Icon_PrefList:
				{
					PrefList_Show(This->m_pPrefList, List_Get(This->m_pDescs, 0));
				}
				break;
			}

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

			switch(pkey->code)
			{
				case 0x1b: // Esc
				{
					EditCDDesc_OnCancel(This);
					Window_Close(This->m_wnd);
					return EListenerAction_StopEvent;
				}
				break;
				case 0x0d: // Return
				{
					if (pkey->caret.pos.i == Icon_Collective)
					{
						if (EditCDDesc_OnOk(This))
						{
							Window_Close(This->m_wnd);
						}
					}
					return EListenerAction_StopEvent;
				}
				break;
				case 0x181: // F1
				{
					App_StrongHelp("CDs_CDProperties");
					return EListenerAction_StopEvent;
				}
				break;
				default:
				{
					switch (pkey->caret.pos.i)
					{
						case Icon_Album:
						{
							EditCDDesc_CheckChanges(This, Prop_Album);
						}
						break;
						case Icon_Date:
						{
							EditCDDesc_CheckChanges(This, Prop_Date);
						}
						break;
						case Icon_Box:
						{
							EditCDDesc_CheckChanges(This, Prop_Box);
						}
						break;
						case Icon_Artist:
						{
							EditCDDesc_CheckChanges(This, Prop_Artist);
						}
						break;
						case Icon_Collective:
						{
							EditCDDesc_CheckChanges(This, Prop_Collective);
						}
						break;
					}
					break;
				}
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction EditCDDesc_OnDocEvent(void* pListener, const Event* pEvent)
{
	EditCDDesc* This = pListener;

	if ((This->m_wnd == HWind_None)
	||  (pEvent->pSender == This))
		return EListenerAction_ContinueEvent;

	if (pEvent->pSender == This->m_pPrefList)
	{
		This->m_ModifiedProps &= ~Prop_PrefList;
		if (PrefList_IsModified(This->m_pPrefList))
			This->m_ModifiedProps |= Prop_PrefList;

		EditCDDesc_RefreshButtons(This);
	}

	return EListenerAction_ContinueEvent;
}

void EditCDDesc_Edit(List* pList, const CDDesc* pRef)
{
	EditCDDesc* This = &TheEditCDDesc;

	if (Options()->Player.bParentalLock)
		return;

	if (This->m_wnd == HWind_None)
	{
		memset(This, 0, sizeof(*This));
		This->m_wnd = HWind_None;

		try
		{
			This->m_pDescs = New_List();

			throw_Task_AddListener(EEvent_OptionParentalLock, EditCDDesc_Listener, This, false);

			This->m_wnd = throw_Window_CreateFrom("PropCD", "CPropH");
			throw_Window_RegisterEventHandler(This->m_wnd, EditCDDesc_EventHandler, This, false);
			throw_DocEvents_AddListener(NULL, This, EditCDDesc_OnDocEvent);

			This->m_pWCollectiveList = throw_New_WStringList(DigitalCD.pCollectivesCol, This->m_wnd, Icon_Collective);
			This->m_pWArtistList = throw_New_WStringList(DigitalCD.pArtistsCol, This->m_wnd, Icon_Artist);
			This->m_pWBoxList = throw_New_WStringList(DigitalCD.pBoxesCol, This->m_wnd, Icon_Box);
			This->m_pWAlbumList = throw_New_WStringList(DigitalCD.pAlbumsCol, This->m_wnd, Icon_Album);

			This->m_pPrefList = throw_New_PrefList();
		}
		catch
		{
			EditCDDesc_NotEdit();
			App_ReportException();
			return;
		}
		catch_end
	}

	EditCDDesc_ClearDescs(This);

	try
	{
		if (pList && List_Count(pList))
		{
			ListNode* pNode = NULL;
			const CDDesc* pDesc;

			while ((pNode = List_GetSuccessor(pList, pNode)) != NULL)
			{
				pDesc = List_GetNodeData(pList, pNode);

				List_InsertBefore(This->m_pDescs, NULL, pDesc);
			}
		}
	}
	catch
	{
		App_ReportException();
	}
	catch_end

	EditCDDesc_SetRefDesc(This, pRef);

	if (!List_Count(This->m_pDescs))
	{
		Window_Close(This->m_wnd);
	}
	else
	{
		EditCDDesc_BuildProperties(This);
		EditCDDesc_BuildCommonStatus(This);

		Window_Open(This->m_wnd);
		Icon_SetFocus(This->m_wnd, Icon_Album, -1);
	}
}

void EditCDDesc_NotEdit(void)
{
	EditCDDesc* This = &TheEditCDDesc;

	if (This->m_pDescs)
	{
		EditCDDesc_ClearDescs(This);
		Delete_List(This->m_pDescs);
	}

	Delete_PrefList(This->m_pPrefList);
	Delete_WStringList(This->m_pWCollectiveList);
	Delete_WStringList(This->m_pWArtistList);
	Delete_WStringList(This->m_pWBoxList);
	Delete_WStringList(This->m_pWAlbumList);

	if (This->m_wnd != HWind_None)
	{
		DocEvents_RemoveListener(NULL, This, EditCDDesc_OnDocEvent);
		Task_RemoveListener(EEvent_OptionParentalLock, EditCDDesc_Listener, This);
		Window_DeRegisterEventHandler(This->m_wnd, EditCDDesc_EventHandler, This);
		Window_Delete(This->m_wnd);
	}

	mem_free(This->m_ref.album);
	mem_free(This->m_ref.date);
	mem_free(This->m_ref.box);
	mem_free(This->m_ref.artist);
	mem_free(This->m_ref.collective);

	memset(This, 0, sizeof(*This));
	This->m_wnd = HWind_None;
}
