#include "FLCmds.h"

#include <time.h>
#include "swis.h"

#include "WimpLib:Clipboard.h"
#include "WimpLib:DragDrop.h"
#include "WimpLib:DragSend.h"
#include "WimpLib:Exception.h"
#include "WimpLib:File.h"
#include "WimpLib:Hourglass.h"
#include "WimpLib:mem.h"
#include "WimpLib:Message.h"
#include "WimpLib:Task.h"

#include "Cmds.h"
#include "DigitalCD.h"
#include "EditDirList.h"
#include "EditFile.h"
#include "EditList.h"
#include "EditRadio.h"
#include "EditYPList.h"
#include "FileList.h"
#include "FileTypes.h"
#include "Setup.h"
#include "PListFiles.h"
#include "UserEvents.h"

int FileView_CheckCommand(const View* pView, int cmd, const List* pList)
{
	FileList* pDoc = (FileList*) View_GetDocument(pView);
	ListNode* pNode;
	const FLTrack* pTrack;
	int state = 0;

	// Disable any command if a background Job is active
	if (FileList_IsBusy(pDoc))
		return state;

	switch(cmd)
	{
		case Cmd_PList_Validate:
		case Cmd_PList_ClearPlayMark:
		{
			if (FileList_TrackCount(pDoc) != 0)
				state |= ECmdState_Allow;
		}
		break;
		case Cmd_PList_EditDelete:
		case Cmd_PList_EditCut:
		case Cmd_PList_EditCopy:
		{
			if (List_Count(pList) != 0)
				state |= ECmdState_Allow;
		}
		break;
		case Cmd_PList_File_Delete:
		{
			// Check that there is at least one selected item
			if (List_Count(pList) == 0)
				break;

			pNode = NULL;
			state |= ECmdState_Allow;

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

				// Check it's file
				if (FLTrack_GetObjectType(pTrack) != FLTrack_Type_File)
				{
					state = 0;
					break;
				}
			}
		}
		break;
		case Cmd_PList_ShowSaveAs:
		{
			switch (FileList_GetType(pDoc))
			{
				case FileList_TypeNormal:
				case FileList_TypeExtern:
					state |= ECmdState_Allow;
			}
		}
		break;
		case Cmd_PList_Save:
		{
			switch (FileList_GetType(pDoc))
			{
				case FileList_TypeNormal:
				case FileList_TypeMain:
				case FileList_TypeExtern:
					state |= ECmdState_Allow;
			}
		}
		break;
		case Cmd_PList_Play:
		case Cmd_PList_List_Play:
		case Cmd_PList_DirList_Play:
		case Cmd_PList_File_Play:
		case Cmd_PList_Radio_Play:
		{
			// Check if exactly 1 item is selected
			if (List_Count(pList) != 1)
				break;

			int index = FileList_FindTrack(pDoc, List_Get(pList, 0));

			if (FileList_GetType(pDoc) == FileList_TypeQueue)
			{
				if (index >= 0)
					state |= ECmdState_Allow;
			}
			else if (FileList_GetPlayTrack(pDoc, index) != NULL)
				state |= ECmdState_Allow;
		}
		break;
		case Cmd_PList_Properties:
		case Cmd_PList_File_Properties:
		case Cmd_PList_Radio_Properties:
		case Cmd_PList_List_Properties:
		case Cmd_PList_DirList_Properties:
		case Cmd_PList_YPList_Properties:
		{
			int type = -1;

			// Check that there is at least one selected item
			if (List_Count(pList))
			{
				// Check that all objects are of the same type
				pNode = List_GetSuccessor(pList, NULL);
				pTrack = List_GetNodeData(pList, pNode);
				type = FLTrack_GetObjectType(pTrack);

				state |= ECmdState_Allow;
				while ((pNode = List_GetSuccessor(pList, pNode)) != NULL)
				{
					pTrack = List_GetNodeData(pList, pNode);

					if (type != FLTrack_GetObjectType(pTrack))
					{
						state = 0;
						type = -1;
						break;
					}
				}
			}
		}
		break;
		case Cmd_PList_List_Load:
		case Cmd_PList_DirList_Load:
		case Cmd_PList_YPList_Load:
		{
			// Check that there is at least one selected item
			if (List_Count(pList) == 0)
				break;

			pNode = NULL;
			state |= ECmdState_Allow;

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

				// Check it's an unloaded list
				if (!FLTrack_IsContainer(pTrack)
				||  (FLTrack_GetSubList(pTrack) != NULL))
				{
					state = 0;
					break;
				}
			}
		}
		break;
		case Cmd_PList_List_Open:
		case Cmd_PList_DirList_Open:
		case Cmd_PList_YPList_Open:
		case Cmd_PList_List_Unload:
		case Cmd_PList_DirList_Unload:
		case Cmd_PList_YPList_Unload:
		{
			// Check that there is at least one selected item
			if (List_Count(pList) == 0)
				break;

			pNode = NULL;
			state |= ECmdState_Allow;

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

				// Check it's a loaded list
				if (!FLTrack_IsContainer(pTrack)
				||  (FLTrack_GetSubList(pTrack) == NULL))
				{
					state = 0;
					break;
				}
			}
		}
		break;
		case Cmd_PList_OpenOrProperties:
		{
			int type = -1;
			int list = 1;

			// Check that there is at least one selected item
			if (List_Count(pList) == 0)
				break;

			state |= ECmdState_Allow;

			pNode = List_GetSuccessor(pList, NULL);
			pTrack = List_GetNodeData(pList, pNode);
			type = FLTrack_GetObjectType(pTrack);
			// Check it's a loaded list
			if (!FLTrack_IsContainer(pTrack)
			||  (FLTrack_GetSubList(pTrack) == NULL))
				list = 0;

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

				// Check that all objects are of the same type
				if (type != FLTrack_GetObjectType(pTrack))
					type = -1;

				// Check if object is a loaded list
				if (!FLTrack_IsContainer(pTrack)
				||  (FLTrack_GetSubList(pTrack) == NULL))
					list = 0;

				if ((list == 0) && (type == -1))
				{
					state = 0;
					break;
				}
			}
		}
		break;
		case Cmd_PList_NewList:
		case Cmd_PList_NewDir:
		case Cmd_PList_NewYP:
		{
			if (FileList_GetType(pDoc) == FileList_TypeMain)
				state |= ECmdState_Allow;
		}
		break;
		case Cmd_PList_NewFile:
		case Cmd_PList_NewRadio:
		{
			state |= ECmdState_Allow;
		}
		break;
		case Cmd_PList_Complete:
		{
			if ((*FileList_GetBaseDir(pDoc)) != 0)
				state |= ECmdState_Allow;
		}
		break;
		default:
			return App_CheckCommand(NULL, cmd);
	}

	return state;
}

bool FileList_MayRemove(const List* pList)
{
	ListNode* pNode;
	const FLTrack* pTrack;
	const FileList* pSubList;
	bool bAsk = false;

	pNode = NULL;

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

		pSubList = FLTrack_GetSubList(pTrack);

		if (pSubList
		&&  FileList_IsModified(pSubList))
		{
			bAsk = true;
			break;
		}
	}

	// Ask confirmation of removal of unsaved data
	if (!bAsk
	||  Task_Query(false, Msg_Lookup("AskDel:")))
		return true;

	return false;
}

void FileList_Remove(FileList* pDoc, List* pList, bool bNonContainer)
{
	bool bMod = false;
	ListNode* pNode;
	FLTrack* pTrack;

	FileList_StartUpdate(pDoc);

	pNode = NULL;

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

		if (bNonContainer || !FLTrack_IsContainer(pTrack))
		{
			FileList_DelTrack(pDoc, pTrack);
			bMod = true;
		}
	}

	FileList_EndUpdate(pDoc, false, bMod);

	List_Clear(pList);
}

typedef struct
{
	View*       pView;
	List*       pList;
	ListNode*   pNode;
	bool        bMod;
} ValidateList;

static EJobAction Job_ValidateList(void* pOwner, EJobAction action, void* pParams)
{
	FileList* pDoc = pOwner;
	ValidateList* pVL = pParams;
	FLTrack* pTrack;
	const char* filename;
	int  flags;

	try
	{
		if (action == EJobAction_Continue)
		{
			clock_t timeout = clock() + (CLK_TCK / 50);

			// Scan a limited number of files/dirs at a time
			while(((pVL->pNode = List_GetSuccessor(pVL->pList, pVL->pNode)) != NULL) && (clock() < timeout))
			{
				pTrack = List_GetNodeData(pVL->pList, pVL->pNode);

				filename = FLTrack_GetFilename(pTrack);
				flags = 0;
				// Check if object exists and is supported
				if (FLTrack_GetFlags(pTrack) & (FLTrack_IsDir | FLTrack_IsUrl))
				{
					// No check?
				}
				else if (FLTrack_GetFlags(pTrack) & FLTrack_IsPlaylist)
				{
					if (!FileList_AcceptSourceFileType(File_GetFileType(filename)))
						flags = FLTrack_Invalidated;
				}
				else if (LoadTypes_FindNext(File_GetFileType(filename), NULL) == NULL)
					flags = FLTrack_Invalidated;

				if (FLTrack_SetFlags(pTrack, flags, FLTrack_Invalidated))
				{
					FLTrack_RefreshViews(pTrack, pVL->pView, false, false);
					pVL->bMod = true;
				}
			}

			if (pVL->pNode == NULL)
				action = EJobAction_Stop;
		}
	}
	catch
	{
		App_ReportException();
		action = EJobAction_Stop;
	}
	catch_end

	if (action == EJobAction_Stop)
	{
		FileList_EndUpdate(pDoc, false, pVL->bMod);

		mem_free(pVL);
	}

	return action;
}

static void FileView_Validate(FileList* pDoc, View* pView, List* pList)
{
	ValidateList* pVL = NULL;

	try
	{
		pVL = throw_mem_alloc(sizeof(*pVL));
		pVL->pView = pView;
		pVL->pList = pList;
		pVL->pNode = NULL;
		pVL->bMod = false;

		JobList_Add(Task_GetJobList(), true, pDoc, pVL, Job_ValidateList);
		FileList_StartUpdate(pDoc);
	}
	catch
	{
		mem_free(pVL);

		App_ReportException();
	}
	catch_end
}

bool FileView_ExecCommand(View* pView, int cmd, List* pList, const FLTrack* pRef)
{
	FileList* pDoc = (FileList*) View_GetDocument(pView);
	ListNode* pNode;
	FLTrack* pTrack;

	switch(cmd)
	{
		case Cmd_PList_EditCut:
		{
			if (FileList_MayRemove(pList))
			{
				Clipboard_Claim(&FileList_Clipboard, pList);
				FileList_Remove(pDoc, pList, true);
			}
		}
		break;
		case Cmd_PList_EditCopy:
		{
			Clipboard_Claim(&FileList_Clipboard, pList);
		}
		break;
		case Cmd_PList_Save:
		{
			View_OnSaveDocument(pView);
		}
		break;
		case Cmd_PList_ShowSaveAs:
		{
			if (FileList_GetType(pDoc) == FileList_TypeMain)
				View_OnSaveDocument(pView);
			else
				View_OnSaveDocumentAs(pView);
		}
		break;
		case Cmd_PList_EditDelete:
		{
			if (!FileList_MayRemove(pList))
				return true;

			FileList_Remove(pDoc, pList, true);
		}
		break;
		case Cmd_PList_File_Delete:
		{
			int i = 0;
			int max = List_Count(pList);

			if (!Task_Query(true, "DSDelFile:")) return true;

			bool off = !WLib_Hourglass_IsOn();
			if (off) WLib_Hourglass_On();
			FileList_StartUpdate(pDoc);

			try
			{
				pNode = NULL;

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

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

					PListFiles_RemoveTrack(PListFiles_Get(), pTrack);
					throw_FLTrack_DeleteFile(pTrack);
					FileList_DelTrack(pDoc, pTrack);
				}
			}
			catch
			{
				App_ReportException();
			}
			catch_end

			FileList_EndUpdate(pDoc, false, true);
			if (off) WLib_Hourglass_Off();
		}
		break;
		case Cmd_PList_ClearPlayMark:
		{
			bool bMod = false;

			FileList_StartUpdate(pDoc);

			pNode = NULL;

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

				if (FLTrack_SetFlags(pTrack, 0, FLTrack_Played | FLTrack_PlayedToday | FLTrack_FailedToPlay))
				{
					FLTrack_RefreshViews(pTrack, pView, false, false);
					bMod = true;
				}
			}

			FileList_EndUpdate(pDoc, bMod, false);
		}
		break;
		case Cmd_PList_Properties:
		case Cmd_PList_File_Properties:
		case Cmd_PList_Radio_Properties:
		case Cmd_PList_List_Properties:
		case Cmd_PList_DirList_Properties:
		case Cmd_PList_YPList_Properties:
		{
			pTrack = List_Get(pList, 0);

			switch(FLTrack_GetObjectType(pTrack))
			{
				case FLTrack_Type_File:
				{
					EditFile_Edit(pView, pList, pRef);
				}
				break;
				case FLTrack_Type_Url:
				{
					EditRadio_Edit(pView, pList, pRef);
				}
				break;
				case FLTrack_Type_FileList:
				{
					EditList_Edit(pView, pList, pRef);
				}
				break;
				case FLTrack_Type_Directory:
				{
					EditDirList_Edit(pView, pList, pRef);
				}
				break;
				case FLTrack_Type_YellowPage:
				{
					EditYPList_Edit(pView, pList, pRef);
				}
				break;
				default: ;
			}
		}
		break;
		case Cmd_PList_Play:
		case Cmd_PList_List_Play:
		case Cmd_PList_DirList_Play:
		case Cmd_PList_File_Play:
		case Cmd_PList_Radio_Play:
		{
			int index = FileList_FindTrack(pDoc, List_Get(pList, 0));

			FileList_Play(pDoc, index);
		}
		break;
		case Cmd_PList_Validate:
		{
			FileView_Validate(pDoc, pView, pList);
		}
		break;
		case Cmd_PList_List_Open:
		case Cmd_PList_DirList_Open:
		case Cmd_PList_YPList_Open:
		case Cmd_PList_OpenOrProperties:
		{
			int count = 0;

			pNode = NULL;

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

				switch(FLTrack_GetObjectType(pTrack))
				{
					case FLTrack_Type_FileList:
					case FLTrack_Type_Directory:
					case FLTrack_Type_YellowPage:
					{
						FileList* pFList = FLTrack_GetSubList(pTrack);

						if (pFList)
						{
							FileList_Show(pFList, -1);
							count++;
						}
					}
					break;
					default: ;
				}
			}

			if ((cmd == Cmd_PList_OpenOrProperties)
			&&  (count == 0))
				return FileView_ExecCommand(pView, Cmd_PList_Properties, pList, pRef);
		}
		break;
		case Cmd_PList_List_Load:
		case Cmd_PList_DirList_Load:
		case Cmd_PList_YPList_Load:
		{
			pNode = NULL;

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

				if ((FLTrack_IsContainer(pTrack))
				&&  !FLTrack_GetSubList(pTrack))
				{
					throw_New_FileList_FromTrack(pTrack);
				}
			}
		}
		break;
		case Cmd_PList_List_Unload:
		case Cmd_PList_DirList_Unload:
		case Cmd_PList_YPList_Unload:
		{
			if (!FileList_MayRemove(pList))
				return true;

			pNode = NULL;

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

				FileList* pSubList = FLTrack_GetSubList(pTrack);

				if (pSubList)
				{
					Delete_FileList(pSubList);
					FLTrack_RefreshViews(pTrack, pView, false, false);
				}
			}
		}
		break;
		case Cmd_PList_NewList:
		{
			EditList_EditNew(pView);
		}
		break;
		case Cmd_PList_NewDir:
		{
			EditDirList_EditNew(pView);
		}
		break;
		case Cmd_PList_NewYP:
		{
			EditYPList_EditNew(pView);
		}
		break;
		case Cmd_PList_NewFile:
		{
			EditFile_EditNew(pView);
		}
		break;
		case Cmd_PList_NewRadio:
		{
			EditRadio_EditNew(pView);
		}
		break;
		case Cmd_PList_Complete:
		{
			FileList_Rescan(pDoc);
		}
		break;
		default:
			return App_ExecCommand(NULL, cmd);
	}

	return true;
}
