// http://akelpad.sourceforge.net/forum/viewtopic.php?p=1582#1582
// Description(1033): Works with for WinFS. Script implemented as the library for using in other scripts.
// Description(1049): Библиотека функций для работы с диалогами, файловой системой и т.п.
// Version: 2.19 (2015.10.08)
// Author: VladSh

//Default variables
var pSlash = "\\";
var pExtTXT = "txt";
var pDefaultExt;			//можно задать извне, это будет приоритетнее, т.е. не заменится при вызове функции

//FUNCTIONS

//определение расширения по умолчанию
function getDefaultExt() {
	if (!pDefaultExt) {
		var lParam;
		if (lParam = AkelPad.MemAlloc(256)) {
			AkelPad.SendMessage(AkelPad.GetMainWnd(), 1222 /*AKD_GETMAININFO*/, 224 /*MI_DEFAULTSAVEEXT*/, lParam);
			pDefaultExt = AkelPad.MemRead(lParam, 1 /*DT_UNICODE*/);
			AkelPad.MemFree(lParam);
		}
	}
	return pDefaultExt || pExtTXT;
}

//Вызов диалога Открытия/Сохранения файла (WinAPI) с предварительной обработкой и инициализацией всех необходимых параметров
function fileDialogDefault(bOpenTrueSaveFalse, pInitialDir, pInitialFile, pInitialExt) {
	if (pInitialDir == "\\") pInitialDir = "";
	pInitialFile = getFileName(pInitialFile);
	
	//<определение фильтров>
	var nFilterIndex = 0;		//номер по порядку (не индекс!) для arrFilterLines; по умолчанию считаем, что расширение пока не найдено..
	var arrFilters;
	var arrFilterLines = [];
	var arrFTypeInfo;
	var arrExts = [];
	arrFilters = getFileContent(AkelPad.GetAkelDir(5) + "\\Params\\" + "FileDialogExtentions.ini", false, -1, /\r\n|\n|\r/);
	if (arrFilters) {
		for (var i = 0, l = arrFilters.length; i < l; i++) {
			arrFTypeInfo = arrFilters[i].split("=");
			arrExts[i] = arrFTypeInfo[1];
			arrFilters[i] = getFilter(arrFTypeInfo[0], arrFTypeInfo[1]);
		   if ((";" + arrFTypeInfo[1] + ";").split(";" + pInitialExt + ";").length == 2) {
		   	//нашли точное совпадение расширения
		   	nFilterIndex = i + 1;
		   }
		}
		//полный список
		arrFilterLines = arrFilters;
	}
	else {
		//если файл со списком типов не существует или пуст
		arrExts[0] = "*";
		arrExts[1] = pExtTXT;
		arrFilterLines[0] = getFilter("All Files",        arrExts[0]);
		arrFilterLines[1] = getFilter("Plain Text files", arrExts[1]);
		if (pInitialExt == pExtTXT) nFilterIndex = 2;
	}
	if (nFilterIndex == 0) {
		//если это расширение отсутствует в списке, значит вручную добавляем его как неустановленное..
		if (pInitialExt) {
			arrExts[arrExts.length] = pInitialExt;
			arrFilterLines[arrFilterLines.length] = getFilter("Unspecified file type", pInitialExt);
			nFilterIndex = arrFilterLines.length + 1;
		}
		else
			pInitialExt = getDefaultExt();	//для того, чтобы при неопределении расширения файла он не сохранился без расширения
	}
	var pFilter = arrFilterLines.join("");
	//</определение фильтров>
	
//	if (!bOpenTrueSaveFalse)
//		pInitialFile = pInitialFile || "filename";
	
	var result = fileDialog(bOpenTrueSaveFalse, AkelPad.GetMainWnd(), pInitialDir, pInitialFile, pInitialExt, pFilter, nFilterIndex);
	if (!bOpenTrueSaveFalse) {
		if (result.file && result.filterIndex != null) {
			arrFTypeInfo = arrExts[result.filterIndex].split(";");
			sExtTmp = arrFTypeInfo[0];
			if (sExtTmp != "*") {
				result.file = separateFile(result.file);
				//если расширение в ComboBox'е с расширениями не было изменено 
				if (sExtTmp == pInitialExt) {
					//пробуем взять его из имени файла (возможно вписали туда вручную)
					sExtTmp = result.file.ext;
				}
				return result.file.path + result.file.name + "." + sExtTmp;
			}
		}
	}
	return result.file;
}

//Сборка фильтра
function getFilter(sFTypeInfo, sExts /* расширения ч/з ";" */) {
	var sMask = "*." + (sExts).replace(/;/g, ";*.");		//маска для всех расширений этого типа, без пробелов
	return sFTypeInfo + " (" + sMask.replace(/;/g, "; ") + ")\0" + sMask + "\0";		//фильтр в полном виде
}

//Стандартный диалог Открытия/Сохранения файла (WinAPI)
function fileDialog(bOpenTrueSaveFalse, hWnd, pInitialDir, pInitialFile, pInitialExt, pFilter, nFilterIndex) {
	var nFlags = 0x880804; //OFN_HIDEREADONLY|OFN_PATHMUSTEXIST|OFN_EXPLORER|OFN_ENABLESIZING
	var lpStructure;
	var lpFilterBuffer;
	var lpFileBuffer;
	var lpExtBuffer;
	var lpDirBuffer;
	var oSys;
	var pResultFile = "";
	var nCallResult;
	
	var lFilterBuffer = pFilter.length;		//передаём не 256, а реальный размер pFilter
	if (lpFilterBuffer = AkelPad.MemAlloc((lFilterBuffer+1) * _TSIZE)) {
		AkelPad.MemCopy(lpFilterBuffer, pFilter.substr(0, lFilterBuffer), _TSTR);

		if (lpFileBuffer = AkelPad.MemAlloc(256 * _TSIZE)) {
			AkelPad.MemCopy(lpFileBuffer, pInitialFile.substr(0, 255), _TSTR);

			if (lpExtBuffer = AkelPad.MemAlloc(256 * _TSIZE)) {
				AkelPad.MemCopy(lpExtBuffer, pInitialExt.substr(0, 255), _TSTR);
				
				if (lpDirBuffer = AkelPad.MemAlloc(256 * _TSIZE)) { 
					AkelPad.MemCopy(lpDirBuffer, pInitialDir.substr(0, 255), _TSTR);
					
					if (lpStructure = AkelPad.MemAlloc(_X64?136:76)) {
						//Fill structure
						AkelPad.MemCopy(lpStructure, _X64?136:76, 3 /*DT_DWORD*/);										 //lStructSize
						AkelPad.MemCopy(lpStructure + (_X64?8:4), hWnd, 2 /*DT_QWORD*/);							 //hwndOwner
						AkelPad.MemCopy(lpStructure + (_X64?24:12), lpFilterBuffer, 2 /*DT_QWORD*/);	 //lpstrFilter
						AkelPad.MemCopy(lpStructure + (_X64?44:24), nFilterIndex, 3 /*DT_DWORD*/);		 //nFilterIndex
						nFilterIndex = -1;
						AkelPad.MemCopy(lpStructure + (_X64?48:28), lpFileBuffer, 2 /*DT_QWORD*/);		 //lpstrFile
						AkelPad.MemCopy(lpStructure + (_X64?56:32), 256, 3 /*DT_DWORD*/);							//nMaxFile
						AkelPad.MemCopy(lpStructure + (_X64?68:44), lpDirBuffer, 2 /*DT_QWORD*/);       //lpstrInitialDir
						AkelPad.MemCopy(lpStructure + (_X64?96:52), nFlags, 3 /*DT_DWORD*/);					 //Flags
						AkelPad.MemCopy(lpStructure + (_X64?104:60), lpExtBuffer, 2 /*DT_QWORD*/);		 //lpstrDefExt
						
						if (oSys = AkelPad.SystemFunction()) {
							//Call dialog
							if (bOpenTrueSaveFalse == true)
								nCallResult = oSys.Call("comdlg32::GetOpenFileName" + _TCHAR, lpStructure);
							else
								nCallResult = oSys.Call("comdlg32::GetSaveFileName" + _TCHAR, lpStructure);
	
							//Result file
							if (nCallResult) {
								pResultFile = AkelPad.MemRead(lpFileBuffer, _TSTR);
								nFilterIndex = AkelPad.MemRead(lpStructure + (_X64?44:24), 3 /*DT_DWORD*/) - 1;
							}
						}
						AkelPad.MemFree(lpStructure);
					}
					AkelPad.MemFree(lpDirBuffer);
				}
				AkelPad.MemFree(lpExtBuffer);
			}
			AkelPad.MemFree(lpFileBuffer);
		}
		AkelPad.MemFree(lpFilterBuffer);
	}
	return {
		file: pResultFile,
		filterIndex: nFilterIndex
	};
}

// Определение основных характеристик формата файла
function getFileFormat(hWnd /*hWndEdit*/) {
	return {
		cp: AkelPad.GetEditCodePage(hWnd),	// CodePage
		BOM: AkelPad.GetEditBOM(hWnd),
		nl: AkelPad.GetEditNewLine(hWnd)		// NewLine
	};
}

///Создание вкладки нового файла с передаваемыми параметрами
function createFile(fileFormat /*объект, полученный ранее с помощью getFileFormat*/,
                    pExt       /*расширение, для установки подстветки; если "", будет пытаться определить автоматически*/) {
	// пытаемся определить расширение для установки нужной подсветки
	var bCoderInclude = AkelPad.Include("CoderFunctions.js");
	if (!pExt && bCoderInclude)
		pExt = GetSyntaxAliasExtension();
	createNew();		//создание (закладки) для сохранения текста отдельным файлом
	AkelPad.SaveFile(0, "", fileFormat.cp, fileFormat.BOM);		//выставление кодировки
	AkelPad.SendMessage(AkelPad.GetMainWnd(), 1230 /*AKD_SETNEWLINE*/, 0, fileFormat.nl);		//выставление "формата новой строки"
	// пытаемся выставить нужную подсветку
	if (pExt && bCoderInclude) SetSyntax(pExt);
}

//Создание вкладки нового файла с отключением появления диалога Templates-плагина
function createNew() {
	AkelPad.SendMessage(AkelPad.GetMainWnd(), 273 /*WM_COMMAND*/, 4101 /*wParam=MAKEWAPARAM(0,IDM_FILE_NEW)*/, 1 /*lParam=TRUE*/);
}

//Закрытие файла и его вкладки
function closeFile() {
	AkelPad.Command(4324 /*IDM_WINDOW_FILECLOSE*/);
	AkelPad.Command(4318 /*IDM_WINDOW_FRAMECLOSE*/);
}

//Разбивает полный путь в массив [Папка, Имя_файла, Расширение]
function separateFile(pFile) {
	var pPath = "";
	var pFileName = "";
	var pFileExt = "";
	
	var pos = pFile.lastIndexOf(pSlash);
	if (pos != -1)
		pPath = pFile.slice(0, pos + 1);
	
	pFileName = pFile.slice(pos + 1);
	if (pFileName) {
		pos = pFileName.lastIndexOf(".");
		if (pos != -1)
		{
			pFileExt = pFileName.slice(pos + 1);
			pFileName = pFileName.slice(0, pos);
		}
	}
	
	return {
		path: pPath,
		name: pFileName,
		ext: pFileExt
	};
}

//Собирает полное имя файла из объекта, полученного с помощью separateFile()
function joinFile(oFile) {
	return oFile.path + oFile.name + "." + oFile.ext;
}

//Возвращает полное имя папки БЕЗ закрывающего \
function getParent(pFile) {
	var pDir = "";
	var pozLastSep = pFile.lastIndexOf(pSlash);
	if (pozLastSep != -1)
		pDir = pFile.slice(0, pozLastSep);
	return pDir;
}

//Возвращает полное имя папки С закрывающим \
function getParentClosed(pFile) {
	var pDir = getParent(pFile);
	if (pDir) pDir = pDir + pSlash;
	return pDir;
}

//Возвращает имя файла С расширением
function getFileName(pFile) {
	return pFile.slice(pFile.lastIndexOf(pSlash) + 1);
}

//Возвращает имя файла БЕЗ расширения
function getFileNameOnly(pFile) {
	var pFileName = getFileName(pFile);
	var pos = pFileName.lastIndexOf(".");
	if (pos != -1)
		pFileName = pFileName.slice(0, pos);
	return pFileName;
}

//Возвращает расширение файла
function getFileExt(pFile) {
	var ext = "";
	var pos = pFile.lastIndexOf(".");
	if (pos != -1) ext = pFile.substr(pos + 1);
	return ext;
}

function getEnvironmentPath(pPath) {
	var result = "";
	var WshShell = new ActiveXObject("WScript.shell");
	var path = WshShell.ExpandEnvironmentStrings(pPath);
	var fso = new ActiveXObject("Scripting.FileSystemObject");
	if (fso.FileExists(path)) result = path;
	return result;
}

//Remove inadmissible symbols (from wisgest)
function correctFileName(pFileNameOnly) {
	pFileNameOnly = pFileNameOnly.replace(/\t/g, " ");		//валим табуляции, т.к. диалог с ними иногда просто не отображается
	pFileNameOnly = pFileNameOnly.replace(/  /g, " ");		//убираем повторяющиеся пробелы
	return pFileNameOnly.replace(/[\\\/:\*\?"{}<>\|]/g, "");
}

function correctFileNameFull(pFile) {
	pFileNameOnly = getFileName(pFile);
	pFileNameOnly = correctFileName(pFileNameOnly);
	return getParent(pFile) + pSlash + pFileNameOnly;
}

//Получает содержимое файла из файловой системы; возвращает:
//если файла нет - undefined; если pSepRow не передан - возвращает содержимое в виде строки; если передан - возвращает массив, разбитый через pSepRow
//bCurrent = true - если переданный файл является текущим открытым, то его текст берётся сразу, если нет - из файловой системы с помощью FSO
//nSmb - количество возвращаемых символов; 0 или -1 - вернуть содержимое всего файла
function getFileContent(pFile, bCurrent, nBytesMax, pSepRow) {
	var result;
	if (nBytesMax == 0) {
		nBytesMax = -1;
	}
	if (bCurrent) {
		if (pFile == AkelPad.GetEditFile(0)) {
			result = AkelPad.GetTextRange(0, nBytesMax);
		}
	}
	if (!result) {
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		if (fso.FileExists(pFile) == true) {
			result = AkelPad.ReadFile(pFile, 0xD, 0, 0, nBytesMax);
		}
	}
	if (result) {
		if (pSepRow.length != 0) {
			result = result.split(pSepRow);
		}
	}
	return result;
}

//by MSDN
function getDrivesList() {
	var e = new Enumerator(fso.Drives);
	var x;
	var i = 0;
	var disks = [];
	for (; !e.atEnd(); e.moveNext()) {
		x = e.item();
		if (x.DriveType == 3 && x.IsReady) {
			disks[i] = x.DriveLetter;
			i += 1;
		}
	}
	return disks;
}