// http://akelpad.sourceforge.net/forum/viewtopic.php?p=21354#21354
// http://infocatcher.ucoz.net/js/akelpad_scripts/winMergeTabs.js
// https://github.com/Infocatcher/AkelPad_scripts/blob/master/winMergeTabs.js

// (c) Infocatcher 2013-2014
// Version: 0.1.2 - 2014-04-13
// Author: Infocatcher

//// Compare contents of current and next selected tab using WinMerge (http://winmerge.org/)
// or any other compare tool
// Required MDI or PMDI window mode and timer.js library!
// https://github.com/Infocatcher/AkelPad_scripts/blob/master/Include/timer.js
// (or use -noBlink=true argument to work without timer.js)

// Arguments:
//   -path="%ProgramFiles%\WinMerge\WinMerge.exe" - path to WinMerge executable
//                                                  (or many paths: "path1|path2|path3")
//   -cmd="<exe> /S=C <f1> <f2>"                  - set custom command line for any other compare tool
//                                                  (an example for Total Commander)
//   -save=true                                   - true  - save (already saved, but modified) file before compare
//                                                  false - use temporary files for modified files
//   -temp="%AkelScripts%\temp"                   - path to temporary directory
//   -useTabsOrder=true                           - always compare left tab with right tab
//   -noBlink=true                                - disable blink in status bar (and just show "Select tab!" text)

// Usage:
//   Call("Scripts::Main", 1, "winMergeTabs.js")
//   Call("Scripts::Main", 1, "winMergeTabs.js", '-path="%COMMANDER_PATH%\TOTALCMD.EXE" -cmd="<exe> /S=C <f1> <f2>"')

function _localize(s) {
	var strings = {
		"No tabs!": {
			cn: "无标签页!"
		},
		"MDI or PMDI window mode required!": {
			cn: "需要 MDI 或 PMDI 窗口模式！"
		},
		"WinMerge not found!": {
			cn: "找不到 WinMerge！"
		},
		"Select tab!": {
			cn: "请选择标签页！"
		},
		"Not found file from first tab:\n": {
			cn: "找不到第一个标签页的文件:\n"
		},
		"Not found file from second tab:\n": {
			cn: "找不到第二个标签页的文件:\n"
		}
	};
	var lng;
	switch(AkelPad.GetLangId(1 /*LANGID_PRIMARY*/)) {
		case 0x4:  lng = "cn"; break;
		default:   lng = "en";
	}
	_localize = function(s) {
		return strings[s] && strings[s][lng] || s;
	};
	return _localize(s);
}

var hScript = AkelPad.ScriptHandle(WScript.ScriptName, 3 /*SH_FINDSCRIPT*/);
if(hScript) do {
	if(AkelPad.ScriptHandle(hScript, 13 /*SH_GETMESSAGELOOP*/)) {
		// Script is running, second call close it
		AkelPad.ScriptHandle(hScript, 33 /*SH_CLOSESCRIPT*/);
		WScript.Quit();
	}
}
while(hScript = AkelPad.ScriptHandle(hScript, 32 /*SH_NEXTSAMESCRIPT*/));

var paths = AkelPad.GetArgValue("path", "");
var cmdLineTemplate = AkelPad.GetArgValue("cmd", "<exe> <f1> <f2>");
var save = AkelPad.GetArgValue("save", false);
var tempDir = AkelPad.GetArgValue("temp", "%temp%");
var useTabsOrder = AkelPad.GetArgValue("useTabsOrder", false);

var winMergePaths = paths
	? paths.split("|")
	: [
		"<HKCU\\Software\\Thingamahoochie\\WinMerge\\Executable>",
		"<HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\WinMergeU.exe\\>",
		"<HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\WinMerge.exe\\>",
		"%ProgramFiles%\\WinMerge\\WinMergeU.exe",
		"%ProgramFiles (x86)%\\WinMerge\\WinMergeU.exe",
		"%AkelDir%\\..\\WinMergePortable\\WinMergePortable.exe",
		"%COMMANDER_PATH%\\..\\WinMergePortable\\WinMergePortable.exe"
	];

var hMainWnd = AkelPad.GetMainWnd();
var oSys = AkelPad.SystemFunction();
var fso = new ActiveXObject("Scripting.FileSystemObject");
var wsh = new ActiveXObject("WScript.Shell");

if(
	hMainWnd
	&& AkelPad.IsMDI() // WMD_MDI or WMD_PMDI
) {
	var lpFrame = AkelPad.SendMessage(hMainWnd, 1288 /*AKD_FRAMEFIND*/, 1 /*FWF_CURRENT*/, 0);
	var lpFrame2;

	if(lpFrame) {
		var statusbar = new Statusbar();
		statusbar.save();
		var statusMsg = _localize("Select tab!");
		statusbar.set(statusMsg);

		if(
			!AkelPad.GetArgValue("noBlink", false)
			&& AkelPad.Include("timer.js")
		) {
			var showDelay = 600;
			var hideDelay = 150;
			// show -> [showDelay] -> hide -> [hideDelay] -> show -> [showDelay] -> hide
			var timerHide = setTimeout(function() {
				statusbar.set("");
				clearTimeout(timerHide);
				timerHide = setInterval(function() {
					statusbar.set("");
				}, showDelay + hideDelay, timerHide);
			}, showDelay);
			var timerShow = setInterval(function() {
				statusbar.set(statusMsg);
			}, showDelay + hideDelay);
			var stopTimers = function() {
				clearInterval(timerHide);
				clearInterval(timerShow);
			};
		}
	}

	if(
		lpFrame
		&& AkelPad.WindowSubClass(
			1 /*WSC_MAINPROC*/,
			mainCallback,
			0x416 /*AKDN_FRAME_ACTIVATE*/,
			0x418 /*AKDN_FRAME_DESTROY*/
		)
	) {
		AkelPad.ScriptNoMutex(); // Allow other scripts running
		AkelPad.WindowGetMessage(); // Message loop
		AkelPad.WindowUnsubClass(1 /*WSC_MAINPROC*/);

		stopTimers && stopTimers();
		statusbar.restore();
		if(lpFrame2)
			compareTabs(lpFrame, lpFrame2);
	}
	else {
		stopTimers && stopTimers();
		statusbar && statusbar.restore();
		AkelPad.MessageBox(hMainWnd, _localize("No tabs!"), WScript.ScriptName, 48 /*MB_ICONEXCLAMATION*/);
	}
}
else {
	AkelPad.MessageBox(hMainWnd, _localize("MDI or PMDI window mode required!"), WScript.ScriptName, 48 /*MB_ICONEXCLAMATION*/);
}
function mainCallback(hWnd, uMsg, wParam, lParam) {
	if(uMsg == 0x416 /*AKDN_FRAME_ACTIVATE*/) {
		if(lParam != lpFrame) {
			lpFrame2 = lParam;
			oSys.Call("user32::PostQuitMessage", 0); // Exit message loop
		}
	}
	else if(uMsg == 0x418 /*AKDN_FRAME_DESTROY*/) {
		if(lParam == lpFrame)
			oSys.Call("user32::PostQuitMessage", 0); // Exit message loop
	}
}

function compareTabs(lpFrame, lpFrame2) {
	var winMerge = getWinMerge();
	if(!winMerge) {
		AkelPad.MessageBox(
			hMainWnd,
			_localize("WinMerge not found!") + "\n\n" + getWinMergePaths(),
			WScript.ScriptName,
			48 /*MB_ICONEXCLAMATION*/
		);
		return;
	}

	setRedraw(hMainWnd, false);

	var file  = getFile(lpFrame);
	var file2 = getFile(lpFrame2);

	//if(AkelPad.SendMessage(hMainWnd, 1288 /*AKD_FRAMEFIND*/, 1 /*FWF_CURRENT*/, 0) != lpFrame2)
	//	AkelPad.SendMessage(hMainWnd, 1285 /*AKD_FRAMEACTIVATE*/, 0, lpFrame2);
	setRedraw(hMainWnd, true);
	// Force redraw current edit window
	oSys.Call("user32::InvalidateRect", AkelPad.GetEditWnd(), 0, true);

	var noFile  = !fso.FileExists(file);
	var noFile2 = !fso.FileExists(file2);
	if(noFile || noFile2) {
		var errs = [];
		if(noFile)
			errs[errs.length] = _localize("Not found file from first tab:\n") + file;
		if(noFile2)
			errs[errs.length] = _localize("Not found file from second tab:\n") + file2;
		AkelPad.MessageBox(
			hMainWnd,
			errs.join("\n\n"),
			WScript.ScriptName,
			48 /*MB_ICONEXCLAMATION*/
		);
		if(!noFile && file.isTemp)
			fso.DeleteFile(file);
		if(!noFile2 && file2.isTemp)
			fso.DeleteFile(file2);
		return;
	}

	if(useTabsOrder) {
		var pos  = AkelPad.SendMessage(hMainWnd, 1294 /*AKD_FRAMEINDEX*/, 0, lpFrame);
		var pos2 = AkelPad.SendMessage(hMainWnd, 1294 /*AKD_FRAMEINDEX*/, 0, lpFrame2);
		if(pos2 < pos) {
			var tmp = file;
			file = file2;
			file2 = tmp;
		}
	}

	var cmdLine = cmdLineTemplate
		.replace("<exe>", '"' + winMerge + '"')
		.replace("<f1>", '"' + file + '"')
		.replace("<f2>", '"' + file2 + '"');
	var wm = wsh.Exec(cmdLine);
	if(file.isTemp || file2.isTemp) {
		// If WinMerge are already opened, new process will be immediately closed,
		// so don't delete files too early
		WScript.Sleep(4000);
		for(;;) {
			if(wm.Status != 0) {
				if(file.isTemp)
					fso.DeleteFile(file);
				if(file2.isTemp)
					fso.DeleteFile(file2);
				break;
			}
			WScript.Sleep(1500);
		}
	}
}
function getFile(lpFrame) {
	AkelPad.SendMessage(hMainWnd, 1285 /*AKD_FRAMEACTIVATE*/, 0, lpFrame);
	var hWndEdit = AkelPad.GetEditWnd();
	var origFile = AkelPad.GetEditFile(hWndEdit);
	var file = origFile;
	if(!origFile || AkelPad.SendMessage(hWndEdit, 3086 /*AEM_GETMODIFY*/, 0, 0)) {
		if(origFile && save)
			AkelPad.Command(4105); // IDM_FILE_SAVE
		else {
			var tempFile = file = getTempFile(hWndEdit, origFile);
			var codePage = -1;
			var hasBOM = -1;
			if(!origFile) {
				// Will use UTF-8 with BOM to correctly save all (most?) new documents
				codePage = 65001;
				hasBOM = 1;
			}
			AkelPad.SaveFile(AkelPad.GetEditWnd(), tempFile, codePage, hasBOM, 0 /* W/o SD_UPDATE */);
		}
	}
	return file;
}
function getWinMerge() {
	for(var i = 0, l = winMergePaths.length; i < l; ++i) {
		var path = expandVariables(winMergePaths[i]);
		if(fso.FileExists(path))
			return path;
	}
	return "";
}
function getWinMergePaths() {
	var out = [];
	for(var i = 0, l = winMergePaths.length; i < l; ++i) {
		var rawPath = winMergePaths[i];
		var path = expandVariables(rawPath);
		out[out.length] = path == rawPath
			? path
			: rawPath + " => " + path
	}
	return out.join("\n");
}
function getTempFile(hWndEdit, file) {
	var fileName, fileExt;
	var tmp = file && /[^\/\\]+$/.test(file) && RegExp.lastMatch;
	if(tmp) {
		fileExt = /\.[^.]+$/.test(tmp) && RegExp.lastMatch || "";
		fileName = tmp.slice(0, -fileExt.length);
	}
	else {
		fileExt = getCurrentExtension();
		fileName = "akelpad-temp";
	}
	var tmpDir = expandVariables(tempDir);
	if(!fso.FolderExists(tmpDir))
		fso.CreateFolder(tmpDir);
	var i = -1;
	do tmp = tmpDir + "\\" + fileName + (++i ? "-" + i : "") + fileExt;
	while(fso.FileExists(tmp));
	var out = new String(tmp);
	out.isTemp = true;
	return out;
}
function getCurrentExtension() {
	var alias = getCoderAlias();
	if(/\.[^.]+$/.test(alias))
		return RegExp.lastMatch;
	return ".txt";
}
function getCoderAlias() {
	if(
		!AkelPad.IsPluginRunning("Coder::HighLight")
		&& !AkelPad.IsPluginRunning("Coder::CodeFold")
		&& !AkelPad.IsPluginRunning("Coder::AutoComplete")
	)
		return "";
	// http://akelpad.sourceforge.net/forum/viewtopic.php?p=19363#19363
	var hWndEdit = AkelPad.GetEditWnd();
	var hDocEdit = AkelPad.GetEditDoc();
	var pAlias = "";
	if(hWndEdit && hDocEdit) {
		var lpAlias = AkelPad.MemAlloc(256 * 2 /*sizeof(wchar_t)*/);
		if(lpAlias) {
			AkelPad.CallW("Coder::Settings", 18 /*DLLA_CODER_GETALIAS*/, hWndEdit, hDocEdit, lpAlias, 0);
			pAlias = AkelPad.MemRead(lpAlias, 1 /*DT_UNICODE*/);
			AkelPad.MemFree(lpAlias);
		}
	}
	return pAlias;
}

function expandVariables(s) {
	return expandEnvironmentVariables(expandRegistryVariables(s));
}
function expandEnvironmentVariables(s) {
	if(s.substr(0, 9) == "%AkelDir%")
		s = AkelPad.GetAkelDir() + s.substr(9);
	if(s.substr(0, 13) == "%AkelScripts%")
		s = AkelPad.GetAkelDir(5 /*ADTYPE_SCRIPTS*/) + s.substr(13);
	return wsh.ExpandEnvironmentStrings(s);
}
function expandRegistryVariables(s) { // <HKCU\Software\Foo\installPath>\foo.exe
	return s.replace(/<(.+?)>/g, function(s, path) {
		var val = getRegistryValue(path);
		if(val)
			return val;
		return s;
	});
}
function getRegistryValue(path) {
	try {
		return wsh.RegRead(path);
	}
	catch(e) {
	}
	return "";
}

function setRedraw(hWnd, bRedraw) {
	AkelPad.SendMessage(hWnd, 11 /*WM_SETREDRAW*/, bRedraw, 0);
	bRedraw && oSys.Call("user32::InvalidateRect", hWnd, 0, true);
}

function Statusbar() {
	this.get = this.set = this.save = this.restore = function() {};

	// Based on Instructor's code: http://akelpad.sourceforge.net/forum/viewtopic.php?p=13656#13656
	var hWndStatus = oSys.Call("user32::GetDlgItem", hMainWnd, 10002 /*ID_STATUS*/);
	if(!hWndStatus || !oSys.Call("user32::IsWindowVisible", hWndStatus))
		return;
	var nParts = AkelPad.SendMessage(hWndStatus, 1030 /*SB_GETPARTS*/, 0, 0);
	if(nParts <= 5)
		return;
	var _origStatus, _customStatus;
	var _this = this;
	function buffer(callback) {
		var lpTextBuffer = AkelPad.MemAlloc(1024 * _TSIZE);
		if(lpTextBuffer) {
			var ret = callback.call(_this, lpTextBuffer);
			AkelPad.MemFree(lpTextBuffer);
			return ret;
		}
		return undefined;
	}
	this.get = function() {
		return buffer(function(lpTextBuffer) {
			AkelPad.SendMessage(hWndStatus, _TSTR ? 1037 /*SB_GETTEXTW*/ : 1026 /*SB_GETTEXTA*/, nParts - 1, lpTextBuffer);
			return AkelPad.MemRead(lpTextBuffer, _TSTR);
		});
	};
	this.set = function(pStatusText) {
		buffer(function(lpTextBuffer) {
			_customStatus = pStatusText;
			AkelPad.MemCopy(lpTextBuffer, pStatusText, _TSTR);
			AkelPad.SendMessage(hWndStatus, _TSTR ? 1035 /*SB_SETTEXTW*/ : 1025 /*SB_SETTEXTA*/, nParts - 1, lpTextBuffer);
		});
	};
	this.save = function() {
		_origStatus = this.get();
	};
	this.restore = function() {
		if(_origStatus != undefined && this.get() == _customStatus)
			this.set(_origStatus);
	};
}