// http://akelpad.sourceforge.net/forum/viewtopic.php?p=30006#30006
// Version: 2015-12-14
// Author: KDJ
//
// *** Run script/method/batch on tabs MDI ***
//
// Usage:
//   Call("Scripts::Main", 1, "RunScriptOnTabs.js", `-cmd='script, method or batch file' -par='script parameters' -tab=0`)
//
// Arguments (are optional):
//   -cmd - can be:
//     - script name without path (*.js or *.vbs)
//     - single Scripts method ("AkelPad." can be ommited)
//     - batch file name; if path is ommited, file should be located in AkelPad Scripts directory
//     if -cmd is not specified, default value is batch file named as the script base name with extension .tsv (eg: RunScriptOnEachTab.tsv)
//   -par - parameters passed to the script (if cmd contains script name)
//   -tab - number:
//     0 - displays question box (default)
//     1 - run on current tab without question
//     2 - run on each tab without question
//     if -cmd contains batch file and -tab is not specified, will be used value defined in batch file
//
// Run script examples:
//   Call("Scripts::Main", 1, "RunScriptOnTabs.js", `-cmd='FontIniRestore.vbs' -tab=2`)
//   Call("Scripts::Main", 1, "RunScriptOnTabs.js", `-cmd='SelectRange.js' -par='-Range="2-15"'`)
// Run method examples:
//   Call("Scripts::Main", 1, "RunScriptOnTabs.js", `-cmd='AkelPad.SetSel(10, 200, 1 /*AESELT_COLUMNON*/)'`)
//   Call("Scripts::Main", 1, "RunScriptOnTabs.js", `-cmd='Command(4108 /*IDM_FILE_PRINT*/)'`)
// Run batch examples:
//   Call("Scripts::Main", 1, "RunScriptOnTabs.js")
//   Call("Scripts::Main", 1, "RunScriptOnTabs.js", `-cmd='RunScriptOnEachTab_batch.tsv'`)
//
// Batch file example:
//   *this is comment line
//   *file should be saves as UTF-16LE with BOM
//   *each line can contain a single script/method or tab parameter definition
//   *after script name you can specify script parameters; both values should be separated by one tab character
//   *to set tab parameter to 1: #tab=1 (see -tab argument description)
//   *setting #tab will be ignored if argument -tab has been passed to the script
//   *example, delete lines from 2 to 15 in all opened documents without question:
//   #tab=2
//   SelectRange.js	-Range="2-15"
//   ReplaceSel("")

if (! AkelPad.GetEditWnd())
  WScript.Quit();

var oFSO     = new ActiveXObject("Scripting.FileSystemObject");
var hMainWnd = AkelPad.GetMainWnd();
var sDir     = AkelPad.GetAkelDir(5 /*ADTYPE_SCRIPTS*/) + "\\";
var sCmd     = AkelPad.GetArgValue("cmd", WScript.ScriptBaseName + ".tsv").replace(/^\s+|\s+$/g, "");
var sPar     = AkelPad.GetArgValue("par", "").replace(/^\s+|\s+$/g, "");
var nTab     = AkelPad.GetArgValue("tab", nTab);
var aCmd     = [];
var sMsg     = "";
var nFrames;
var aFrame;
var nChoice;
var i, n;

if (/\.tsv$/i.test(sCmd))
{
  if (! /\\/.test(sCmd))
    sCmd = sDir + sCmd;

  if (CheckBatch(sCmd))
    sMsg = "Batch file:\n" + sCmd + "\n\n";
  else
    WScript.Quit();
}
else if (/.+\.(js|vbs)$/i.test(sCmd))
{
  if (oFSO.FileExists(sDir + sCmd))
    aCmd[0] = [true, sCmd, sPar];
  else
  {
    AkelPad.MessageBox(hMainWnd, "The script does not exists:\n" + sCmd, WScript.ScriptName, 0x10 /*MB_ICONERROR*/);
    WScript.Quit();
  }
}
else
{
  if (CheckMethod(sCmd))
    aCmd[0] = [false, (/^AkelPad\./.test(sCmd) ? "" : "AkelPad.") + sCmd];
  else
  {
    AkelPad.MessageBox(hMainWnd, "Invalid argument -cmd:\n" + sCmd, WScript.ScriptName, 0x10 /*MB_ICONERROR*/);
    WScript.Quit();
  }
}

for (n = 0; n < aCmd.length; ++n)
  sMsg += aCmd[n][1] + (aCmd[n][2] ? " " + aCmd[n][2] : "") + "\n";

nFrames = AkelPad.SendMessage(hMainWnd, 1291 /*AKD_FRAMESTATS*/, 0 /*FWS_COUNTALL*/, 0);
aFrame  = [AkelPad.SendMessage(hMainWnd, 1290 /*AKD_FRAMEFINDW*/, 1 /*FWF_CURRENT*/, 0)];

if (typeof nTab != "number")
  nTab = 0;

if ((nTab != 1) && (nTab != 2))
{
  nChoice = AkelPad.MessageBox(hMainWnd, sMsg + "\nDo you want to run?", WScript.ScriptName, 0x20 /*MB_ICONQUESTION*/, 0,
                               1001, "   On current tab   ", 0x1 /*BMB_DEFAULT*/,
                               1002, "On each tab", (nFrames > 1) ? 0 : 0x2 /*BMB_DISABLED*/,
                               2,    "Cancel", 0);

  if (nChoice != 2)
    nTab = nChoice - 1000;
  else
    WScript.Quit();
}

if ((nTab == 1) || (nFrames < 2))
  nFrames = 1;
else
{
  for (i = 1; i < nFrames; ++i)
    aFrame[i] = AkelPad.SendMessage(hMainWnd, 1290 /*AKD_FRAMEFINDW*/, 9 /*FWF_TABNEXT*/, aFrame[i - 1]);
}

AkelPad.ScriptNoMutex();

for (i = 0; i < nFrames; ++i)
{
  if (AkelPad.SendMessage(hMainWnd, 1293 /*AKD_FRAMEISVALID*/, 0, aFrame[i]))
  {
    AkelPad.SendMessage(hMainWnd, 1285 /*AKD_FRAMEACTIVATE*/, 0, aFrame[i]);

    for (n = 0; n < aCmd.length; ++n)
    {
      if (aCmd[n][0])
        AkelPad.Call("Scripts::Main", 2, aCmd[n][1], aCmd[n][2]);
      else
        AkelPad.Call("Scripts::Main", 7, aCmd[n][1]);
    }
  }
}

if (AkelPad.SendMessage(hMainWnd, 1293 /*AKD_FRAMEISVALID*/, 0, aFrame[0]))
  AkelPad.SendMessage(hMainWnd, 1285 /*AKD_FRAMEACTIVATE*/, 0, aFrame[0]);

function CheckBatch(sFile)
{
  var aLine;
  var aLineCmd;
  var aExec;
  var sError;
  var i;

  if (! oFSO.FileExists(sFile))
  {
    AkelPad.MessageBox(hMainWnd, "Batch file does not exists:\n" + sFile, WScript.ScriptName, 0x10 /*MB_ICONERROR*/);
    return false;
  }

  aLine = AkelPad.ReadFile(sFile, 0x10 /*OD_ADT_NOMESSAGES*/, 1200 /*UTF-16LE*/, 1 /*bBOM*/).replace(/\r\r\n|\r\n|\r/g, "\n").split("\n");

  for (i = 0; (i < aLine.length) && (! sError); ++i)
  {
    aLine[i] = aLine[i].replace(/^\s+|\s+$/g, "");

    if (aLine[i] && (aLine[i].charAt(0) != "*"))
    {
      if (aLine[i].charAt(0) == "#")
      {
        aExec = /^#\s*tab\s*=\s*(\d+)$/i.exec(aLine[i]);

        if (aExec && aExec[1] && (typeof nTab != "number"))
          nTab = parseInt(aExec[1], 10);
      }
      else
      {
        aLineCmd = aLine[i].split("\t");

        if (! aLineCmd[1])
          aLineCmd[1] = "";

        aLineCmd[0] = aLineCmd[0].replace(/^\s+|\s+$/g, "");
        aLineCmd[1] = aLineCmd[1].replace(/^\s+|\s+$/g, "");

        if (/.+\.(js|vbs)$/i.test(aLineCmd[0]))
        {
          if (oFSO.FileExists(sDir + aLineCmd[0]))
            aCmd.push([true, aLineCmd[0], aLineCmd[1]]);
          else
            sError = "Script does not exists";
        }
        else
        {
          if (CheckMethod(aLineCmd[0]))
            aCmd.push([false, (/^AkelPad\./.test(aLineCmd[0]) ? "" : "AkelPad.") + aLineCmd[0]]);
          else
            sError = "Unknown method";
        }
      }
    }
  }

  if (sError)
  {
    AkelPad.MessageBox(hMainWnd, "Error in batch file.\n\nFile:\t" + sFile + "\nLine:\t" + i + "\nText:\t" + aLineCmd[0] + "\nRemark:\t" + sError, WScript.ScriptName, 0x10 /*MB_ICONERROR*/);
    return false;
  }

  if (! aCmd.length)
  {
    AkelPad.MessageBox(hMainWnd, "Batch file does not contain any data:\n" + sFile, WScript.ScriptName, 0x10 /*MB_ICONERROR*/);
    return false;
  }

  return true;
}

function CheckMethod(sMethod)
{
  var aMethod = [
    "ActiveXThis",
    "Call",
    "CallA",
    "CallEx",
    "CallExA",
    "CallExW",
    "CallW",
    "Command",
    "ConnectObject",
    "CreateDialog",
    "Debug",
    "DebugJIT",
    "DisconnectObject",
    "Exec",
    "Font",
    "GetAkelDir",
    "GetArgLine",
    "GetArgValue",
    "GetClipboardText",
    "GetEditBOM",
    "GetEditCodePage",
    "GetEditDoc",
    "GetEditFile",
    "GetEditModified",
    "GetEditNewLine",
    "GetEditReadOnly",
    "GetEditWnd",
    "GetFilePath",
    "GetInstanceDll",
    "GetInstanceExe",
    "GetLangId",
    "GetMainWnd",
    "GetSelEnd",
    "GetSelStart",
    "GetSelText",
    "GetTextRange",
    "Include",
    "InputBox",
    "IsAkelEdit",
    "IsInclude",
    "IsMDI",
    "IsOldWindows",
    "IsPluginRunning",
    "MemAlloc",
    "MemCopy",
    "MemFree",
    "MemPtrStr",
    "MemRead",
    "MemStrPtr",
    "MessageBox",
    "OpenFile",
    "ReadFile",
    "Recode",
    "ReplaceSel",
    "SaveFile",
    "ScriptExitCode",
    "ScriptHandle",
    "ScriptNoMutex",
    "ScriptSettings().Begin",
    "ScriptSettings().Delete",
    "ScriptSettings().End",
    "ScriptSettings().Read",
    "ScriptSettings().Write",
    "SendMessage",
    "SetClipboardText",
    "SetEditWnd",
    "SetFrameInfo",
    "SetSel",
    "SystemFunction().AddParameter",
    "SystemFunction().Call",
    "SystemFunction().GetLastError",
    "SystemFunction().RegisterCallback",
    "SystemFunction().UnregisterCallback",
    "TextFind",
    "TextReplace",
    "ThreadHook",
    "ThreadUnhook",
    "VarDispatch",
    "VarType",
    "WindowGetMessage",
    "WindowNextProc",
    "WindowNoNextProc",
    "WindowRegisterClass",
    "WindowRegisterDialog",
    "WindowSubClass",
    "WindowUnregisterClass",
    "WindowUnregisterDialog",
    "WindowUnsubClass",
    "WriteFile"];

  var i;

  for (i = 0; i < aMethod.length; ++i)
  {
    if (new RegExp("^(AkelPad\\.)?" + aMethod[i] + "\\(.*\\)$").test(sMethod))
      return true;
  }

  return false;
}
