/*
 *	ActionBack.c - Copyright © 1991 by S.R. & P.C.
 *
 *	Created:	21 Apr 1991  16:09:52
 *	Modified:	12 Jul 1991  20:43:07
 *
 *	Make>> make
 */


#include "Global.h"
#include "FileList.h"
#include "ActionBack.h"
#include "Process.h"
#include "proto/ActionBack.h"
#include "proto/Process.h"
#include "proto/Windows.h"
#include "proto/Mouse.h"
#include "proto/Scan.h"
#include "proto/Sort.h"
#include "proto/Draw.h"
#include "proto/String.h"
#include "proto/File.h"
#include "proto/FileList.h"

extern struct ExecBase *SysBase;
extern struct MinList WindowList;
extern struct Config Config;
extern struct MsgPort *ProcessPort;


#define AB_DELETE		(AB_DELETE_DEST|AB_DELETE_SOURCE)


/* Search by name a ScrollEntry in a window */

static struct ScrollEntry *FindEntry(struct BrowserWindow *Win, char *Name)
{
	struct ScrollEntry *S;
	short i;

	/* No list for main window, only an array */
	if (Win->bw_Type == BW_MAIN) {
		for( i=0 ; i < Win->bw_ShownEntries ; i++ ) {
			S = Win->bw_EntryArray[i];
			if (!Strcmp(S->se_FileInfo.fi_Name, Name))
				return (S->se_State & STATE_DELETED) ? NULL : S;
		}			
	}
	else {
		S = (struct ScrollEntry *)Win->bw_EntryList.mlh_Head;
		while(S->se_Node.mln_Succ) {
			if (!Strcmp(S->se_FileInfo.fi_Name, Name))
				return (S->se_State & STATE_DELETED) ? NULL : S;
			S = (struct ScrollEntry *)S->se_Node.mln_Succ;
		}
	}
	return NULL;
}


/* Search by name a ScrollEntry in NewEntryList of a window */

static struct ScrollEntry *FindNewEntry(struct BrowserWindow *Win, char *Name)
{
	struct ScrollEntry *S;

	S = (struct ScrollEntry *)Win->bw_NewEntryList.mlh_Head;
	while(S->se_Node.mln_Succ) {
		if (!Strcmp(S->se_FileInfo.fi_Name, Name))
			return S;
		S = (struct ScrollEntry *)S->se_Node.mln_Succ;
	}
	return NULL;
}


static void ActionBack(struct SuperFileInfo *sfi, BPTR DirLock, short Action)
{
	struct BrowserWindow *Win;
	struct ScrollEntry *S = NULL;
	struct Point Position;
	BOOL PrintString = FALSE;
	short len;

	if (!Action)
		return;
	if (Action & AB_SCANDEVS) {
		ScanDevs();
		return;
	}
	switch(sfi->FileInfo.fi_Type) {
	case DLX_FILE:
	case DLX_DIR:
		Win = FindDir(DirLock);
		break;
	default:	/* device, volume, assign */
		Win = (struct BrowserWindow *)WindowList.mlh_Head;
	}
	if (Win) {
		if (Action & AB_NEW_ENTRY) {
			if (S = FindEntry(Win, sfi->FileInfo.fi_Name)) {
				/* size of new entry may be different, so... */
				Win->bw_NumBytes -= S->se_FileInfo.fi_Size;
				Win->bw_NumBlocks -= S->se_FileInfo.fi_NumBlocks;
				Win->bw_NumBytes += sfi->FileInfo.fi_Size;
				Win->bw_NumBlocks += sfi->FileInfo.fi_NumBlocks;
				if (S->se_Print) {
					Win->bw_ShownBytes -= S->se_FileInfo.fi_Size;
					Win->bw_ShownBlocks -= S->se_FileInfo.fi_NumBlocks;
					Win->bw_ShownBytes += sfi->FileInfo.fi_Size;
					Win->bw_ShownBlocks += sfi->FileInfo.fi_NumBlocks;
					CopyFileInfo(&S->se_FileInfo, &sfi->FileInfo);
					if ((len = strlen(S->se_FileInfo.fi_Name)) > Win->bw_MaxFilenameLen) {
						Win->bw_Flags |= BWF_REBUILD_STRINGS;
						Win->bw_NewMaxFilenameLen = MAX(len, Win->bw_NewMaxFilenameLen);
					}
					else {
						BuildPrintString(Win, S);
						PrintString = TRUE;
					}
				}
				else
					CopyFileInfo(&S->se_FileInfo, &sfi->FileInfo);
				MakeBottomInfoString(Win);
				RefreshBottomInfo(Win);
			}
			else if (S = FindNewEntry(Win, sfi->FileInfo.fi_Name)) {
				CopyFileInfo(&S->se_FileInfo, &sfi->FileInfo);
			}
			else if (S = AllocMem(sizeof(struct ScrollEntry), MEMF_PUBLIC|MEMF_CLEAR)) {
				CopyFileInfo(&S->se_FileInfo, &sfi->FileInfo);
				/* entry can only be a file or dir here, so set pen to 1 or 3 */
				S->se_Pen = (S->se_FileInfo.fi_Type == DLX_FILE) ? 1 : 3;
				AddTail((struct List *)&Win->bw_NewEntryList, (struct Node *)S);
				Win->bw_NumNewEntries++;
				Win->bw_Flags |= BWF_REBUILD_ARRAY;
				if ((len = strlen(S->se_FileInfo.fi_Name)) > Win->bw_MaxFilenameLen) {
					Win->bw_Flags |= BWF_REBUILD_STRINGS;
					Win->bw_NewMaxFilenameLen = MAX(len, Win->bw_NewMaxFilenameLen);
				}
			}
		}
		else if (S = FindEntry(Win, sfi->OldName)) {
			if (Action & AB_DELETE) {
				if (S->se_State & STATE_SELECTED)
					DoSelect(Win, S, OPT_TOGGLESELECT);	/* deselect entry */
				S->se_State = STATE_DELETED;
				Win->bw_NumDeleted++;
				Win->bw_Flags |= BWF_REBUILD_ARRAY;
				/* if this file had the longest filename, tell DoUpdateDir() to check if
				 * strings could be made smaller */
				if (strlen(sfi->OldName) == Win->bw_MaxFilenameLen)
					Win->bw_Flags |= BWF_CHECK_MAXLEN;
				PrintString = TRUE;
			}
			if (S->se_State & STATE_GHOSTED) {	/* change state only if ghosted */
				S->se_State &= ~STATE_GHOSTED;
				if (S->se_Print) {
					if (Action & AB_SELECT)
						DoSelect(Win, S, 0);
					PrintString = TRUE;
				}
			}
			if (S->se_Print) {
				if (Action & AB_UPDATE_STRING) {
					CopyFileInfo(&S->se_FileInfo, &sfi->FileInfo);
					BuildPrintString(Win, S);
					PrintString = TRUE;
				}
				if (Action & AB_RENAME_ENTRY) {
					CopyFileInfo(&S->se_FileInfo, &sfi->FileInfo);
					Win->bw_Flags |= BWF_SORT;
					if ((len = strlen(S->se_FileInfo.fi_Name)) > Win->bw_MaxFilenameLen) {
						Win->bw_Flags |= BWF_REBUILD_STRINGS;
						Win->bw_NewMaxFilenameLen = MAX(len, Win->bw_NewMaxFilenameLen);
					}
					else {
						/* if this file had the longest filename, tell DoUpdateDir() to check if
						 * strings could be made smaller */
						if (strlen(sfi->OldName) == Win->bw_MaxFilenameLen && len <Win->bw_MaxFilenameLen)
							Win->bw_Flags |= BWF_CHECK_MAXLEN;
						BuildPrintString(Win, S);
						PrintString = TRUE;
					}
				}
				/* Modified entry may no more pass through filters so check it */
				if (!MatchFilters(&S->se_FileInfo, &Win->bw_FiltersInfo))
					Win->bw_Flags |= BWF_REBUILD_ARRAY|BWF_REBUILD_STRINGS;
			}
		}
		else if (S = FindNewEntry(Win, sfi->OldName)) {
			if (Action & AB_DELETE) {
				Remove((struct Node *)S);
				Win->bw_NumNewEntries--;
			}
			else
				CopyFileInfo(&S->se_FileInfo, &sfi->FileInfo);
		}
		if (PrintString && GetSePosition(Win, S, &Position))
			Print(Win, S, Position.X, Position.Y);
	}
}


void DoActionBack(struct SuperFileInfo *sfi, BPTR SrcDir, BPTR DestDir)
{
	if (sfi->ActionBack == AB_NONE)
		return;
	if (DestDir)
		ActionBack(sfi, DestDir, sfi->ActionBack & AB_DEST_FLAGS);
	/* Don't check SrcDir, it may be the main window */
	ActionBack(sfi, SrcDir, sfi->ActionBack & AB_SOURCE_FLAGS);
}


void SendActionBack(struct SuperFileInfo *sfi, BPTR SrcDir, BPTR DestDir)
{
	struct TaskData *TaskData;
	struct BrowserMsg *Msg;

	if (sfi->ActionBack == AB_NONE)
		return;
	if (Config.Options & OPT_ASYNCHRONOUS) {
		TaskData = (struct TaskData *)SysBase->ThisTask->tc_UserData;
		FreeProcessRepliedMsg(TaskData);
		if (!(Msg = AllocBrowserMsg()))
			return;
		Msg->bm_ExecMessage.mn_ReplyPort = TaskData->td_ReplyPort;
		Msg->bm_Type = BM_ACTIONBACK;
		Msg->bm_Info.ActionBack.SrcDir = SrcDir;
		Msg->bm_Info.ActionBack.DestDir = DestDir;
		/* Copy SuperFileInfo */
		Msg->bm_Info.ActionBack.sfi = *sfi;
		if (sfi->FileInfo.fi_Comment)
			Msg->bm_Info.ActionBack.sfi.FileInfo.fi_Comment = CopyStr(sfi->FileInfo.fi_Comment);
		PutMsg(ProcessPort, (struct Message *)Msg);
		TaskData->td_MsgCount++;
	}
	else
		DoActionBack(sfi, SrcDir, DestDir);
	sfi->ActionBack = AB_NONE;
}


void DoUpdateDir(BPTR Dir)
{
	struct BrowserWindow *Win;
	struct ScrollEntry *S, *Next;
	short len=0, i;
	BOOL Ok = TRUE;
	UBYTE Flags;

	if (Win = FindDir(Dir)) {
		if (Flags = Win->bw_Flags) {
			/* if strings have to be rebuilt, they must be longer, so don't check if
			 * they can be made smaller !! */
			if ((Flags & BWF_CHECK_MAXLEN) && !(Flags & BWF_REBUILD_STRINGS)) {
				for( i=0 ; i<Win->bw_ShownEntries ; i++ ) {
					S = Win->bw_EntryArray[i];
					if (!(S->se_State & STATE_DELETED))
						len = MAX(len, strlen(S->se_FileInfo.fi_Name));
				}
				if (len < Win->bw_MaxFilenameLen) {
					Win->bw_MaxFilenameLen = len;
					Flags |= BWF_REBUILD_STRINGS;
				}
			}
			if ((Flags & BWF_SORT) && !(Flags & BWF_REBUILD_ARRAY) && (Win->bw_Sort & 0x0F) == NAME_SORT)
				Sort(Win);
			if ((Flags & BWF_REBUILD_STRINGS) && !(Flags & BWF_REBUILD_ARRAY)) {
				Ok = BuildPrintStrings(Win);
			}
			else if ((Flags & BWF_REBUILD_ARRAY) && !(Flags & BWF_REBUILD_STRINGS)) {
				if (Ok = UpdateEntryList(Win, TRUE)) {
					Win->bw_ShownFiles = Win->bw_ShownDirs = Win->bw_ShownEntries = 0;
					Win->bw_ShownBytes = Win->bw_ShownBlocks = 0;
					S = (struct ScrollEntry *)Win->bw_EntryList.mlh_Head;
					i = 0;
					while(Next = (struct ScrollEntry *)S->se_Node.mln_Succ) {
						if (S->se_Print) {
							Win->bw_EntryArray[i++] = S;
							if (S->se_FileInfo.fi_Type == DLX_FILE)
								Win->bw_ShownFiles++;
							else
								Win->bw_ShownDirs++;
							Win->bw_ShownBytes += S->se_FileInfo.fi_Size;
							Win->bw_ShownBlocks += S->se_FileInfo.fi_NumBlocks;
						}
						S = Next;
					}
					Win->bw_ShownEntries = i;
					Sort(Win);
					MakeBottomInfoString(Win);
				}
			}
			else {	/* both flags BWF_REBUILD_ARRAY and BWF_REBUILD_STRINGS are set */
				if (Ok = List2Array(Win))
					MakeBottomInfoString(Win);
			}
			Win->bw_Flags = 0;
			if (Ok)
				RefreshWindow(Win);
			else
				CloseBrowserWindow(Win);
		}
		else {	/* some entries may have been reselected, so update bottom info */
			MakeBottomInfoString(Win);
			RefreshBottomInfo(Win);
		}
	}
}


void SendUpdateDir(BPTR Dir)
{
	struct TaskData *TaskData;
	struct BrowserMsg *Msg;

	if (!Dir)
		return;
	if (Config.Options & OPT_ASYNCHRONOUS) {
		TaskData = (struct TaskData *)SysBase->ThisTask->tc_UserData;
		if (!(Msg = AllocBrowserMsg()))
			return;
		Msg->bm_ExecMessage.mn_ReplyPort = TaskData->td_ReplyPort;
		Msg->bm_Type = BM_UPDATEDIR;
		Msg->bm_Info.Dir = Dir;
		PutMsg(ProcessPort, (struct Message *)Msg);
		TaskData->td_MsgCount++;
		/* Wait for all pending messages before unlocking Dir, or locks given to
		 * actionback messages won't be valid anymore */
		WaitAllMsg(TaskData);
	}
	else
		DoUpdateDir(Dir);
}


