// http://akelpad.sourceforge.net/en/plugins.php#Scripts
// Version: 1.2
// Author: Shengalts Aleksander aka Instructor
//
// Description(1033): Filter lines using regular expressions.
// Description(2052): 使用正则表达式来筛选行。

try
{
  var hMainWnd=AkelPad.GetMainWnd();
}
catch (oError)
{
  WScript.Quit();
}

if (!(AkelPad.Include("sr_function.js")) || !(AkelPad.Include("selCompleteLine.js")))
{
  WScript.Quit();
}

//Actions
var ACTL_INCLUDE=1;
var ACTL_EXCLUDE=2;

//Options
var bRegExp=true;
var bSensitive=false;
var nAction=ACTL_INCLUDE;
var nSearchStrings=10;

//Control IDs
var IDC_FIND           =1001;
var IDC_REGEXP         =1002;
var IDC_MATCHCASE      =1003;
var IDC_INCLUDE        =1004;
var IDC_EXCLUDE        =1005;
var IDC_ALLFILES       =1006;
var IDC_OK             =1007;
var IDC_CANCEL         =1008;
var IDC_STATIC         =-1;

//Variables
var hWndEdit=AkelPad.GetEditWnd();
var oSys=AkelPad.SystemFunction();
var oSet=AkelPad.ScriptSettings();
var pScriptName=WScript.ScriptName;
var hInstanceDLL=AkelPad.GetInstanceDll();
var pClassName="AkelPad::Scripts::" + pScriptName + "::" + oSys.Call("kernel32::GetCurrentProcessId");
var bAkelEdit=AkelPad.IsAkelEdit();
var hWndDialog;
var hWndStatic;
var hWndFind;
var hWndRegExp;
var hWndCase;
var hWndGroup;
var hWndInclude;
var hWndExclude;
var hWndAll;
var hWndOK;
var hWndCancel;
var rcControl=[];
var rcRdsMinMax=[];
var rcRdsCurrent=[];
var rds=[];
var lpRds=0;
var lpRdsm=0;
var lpRdsMinMax=0;
var lpRdsCurrent=0;
var ptScale=[];
var hGuiFont;
var lpBuffer;
var lpSearchBuffer;
var lpFindStrings=[];
var pFindIt="";
var nFindItLength;

var nMaxLen = 1024;
var IDC_PREVIEW        =1009;
var hWndPreview;
var bPreview=false;
var bLogPluginExists=oSys.Call("kernel32::GetFileAttributes" + _TCHAR, AkelPad.GetAkelDir(4 /*ADTYPE_PLUGS*/) + "\\Log.dll") != -1;

if (ScriptEngineMajorVersion() <= 5 && ScriptEngineMinorVersion() < 5)
{
  AkelPad.MessageBox(hMainWnd, GetLangString(0), pScriptName, 48 /*MB_ICONEXCLAMATION*/);
  WScript.Quit();
}

if (hWndEdit)
{
  if (AkelPad.WindowRegisterClass(pClassName))
  {
    if (lpBuffer=AkelPad.MemAlloc(nMaxLen * _TSIZE))
    {
      //Create dialog
      AkelPad.CreateDialog(0, pClassName, pScriptName, 0x90ce0040 /*DS_SETFONT|WS_VISIBLE|WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_THICKFRAME*/, 0, 0, 365, 165, hMainWnd, DialogCallback, 0x2 /*CDF_PIXELS*/, /* 0, 0, "宋体", 0, 9, */ "|",
                           0, "STATIC", GetLangString(3), 0x50000000 /*WS_VISIBLE|WS_CHILD*/, 14, 13, 305, 13, IDC_STATIC, "|",
                           0, "COMBOBOX", "", 0x50210042 /*WS_VISIBLE|WS_CHILD|WS_TABSTOP|WS_VSCROLL|CBS_DROPDOWN|CBS_AUTOHSCROLL*/, 11, 31, 323, 160, IDC_FIND, "|",
                           0, "BUTTON", GetLangString(4), 0x50010003 /*WS_VISIBLE|WS_CHILD|WS_TABSTOP|BS_AUTOCHECKBOX*/, 14, 69, 155, 16, IDC_REGEXP, "|",
                           0, "BUTTON", GetLangString(5), 0x50010003 /*WS_VISIBLE|WS_CHILD|WS_TABSTOP|BS_AUTOCHECKBOX*/, 14, 90, 155, 16, IDC_MATCHCASE, "|",
                           0, "BUTTON", "", 0x50000007 /*WS_VISIBLE|WS_CHILD|BS_GROUPBOX*/, 180, 57, 155, 56, IDC_STATIC, "|",
                           0, "BUTTON", GetLangString(6), 0x50000009 /*WS_VISIBLE|WS_CHILD|BS_AUTORADIOBUTTON*/, 186, 71, 143, 16, IDC_INCLUDE, "|",
                           0, "BUTTON", GetLangString(7), 0x50000009 /*WS_VISIBLE|WS_CHILD|BS_AUTORADIOBUTTON*/, 186, 90, 143, 16, IDC_EXCLUDE, "|",
                           0, "BUTTON", GetLangString(8), 0x50010000 /*WS_VISIBLE|WS_CHILD|WS_TABSTOP*/, 98, 130, 75, 23, IDC_ALLFILES, "|",
                           0, "BUTTON", GetLangString(9), 0x50010003 /*WS_VISIBLE|WS_CHILD|WS_TABSTOP|BS_AUTOCHECKBOX*/, 14, 111, 155, 16, IDC_PREVIEW, "|",
                           0, "BUTTON", GetLangString(1), 0x50010001 /*WS_VISIBLE|WS_CHILD|WS_TABSTOP|BS_DEFPUSHBUTTON*/, 179, 130, 75, 23, IDC_OK, "|",
                           0, "BUTTON", GetLangString(2), 0x50010000 /*WS_VISIBLE|WS_CHILD|WS_TABSTOP*/, 260, 130, 75, 23, IDC_CANCEL);

      if (hWndDialog)
      {
        //Allow other scripts running
        AkelPad.ScriptNoMutex();

        //Message loop
        AkelPad.WindowGetMessage(0x4 /*WGM_KEYDOWNUP*/);
      }
      AkelPad.MemFree(lpBuffer);
    }
    AkelPad.WindowUnregisterClass(pClassName);
  }
  else if (hWndDialog=oSys.Call("user32::FindWindowEx" + _TCHAR, 0, 0, pClassName, 0))
  {
    if (oSys.Call("user32::IsIconic", hWndDialog))
      oSys.Call("user32::ShowWindow", hWndDialog, 9 /*SW_RESTORE*/);
    AkelPad.SendMessage(hWndDialog, 7 /*WM_SETFOCUS*/, oSys.Call("user32::GetFocus"), 0);
  }
}


function DialogCallback(hWnd, uMsg, wParam, lParam)
{
  if (uMsg == 0x110 /*WM_INITDIALOG*/)
  {
    hWndDialog=hWnd;

    hWndStatic =oSys.Call("user32::GetDlgItem", hWndDialog, IDC_STATIC);
    hWndFind   =oSys.Call("user32::GetDlgItem", hWndDialog, IDC_FIND);
    hWndRegExp =oSys.Call("user32::GetDlgItem", hWndDialog, IDC_REGEXP);
    hWndCase   =oSys.Call("user32::GetDlgItem", hWndDialog, IDC_MATCHCASE);
    hWndGroup  =oSys.Call("user32::GetDlgItem", hWndDialog, IDC_STATIC);
    hWndInclude=oSys.Call("user32::GetDlgItem", hWndDialog, IDC_INCLUDE);
    hWndExclude=oSys.Call("user32::GetDlgItem", hWndDialog, IDC_EXCLUDE);
    hWndAll    =oSys.Call("user32::GetDlgItem", hWndDialog, IDC_ALLFILES);
    hWndOK     =oSys.Call("user32::GetDlgItem", hWndDialog, IDC_OK);
    hWndCancel =oSys.Call("user32::GetDlgItem", hWndDialog, IDC_CANCEL);
    hWndPreview=oSys.Call("user32::GetDlgItem", hWndDialog, IDC_PREVIEW);

    //Read settings
    if (oSet.Begin("", 0x1 /*POB_READ*/))
    {
      bRegExp=oSet.Read("RegExp", 1 /*PO_DWORD*/);
      bSensitive=oSet.Read("Sensitive", 1 /*PO_DWORD*/);
      bPreview=oSet.Read("Preview", 1 /*PO_DWORD*/);
      nAction=oSet.Read("Action", 1 /*PO_DWORD*/);

      //Find
      for (i=0; i < nSearchStrings; ++i)
      {
        lpFindStrings[i]=oSet.Read("Find" + i, 3 /*PO_STRING*/);
        if (typeof lpFindStrings[i] == "undefined")
          break;
      }
      if (typeof lpFindStrings[0] != "undefined")
        pFindIt=lpFindStrings[0];

      oSet.End();
    }

    //Fill combobox
    for (i=0; i < nSearchStrings && typeof lpFindStrings[i] != "undefined"; ++i)
    {
      AkelPad.SendMessage(hWndFind, 0x143 /*CB_ADDSTRING*/, 0, lpFindStrings[i]);
    }
    AkelPad.SendMessage(hWndFind, 0x14E /*CB_SETCURSEL*/, 0, 0);

    //Check that Log.dll exists
    bLogPluginExists=IsFileExist(AkelPad.GetAkelDir(4 /*ADTYPE_PLUGS*/) + "\\Log.dll");

    //Checks
    if (bRegExp) AkelPad.SendMessage(hWndRegExp, 241 /*BM_SETCHECK*/, 1 /*BST_CHECKED*/, 0);
    if (bSensitive) AkelPad.SendMessage(hWndCase, 241 /*BM_SETCHECK*/, 1 /*BST_CHECKED*/, 0);
    if (bLogPluginExists && bPreview) AkelPad.SendMessage(hWndPreview, 241 /*BM_SETCHECK*/, 1 /*BST_CHECKED*/, 0);
    if (nAction == ACTL_EXCLUDE)
      AkelPad.SendMessage(hWndExclude, 241 /*BM_SETCHECK*/, 1 /*BST_CHECKED*/, 0);
    else
      AkelPad.SendMessage(hWndInclude, 241 /*BM_SETCHECK*/, 1 /*BST_CHECKED*/, 0);

    //Center dialog
    CenterWindow(hMainWnd, hWnd);

    //Update buttons
    AkelPad.SendMessage(hWnd, 273 /*WM_COMMAND*/, IDC_FIND, 0);
  }
  else if (uMsg == 7)  //WM_SETFOCUS
  {
    oSys.Call("user32::SetFocus", hWndFind);
  }
  else if (uMsg == 256)  //WM_KEYDOWN
  {
    if (wParam == 27)  //VK_ESCAPE
    {
      //Escape key pushes Cancel button
      oSys.Call("user32::PostMessage" + _TCHAR, hWndDialog, 273 /*WM_COMMAND*/, IDC_CANCEL, 0);
    }
    else if (wParam == 13)  //VK_RETURN
    {
      if (oSys.Call("user32::IsWindowEnabled", hWndOK))
      {
        //Return key pushes OK button
        oSys.Call("user32::PostMessage" + _TCHAR, hWndDialog, 273 /*WM_COMMAND*/, IDC_OK, 0);
      }
    }
  }
  else if (uMsg == 273)  //WM_COMMAND
  {
    if (LOWORD(wParam) == IDC_FIND)
    {
      var nCurSel;
      var nTextLen;

      if (HIWORD(wParam) == 1 /*CBN_SELCHANGE*/)
      {
        nCurSel=AkelPad.SendMessage(hWndFind, 0x147 /*CB_GETCURSEL*/, 0, 0);
        nTextLen=AkelPad.SendMessage(hWndFind, 0x149 /*CB_GETLBTEXTLEN*/, nCurSel, 0);
      }
      else nTextLen=oSys.Call("user32::GetWindowTextLength" + _TCHAR, hWndFind);

      if (nTextLen)
      {
        oSys.Call("user32::EnableWindow", hWndOK, true);
        if (hWndAll) oSys.Call("user32::EnableWindow", hWndAll, true);

        if (bLogPluginExists && bPreview)
        {
          oSys.Call("user32::GetWindowText" + _TCHAR, hWndFind, lpBuffer, 256);
          pFindIt=AkelPad.MemRead(lpBuffer, _TSTR);

          try
          {
            var pResult=LineFilterPreView();
            AkelPad.Call("Log::Output", 4 + _TSTR, pResult, pResult.length, 0 /*APPEND*/);
          }
          catch (nError)
          {
            AkelPad.Call("Log::Output", 4 + _TSTR, "", 0, 0 /*APPEND*/);
            oSys.Call("user32::EnableWindow", hWndOK, false);
            if (hWndAll) oSys.Call("user32::EnableWindow", hWndAll, false);
          }
        }
      }
      else
      {
        oSys.Call("user32::EnableWindow", hWndOK, false);
        if (hWndAll) oSys.Call("user32::EnableWindow", hWndAll, false);
      }
    }
    else if (LOWORD(wParam) == IDC_REGEXP ||
             LOWORD(wParam) == IDC_MATCHCASE ||
             LOWORD(wParam) == IDC_PREVIEW ||
             LOWORD(wParam) == IDC_INCLUDE ||
             LOWORD(wParam) == IDC_EXCLUDE)
    {
      if (LOWORD(wParam) == IDC_REGEXP)
        bRegExp=AkelPad.SendMessage(hWndRegExp, 240 /*BM_GETCHECK*/, 0, 0);
      else if (LOWORD(wParam) == IDC_MATCHCASE)
        bSensitive=AkelPad.SendMessage(hWndCase, 240 /*BM_GETCHECK*/, 0, 0);
      else if (LOWORD(wParam) == IDC_PREVIEW)
        bPreview=AkelPad.SendMessage(hWndPreview, 240 /*BM_GETCHECK*/, 0, 0);
      else if (LOWORD(wParam) == IDC_INCLUDE)
        nAction=ACTL_INCLUDE;
      else if (LOWORD(wParam) == IDC_EXCLUDE)
        nAction=ACTL_EXCLUDE;
      if (bLogPluginExists && bPreview) oSys.Call("user32::PostMessage" + _TCHAR, hWndDialog, 273 /*WM_COMMAND*/, IDC_FIND, 0);
    }
    else if (LOWORD(wParam) == IDC_OK ||
             LOWORD(wParam) == IDC_ALLFILES)
    {
      //Find
      nFindItLength=oSys.Call("user32::GetWindowTextLength" + _TCHAR, hWndFind);

      if (lpSearchBuffer=AkelPad.MemAlloc((nFindItLength + 1) * _TSIZE))
      {
        oSys.Call("user32::GetWindowText" + _TCHAR, hWndFind, lpSearchBuffer, nFindItLength + 1);
        pFindIt=AkelPad.MemRead(lpSearchBuffer, _TSTR);

      if (nSearchStrings)
      {
        for (i=0; i < nSearchStrings && typeof lpFindStrings[i] != "undefined"; ++i)
        {
          if (lpFindStrings[i] == pFindIt)
          {
            AkelPad.SendMessage(hWndFind, 0x144 /*CB_DELETESTRING*/, i, 0);
            DeleteFromArray(lpFindStrings, i, 1);
          }
        }
        InsertInArray(lpFindStrings, pFindIt, 0);
        if (lpFindStrings.length > nSearchStrings)
          DeleteFromArray(lpFindStrings, -1, 1);

          //AkelPad.MemCopy(lpSearchBuffer, pFindIt, _TSTR);
          AkelPad.SendMessage(hWndFind, 0x14A /*CB_INSERTSTRING*/, 0, lpSearchBuffer);
          AkelPad.SendMessage(hWndFind, 0x14E /*CB_SETCURSEL*/, 0, 0);
        }
        AkelPad.MemFree(lpSearchBuffer);
      }

      oSys.Call("user32::EnableWindow", hWndOK, false);
      if (hWndAll) oSys.Call("user32::EnableWindow", hWndAll, false);

      if (LOWORD(wParam) == IDC_OK)
        LinesFilter(false);
      else
        LinesFilter(true);

      oSys.Call("user32::PostMessage" + _TCHAR, hWndDialog, 16 /*WM_CLOSE*/, 0, 0);
    }
    else if (LOWORD(wParam) == IDC_CANCEL)
    {
      oSys.Call("user32::PostMessage" + _TCHAR, hWndDialog, 16 /*WM_CLOSE*/, 0, 0);
    }
  }
  else if (uMsg == 16)  //WM_CLOSE
  {
    if (oSet.Begin("", 0x2 /*POB_SAVE*/))
    {
      //Save settings
      oSet.Write("RegExp", 1 /*PO_DWORD*/, bRegExp);
      oSet.Write("Sensitive", 1 /*PO_DWORD*/, bSensitive);
      oSet.Write("Preview", 1 /*PO_DWORD*/, bPreview);
      oSet.Write("Action", 1 /*PO_DWORD*/, nAction);

      //Save find history
      for (i=0; i < nSearchStrings && typeof lpFindStrings[i] != "undefined"; ++i)
        oSet.Write("Find" + i, 3 /*PO_STRING*/, lpFindStrings[i]);

      oSet.End();
    }

    //Destroy dialog
    oSys.Call("user32::DestroyWindow", hWnd);
  }
  else if (uMsg == 2)  //WM_DESTROY
  {
    //Exit message loop
    oSys.Call("user32::PostQuitMessage", 0);
  }
  return 0;
}

function LinesFilter(bAllDocuments)
{
  var lpFrameInit=AkelPad.SendMessage(hMainWnd, 1288 /*AKD_FRAMEFIND*/, 1 /*FWF_CURRENT*/, 0);
  var lpFrameCur;
  var hWndEditCur;
  var oPattern;
  var pSelText;
  var pArray;
  var pResult;
  var nFirstLine;
  var nSelStart;
  var nSelEnd;
  var bAtLineStart=false;
  var bAtLineEnd=false;
  var i;

  if (bRegExp)
  {
    if (pFindIt.charAt(0) == "^")
      bAtLineStart=true;
    else if (pFindIt.charAt(pFindIt.length - 1) == "$")
      bAtLineEnd=true;
  }

  if (bRegExp && AkelPad.Include("cnRegExp.js")) pFindIt=cnRegExp(pFindIt);

  oPattern=new RegExp((bAtLineStart?"":"^.*") + "(" + (bRegExp?pFindIt:EscRegExp(pFindIt)) + ")" + (bAtLineEnd?"":".*$") + "\n?", "gm" + (bSensitive?"":"i"));
  for (;;)
  {
    hWndEditCur=AkelPad.GetEditWnd();
    nFirstLine=SaveLineScroll(hWndEditCur);
    nSelStart=AkelPad.GetSelStart();
    nSelEnd=AkelPad.GetSelEnd();
    if (nSelStart == nSelEnd)
      AkelPad.SetSel(0, -1);
    else
      selCompleteLine(hWndEditCur, nSelStart, nSelEnd);
    pSelText=AkelPad.GetSelText(2 /*\n*/);
    if (!bAkelEdit)
      pSelText=pSelText.replace(/\r/g, "\n");
    pResult="";

    if (nAction == ACTL_INCLUDE)
    {
      if (pArray=pSelText.match(oPattern))
        pResult=pArray.join("");
    }
    else
    {
      pResult=pSelText.replace(oPattern, "");
    }

    AkelPad.ReplaceSel(pResult);
    RestoreLineScroll(hWndEditCur, nFirstLine);

    if (bAllDocuments)
    {
      //Next MDI frame
      lpFrameCur=AkelPad.Command(4316 /*IDM_WINDOW_FRAMENEXT*/);
      if (!lpFrameCur || lpFrameCur == lpFrameInit)
        break;
    }
    else break;
  }
}


//Functions
function GetLangString(nStringID)
{
  var nLangID=AkelPad.GetLangId(1 /*LANGID_PRIMARY*/);

  if (nLangID == 0x4) //LANG_CHINESE
  {
    if (nStringID == 0)
      return "JScript 版本低于 5.5。";
    if (nStringID == 1)
      return "确定";
    if (nStringID == 2)
      return "取消";
    if (nStringID == 3)
      return "字符串包含:";
    if (nStringID == 4)
      return "正则表达式";
    if (nStringID == 5)
      return "区分大小写";
    if (nStringID == 6)
      return "包括行";
    if (nStringID == 7)
      return "排除行";
    if (nStringID == 8)
      return "所有文件(&A)";
    if (nStringID == 9)
      return "预览";
  }
  else
  {
    if (nStringID == 0)
      return "JScript version is less than 5.5.";
    if (nStringID == 1)
      return "OK";
    if (nStringID == 2)
      return "Cancel";
    if (nStringID == 3)
      return "String contain:";
    if (nStringID == 4)
      return "Regular expressions";
    if (nStringID == 5)
      return "Case sensitive";
    if (nStringID == 6)
      return "Include lines";
    if (nStringID == 7)
      return "Exclude lines";
    if (nStringID == 8)
      return "&All files";
    if (nStringID == 9)
      return "Preview";
  }
  return "";
}

function LineFilterPreView()
{
  var hWndFrameInit=AkelPad.SendMessage(hMainWnd, 1288 /*AKD_FRAMEFIND*/, 1 /*FWF_CURRENT*/, 0);
  var oPattern;
  var pSelText;
  var pArray;
  var pResult;
  var nSelStart;
  var nSelEnd;
  var bAtLineStart=false;
  var bAtLineEnd=false;
  var i;

  if (bRegExp)
  {
    if (pFindIt.charAt(0) == "^")
      bAtLineStart=true;
    else if (pFindIt.charAt(pFindIt.length - 1) == "$")
      bAtLineEnd=true;
  }

  if (bRegExp && AkelPad.Include("cnRegExp.js")) pFindIt=cnRegExp(pFindIt);

  oPattern=new RegExp((bAtLineStart?"":"^.*") + "(" + (bRegExp?pFindIt:PatternToString(pFindIt)) + ")" + (bAtLineEnd?"":".*$") + "\n?", "gm" + (bSensitive?"":"i"));

  nSelStart=AkelPad.GetSelStart();
  nSelEnd=AkelPad.GetSelEnd();
  if (nSelStart == nSelEnd)
  {
    nSelStart=0;
    nSelEnd=-1;
    pSelText=AkelPad.GetTextRange(nSelStart, nSelEnd, 2 /*\n*/);
  }
  else
  {
    selCompleteLine(AkelPad.GetEditWnd(), nSelStart, nSelEnd);
    pSelText=AkelPad.GetSelText(2 /*\n*/);
  }

  if (!bAkelEdit)
    pSelText=pSelText.replace(/\r/g, "\n");
  pResult="";

  if (nAction == ACTL_INCLUDE)
  {
    if (pArray=pSelText.match(oPattern))
      pResult=pArray.join("");
  }
  else
  {
    pResult=pSelText.replace(oPattern, "");
  }
  return pResult;
}
