//	Zinc Application Framework - I_FILDLG.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <stdlib.h>
#include <z_fildlg.hpp>
#include <z_string.hpp>
#include <z_border.hpp>
#include <z_title.hpp>
#include <z_prompt.hpp>
#include <z_hlist.hpp>
#include <z_combo.hpp>
#include <z_button.hpp>
#include <z_str1.hpp>
#include <z_scrll2.hpp>
#include <z_utils.hpp>
#include <z_mouse2.hpp>
#include <z_error.hpp>
#include <z_msgwin.hpp>

#define ZAF_FILE_DIALOG_INFO
#include "img_def.cpp"
#include "lang_def.cpp"

const ZafEventType FILE_DLG_SELECT_DIRECTORY	= 11001;
const ZafEventType FILE_DLG_SELECT_FILE			= 11002;
const ZafEventType FILE_DLG_SELECT_FILTER		= 11003;
const ZafEventType FILE_DLG_SELECT_VOLUME		= 11004;

static ZafBitmapData directoryBitmapData(_closedFile_ZafBitmap);
static ZafBitmapData hardDriveBitmapData(_hardDrive_ZafBitmap);

static ZafEventType FileItemUserFunction(ZafWindowObject *item, ZafEventStruct &, ZafEventType ccode)
{
	ZafEventStruct selectEvent;
	selectEvent.windowObject = item;
	bool isDirectory = item->userFlags;

	if (ccode == L_SELECT && !isDirectory)
	{
		selectEvent.type = FILE_DLG_SELECT_FILE;
		zafEventManager->Put(selectEvent);
	}
	else if (ccode == L_DOUBLE_CLICK)
	{
		if (!isDirectory)
			selectEvent.type = S_DLG_OK;
		else
			selectEvent.type = FILE_DLG_SELECT_DIRECTORY;
		zafEventManager->Put(selectEvent);
	}

	return (0);
}

static ZafEventType FilterComboUserFunction(ZafWindowObject *combo, ZafEventStruct &, ZafEventType ccode)
{
	if (ccode == L_SELECT)
	{
	 	ZafEventStruct selectEvent(FILE_DLG_SELECT_FILTER);
		selectEvent.windowObject = combo;
		zafEventManager->Put(selectEvent);
	}

	return (0);
}

static ZafEventType VolumeComboUserFunction(ZafWindowObject *combo, ZafEventStruct &, ZafEventType ccode)
{
	if (ccode == L_SELECT)
	{
	 	ZafEventStruct selectEvent(FILE_DLG_SELECT_VOLUME);
		selectEvent.windowObject = combo;
		zafEventManager->Put(selectEvent);
	}

	return (0);
}

static int FileListCompareFunction(ZafWindowObject *item1, ZafWindowObject *item2)
{
	bool item1IsDirectory = item1->userFlags;
	bool item2IsDirectory = item2->userFlags;

	if (item1IsDirectory && !item2IsDirectory)
		return (-1);
	else if (item2IsDirectory && !item1IsDirectory)
		return (1);
	else
		return stricmp(item1->Text(), item2->Text());
}

void ZafFileDialog::UpdateFiles(void)
{
	eventManager->SetDeviceState(E_MOUSE, DM_WAIT);

	ZafIChar fullPathFilter[ZAF_MAXPATHLEN];
	strcpy(fullPathFilter, directory);
	if (fullPathFilter[strlen(fullPathFilter) - 1] != ZafOSDirSepStr()[0])
		strcat(fullPathFilter, ZafOSDirSepStr());
	strcat(fullPathFilter, allFilesFilter);

	ZafIChar activeFilter[ZAF_MAXPATHLEN];
	strcpy(activeFilter, DynamicPtrCast(GetObject("FILTER_COMBO"), ZafComboBox)->Text());
#if defined(ZAF_MSDOS) || defined(ZAF_WIN16)
		strupr(activeFilter);
#elif defined(ZAF_VXWORKS)
		strlwr(activeFilter);
#endif


	ZafHzList *fileList = DynamicPtrCast(GetObject("FILE_LIST"), ZafHzList);

//	Adding items is MUCH slower while list is registered.
// 	fileList->SetAutomaticUpdate(false);
	Subtract(fileList);

	fileList->Destroy();

	ZafFileInfoStruct fileInfo;
	int error = fileSystem->FindFirst(fileInfo, fullPathFilter); 
	if (error)
	{
		ZafMessageWindow *errorWindow = new ZafMessageWindow(ZAF_ITEXT("Error!"),
			ZAF_EXCLAMATION_ICON, ZAF_DIALOG_CANCEL | ZAF_DIALOG_RETRY,
			ZAF_DIALOG_CANCEL, ZAF_ITEXT("Can't read directory %s"),
			fullPathFilter);

		bool finished = false;
		while (!finished)
		{
			if (errorWindow->Control() != S_DLG_RETRY)
				finished = true;
			else
			{
				fileSystem->FindClose(fileInfo);
				error = fileSystem->FindFirst(fileInfo, fullPathFilter);
				if (!error)
					finished = true;
			}
		}

		delete errorWindow;
	}

	while (!error)
	{
		if ((fileInfo.Directory() && strcmp(fileInfo.name, fileSystem->currentDirectoryName) != 0) ||
			(!fileInfo.Directory() && WildStrcmp(fileInfo.name, activeFilter) == 0))
		{
			ZafButton *item = new ZafButton(0, 0, 0, 0, fileInfo.name,
				fileInfo.Directory() ? &directoryBitmapData : ZAF_NULLP(ZafBitmapData),
				ZAF_FLAT_BUTTON);
			item->SetHzJustify(ZAF_HZ_LEFT);
			item->SetAllowToggling(true);
	 		item->userFlags = fileInfo.Directory();
			item->SetUserFunction(FileItemUserFunction);
			item->SetSelectOnDoubleClick(true);
  			fileList->Add(item);
		}

		error = fileSystem->FindNext(fileInfo, fullPathFilter);
	}
	fileSystem->FindClose(fileInfo);

	fileList->SetCurrent(fileList->First());
	fileList->Event(S_HSCROLL_CHECK);

//	Adding items is MUCH slower while list is registered.
//	fileList->SetAutomaticUpdate(true);
	Add(fileList);

	eventManager->SetDeviceState(E_MOUSE, DM_VIEW);
}

void ZafFileDialog::UpdateVolumes(void)
{
	ZafComboBox *volumeCombo = DynamicPtrCast(GetObject("VOLUME_COMBO"), ZafComboBox);
	volumeCombo->SetAutomaticUpdate(false);
	volumeCombo->Destroy();

	ZafIChar **volumeArray;
	((ZafDiskFileSystem *)fileSystem)->GetDriveNames(&volumeArray);
	int index = 0;
	while (volumeArray[index])
	{
		zafDisplay->SetFont(volumeCombo->Font());
		int height = ZafMax(zafDisplay->TextSize(ZAF_ITEXT("Iq")).Height(), hardDriveBitmapData.height) + zafDisplay->preSpace + zafDisplay->postSpace;
		ZafButton *item = new ZafButton(0, 0, 0, height, volumeArray[index],
			&hardDriveBitmapData, ZAF_FLAT_BUTTON);
		item->SetAutoSize(false);
		item->SetCoordinateType(ZAF_PIXEL);
		item->SetHzJustify(ZAF_HZ_LEFT);
		item->SetAllowToggling(true);
  		volumeCombo->Add(item);
		if (strnicmp(volumeArray[index], directory, strlen(volumeArray[index])) == 0)
			item->SetSelected(true);
		index++;
	}
	((ZafDiskFileSystem *)fileSystem)->DeleteDriveNames(volumeArray);

	volumeCombo->SetAutomaticUpdate(true);
}

void ZafFileDialog::UpdateTitle()
{
	const ZafIChar *titleText = Text();
	const ZafIChar *separator = strchr(titleText, '-');

	int saveTitleTextLen = (separator ? int(separator - titleText) - 1 : strlen(titleText));

	ZafIChar *newTitleText = new ZafIChar[saveTitleTextLen + strlen(directory) + 5];
	strncpy(newTitleText, titleText, saveTitleTextLen);
	newTitleText[saveTitleTextLen] = 0;
	strcat(newTitleText, ZAF_ITEXT(" - "));
	strcat(newTitleText, directory);

	SetText(newTitleText);
}

// ----- ZafFileDialog ----------------------------------------------------

ZafFileDialog::ZafFileDialog(int left, int top, int, int) :
	ZafDialogWindow(left, top, 500, 100)
{
	SetCoordinateType(ZAF_MINICELL);
	fileSystem = new ZafDiskFileSystem();

	directoryBitmapData.SetDestroyable(false);
	hardDriveBitmapData.SetDestroyable(false);

	// Get the error stings.
	messages = ZafLanguageManager::Allocate(className, defaultMessages);

	directory[0] = file[0] = filter[0] = 0;
	SetModal(true);
	SetSizeable(true);

	// Add the decorations.
	Add(new ZafBorder);
	Add(new ZafTitle(ZAF_ITEXT("")));

	// Add the volume prompt.
	ZafPrompt *prompt = new ZafPrompt(10, 2, 110, ZAF_ITEXT("Look in:"));
	prompt->zafRegion.bottom = prompt->zafRegion.top + 8;
	prompt->SetCoordinateType(ZAF_MINICELL);
	prompt->SetHotKey('i', 5);
	Add(prompt);

	// Add the volume combo.
	ZafComboBox *comboBox = new ZafComboBox(120, 2, 200, 40);
	comboBox->zafRegion.bottom = comboBox->zafRegion.top + 8;
	comboBox->SetCoordinateType(ZAF_MINICELL);
	comboBox->SetStringID(ZAF_ITEXT("VOLUME_COMBO"));
	comboBox->SetViewOnly(true);
	comboBox->SetOSDraw(false);
	comboBox->Add(new ZafScrollBar(ZAF_VERTICAL_SCROLL));
	comboBox->SetUserFunction((ZafUserFunction)VolumeComboUserFunction);
	comboBox->SetAutoSortData(true);
	Add(comboBox);

	// Add the file list.
	ZafHzList *hzList = new ZafHzList(10, 13, 480, 59, 0, 0);
	hzList->SetCoordinateType(ZAF_MINICELL);
	hzList->ConvertCoordinates(ZAF_PIXEL);
	hzList->SetCoordinateType(ZAF_PIXEL);
	display->SetFont(Font());
	hzList->SetCellWidth(15 * display->cellWidth);
	hzList->SetCellHeight(ZafMax(display->TextSize(ZAF_ITEXT("Iq")).Height(), directoryBitmapData.height) + display->preSpace + display->postSpace);
	hzList->SetStringID(ZAF_ITEXT("FILE_LIST"));
	hzList->Add(new ZafScrollBar(ZAF_HORIZONTAL_SCROLL));
	hzList->SetCompareFunction((ZafCompareFunction)FileListCompareFunction);
	Add(hzList);

	// Add the file name prompt.
	prompt = new ZafPrompt(10, 74, 110, ZAF_ITEXT("File name:"));
	prompt->zafRegion.bottom = prompt->zafRegion.top + 8;
	prompt->SetCoordinateType(ZAF_MINICELL);
	prompt->SetHotKey('n', 5);
	Add(prompt);

	// Add the file name string.
	ZafString *string = new ZafString(120, 74, 200, ZAF_NULLP(ZafStringData));
	string->SetStringID(ZAF_ITEXT("FILE_NAME_FIELD"));
	string->zafRegion.bottom = string->zafRegion.top + 8;
	string->SetCoordinateType(ZAF_MINICELL);
	Add(string);

	// Add the filter prompt.
	prompt = new ZafPrompt(10, 85, 110, ZAF_ITEXT("Files of type:"));
	prompt->zafRegion.bottom = prompt->zafRegion.top + 8;
	prompt->SetCoordinateType(ZAF_MINICELL);
	prompt->SetHotKey('t', 9);
	Add(prompt);

	// Add the filter combo.
	comboBox = new ZafComboBox(120, 85, 200, 30);
	comboBox->SetStringID(ZAF_ITEXT("FILTER_COMBO"));
	comboBox->zafRegion.bottom = comboBox->zafRegion.top + 8;
	comboBox->SetCoordinateType(ZAF_MINICELL);
	comboBox->Add(new ZafScrollBar(ZAF_VERTICAL_SCROLL));
	comboBox->SetUserFunction((ZafUserFunction)FilterComboUserFunction);
	Add(comboBox);

	// Add the OK button.
	ZafButton *button = new ZafButton(370, 74, 110, 10, ZAF_ITEXT("OK"));
	button->SetCoordinateType(ZAF_MINICELL);
	button->SetAutoSize(false);
	button->SetHotKey('O', 0);
	button->SetAllowDefault(true);
	button->SetSendMessageWhenSelected(true);
	button->SetValue(S_DLG_OK);
	Add(button);
	SetDefaultButton(button);

	// Add the cancel button.
	button = new ZafButton(370, 86, 110, 10, ZAF_ITEXT("Cancel"));
	button->SetCoordinateType(ZAF_MINICELL);
	button->SetAutoSize(false);
	button->SetHotKey('C', 0);
	button->SetSendMessageWhenSelected(true);
	button->SetValue(S_DLG_CANCEL);
	Add(button);
}

ZafEventType ZafFileDialog::Event(const ZafEventStruct &event)
{
	ZafEventType ccode = LogicalEvent(event);

	switch (ccode)
	{
	case FILE_DLG_SELECT_FILE:
		GetObject(ZAF_ITEXT("FILE_NAME_FIELD"))->SetText(event.windowObject->Text());
		break;

	case FILE_DLG_SELECT_DIRECTORY:
		{
		const ZafIChar *newDirectory = event.windowObject->Text();
		if (strcmp(newDirectory, fileSystem->parentDirectoryName) == 0)
			*strrchr(directory, ZafOSDirSepStr()[0]) = 0;
		else
		{
			if (directory[strlen(directory) - 1] != ZafOSDirSepStr()[0])
				strcat(directory, ZafOSDirSepStr());
			strcat(directory, newDirectory);
		}

		UpdateTitle();
		UpdateFiles();
		}
		break;

	case FILE_DLG_SELECT_FILTER:
		UpdateFiles();
		break;

	case FILE_DLG_SELECT_VOLUME:
		{
		strcpy(directory, event.windowObject->Text());
		strcat(directory, ZafOSDirSepStr());
		UpdateTitle();
		UpdateFiles();
		}
		break;

	case S_DLG_OK:
		{
		file[0] = 0;

		ZafIChar *fileFieldText = strdup(GetObject(ZAF_ITEXT("FILE_NAME_FIELD"))->Text());
		ZafIChar *fileFieldTextPtr = fileFieldText;

#if defined(ZAF_MSDOS) || defined(ZAF_WIN16)
		strupr(fileFieldText);
#elif defined(ZAF_VXWORKS)
		strlwr(fileFieldText);
#endif

		// Test for a volume/drive specification.
		ZafIChar *volumeSeparator = ZAF_NULLP(ZafIChar);
		ZafIChar **volumeArray;
		((ZafDiskFileSystem *)fileSystem)->GetDriveNames(&volumeArray);
		int index = 0;
		ZafIChar *newVolume = ZAF_NULLP(ZafIChar);
		ZafIChar *oldVolume = ZAF_NULLP(ZafIChar);
		while (volumeArray[index])
		{
			if (strnicmp(fileFieldText, volumeArray[index], strlen(volumeArray[index])) == 0)
				newVolume = volumeArray[index];
			if (strnicmp(directory, volumeArray[index], strlen(volumeArray[index])) == 0)
			{
				oldVolume = volumeArray[index];
				volumeSeparator = &directory[strlen(volumeArray[index]) - 1];
			}

			index++;
		}

		if (newVolume)
		{
			strcpy(directory, newVolume);
			fileFieldTextPtr += strlen(newVolume);
			volumeSeparator = &directory[strlen(newVolume) - 1];
		}
		else if (*fileFieldTextPtr == ZafOSDirSepStr()[0])
		{
			strcpy(directory, oldVolume);
			fileFieldTextPtr++;
		}

		((ZafDiskFileSystem *)fileSystem)->DeleteDriveNames(volumeArray);

		// Parse directories.
		ZafIChar *directoryEntry = strtok(fileFieldTextPtr, ZafOSDirSepStr());
		while (directoryEntry)
		{
			if (strcmp(directoryEntry, fileSystem->parentDirectoryName) == 0)
			{
				ZafIChar *dirSep = strrchr(directory, ZafOSDirSepStr()[0]);
				if (dirSep && dirSep > volumeSeparator)
					*dirSep = 0;
			}
			else if (strcmp(directoryEntry, fileSystem->currentDirectoryName) != 0)
			{
				ZafIChar *oldTerminator = &directory[strlen(directory)];
				strcat(directory, ZafOSDirSepStr());
				strcat(directory, directoryEntry);

				ZafFileInfoStruct fileInfo;
				if (fileSystem->FindFirst(fileInfo, directory) != 0 ||
					!fileInfo.Directory())
				{
					*oldTerminator = 0;
					if (!strtok(0, ZafOSDirSepStr()))
						strcpy(file, directoryEntry);
					fileSystem->FindClose(fileInfo);
					break;
				}
				fileSystem->FindClose(fileInfo);
			}

			directoryEntry = strtok(0, ZafOSDirSepStr());
		}

		if (!file[0])
		{
			UpdateVolumes();
			UpdateFiles();
			UpdateTitle();
			ccode = S_NO_OBJECT;
		}

		delete []fileFieldText;
		}
		break;

	default:
		ccode = ZafDialogWindow::Event(event);
		break;
	}

	return (ccode);
}

void ZafFileDialog::FullPath(ZafIChar *pathBuffer, int bufferSize)
{
	// Copy directory string.
	strncpy(pathBuffer, directory, bufferSize);
	int bufferIndex = strlen(directory);

	// Append a directory separator character if neccessary.
	if (pathBuffer[bufferIndex - 1] != ZafOSDirSepStr()[0])
	{
		strncpy(&pathBuffer[bufferIndex], ZafOSDirSepStr(), bufferSize - bufferIndex);
		bufferIndex++;
	}

	// Append the file name.
	strncpy(&pathBuffer[bufferIndex], file, bufferSize - bufferIndex);
}

ZafDialogEvent ZafFileDialog::GetFile(ZafFileDialogRequest request)
{
	if (!*directory)
		fileSystem->GetCWD(directory, ZAF_MAXPATHLEN);

	switch (request)
	{
	case ZAF_FILE_DIALOG_NEW:
		SetText(messages->GetMessage(ZAF_MSG_NEW));
		break;

	case ZAF_FILE_DIALOG_OPEN:
		SetText(messages->GetMessage(ZAF_MSG_OPEN));
		break;

	case ZAF_FILE_DIALOG_SAVEAS:
		SetText(messages->GetMessage(ZAF_MSG_SAVEAS));
		break;
	}

	UpdateTitle();

	ZafComboBox *filterCombo = DynamicPtrCast(GetObject("FILTER_COMBO"), ZafComboBox);
	filterCombo->Destroy();
	ZafIChar *filterString = strdup(Filter());
	ZafIChar *singleFilter = strtok(filterString, ZAF_ITEXT(";"));
	while (singleFilter)
	{
		filterCombo->Add(new ZafString(0, 0, 0, singleFilter, -1));
		singleFilter = strtok(0, ZAF_ITEXT(";"));
	}
	delete []filterString;
	filterCombo->Add(new ZafString(0, 0, 0, allFilesFilter, -1));

	UpdateVolumes();
	UpdateFiles();

	// Get the file and directory information.
	ZafEventType result = Control();
	if (result != S_DLG_OK)
		file[0] = 0;

	return (result);
}

OSWindowID ZafFileDialog::OSScreenID(ZafScreenIDType) const
{
	return (screenID);
}

void ZafFileDialog::OSRegisterObject(void)
{
	ZafDialogWindow::OSRegisterObject();
}

void ZafFileDialog::OSSize(void)
{
	ZafDialogWindow::OSSize();
}
