// http://akelpad.sourceforge.net/forum/viewtopic.php?p=31498#31498
// Version: 2016-07-03
// Author: KDJ
//
// *** Hide/Show lines that contains text ***
//
// Usage:
//   Call("Scripts::Main", 1, "LinesHideShow.js", '-hide=1 -text="^\s*$" -flag="r"')
//   Call("Scripts::Main", 1, "LinesHideShow.js", '-hide=2 -text="world" -flag="i"')
//   Call("Scripts::Main", 1, "LinesHideShow.js", '-flag="i"')
//   Call("Scripts::Main", 1, "LinesHideShow.js")
//
// Arguments:
//   -hide (number):
//     0 - show all lines,
//     1 - hide lines that contains text,
//     2 - hide lines that doesn't contain text.
//   -text (string):
//     Text to find. Can be pattern of AkelPad regular expression.
//     If not specified, it will be text selected in edit window.
//   -flag (string)
//     Can be combination of the following:
//       "i" - ignore case,
//       "w" - whole word,
//       "r" - -text is pattern of regular expression,
//       "n" - dot in regular expression specifies any character except new line "\n".
//
// Remarks:
//   Coder::CodeFold function does not work with this script, so do not use this function when any line is hidden.
//   You can not hide the first line in document.
//   Behavior when script is called without -hide and -text arguments:
//     - if no selection in edit window - show all lines,
//     - if selection is from left to right (caret is at end of selection) - hide lines that contains selected text,
//     - if selection is from right to left (caret is at beginning of selection) - hide lines that doesn't contain selected text.
//
// Warning:
//   If you select text that contains hidden lines and delete selection, the hidden lines will also be deleted!

var hMainWnd = AkelPad.GetMainWnd();
var hEditWnd = AkelPad.GetEditWnd();
var hEditWnd = AkelPad.GetEditWnd();
var oSys     = AkelPad.SystemFunction();
var nHide;
var sText;
var sFlag;
var bCodeFold;
var nLastHide;
var nFirstVisLine;
var nLastLine;
var lpCaret;
var lpSelect;
var lpPoint64;
var lpFold;
var lpPoint1;
var lpPoint2;
var lpIndex1;
var lpIndex2;
var aFold;
var nFRF;
var nOffset3;
var nLine1;
var nLine2;
var nLine3;
var nLine4;
var hFoldStack;
var i;

if (! hEditWnd)
  WScript.Quit();

nHide = AkelPad.GetArgValue("hide", 1);
sText = AkelPad.GetArgValue("text", "");
sFlag = AkelPad.GetArgValue("flag", "").toLowerCase();

ReadIni();

if ((! sText) && (! (sText = AkelPad.GetTextRange(AkelPad.GetSelStart(), AkelPad.GetSelEnd(), 1 /*\r*/))))
  nHide = 0;

nFirstVisLine = AkelPad.SendMessage(hEditWnd, 3129 /*AEM_GETLINENUMBER*/, 4 /*AEGL_FIRSTVISIBLELINE*/, 0);
nLastLine     = AkelPad.SendMessage(hEditWnd, 3129 /*AEM_GETLINENUMBER*/, 11 /*AEGL_LINEUNWRAPCOUNT*/, 0) - 1;


if (nHide == 0)
{
  if (AkelPad.SendMessage(hEditWnd, 3406 /*AEM_GETFOLDCOUNT*/, 0 /*AEFC_ALL*/, 0))
  {
    AkelPad.SendMessage(hEditWnd, 11 /*WM_SETREDRAW*/, 0, 0);

    if (AkelPad.IsPluginRunning("Coder::CodeFold"))
    {
      bCodeFold = true;
      AkelPad.Call("Coder::CodeFold");
    }

    AkelPad.SendMessage(hEditWnd, 3408 /*AEM_DELETEFOLD*/, 0 /*all*/, 0);
    AkelPad.SendMessage(hEditWnd, 3418 /*AEM_SETFOLDHIDEOFFSET*/, MakeLong(1, -1), 0);

    if (bCodeFold)
      AkelPad.Call("Coder::CodeFold");

    AkelPad.SendMessage(hEditWnd, 11 /*WM_SETREDRAW*/, 1, 0);
    AkelPad.SendMessage(hEditWnd, 3416 /*AEM_UPDATEFOLD*/, 0, nFirstVisLine);
    oSys.Call("User32::InvalidateRect", hEditWnd, 0, 1);
  }
}
else if (nLastLine > 1)
{
  nFRF  = 0x00000001 /*FRF_DOWN*/;
  if (sFlag.indexOf("i") == -1)
    nFRF |= 0x00000004 /*FRF_MATCHCASE*/;
  if (sFlag.indexOf("w") > -1)
    nFRF |= 0x00000002 /*FRF_WHOLEWORD*/;
  if (sFlag.indexOf("n") > -1)
    nFRF |= 0x00040000 /*FRF_REGEXPNONEWLINEDOT*/;
  if (sFlag.indexOf("r") > -1)
  {
    nFRF |= 0x00080000 /*FRF_REGEXP*/;
    nOffset3 = AkelPad.TextFind(0, sText, nFRF | 0x80000000 /*FRF_TEST*/);

    if (nOffset3 <= -100)
    {
      AkelPad.MessageBox(hMainWnd, "Syntax error in regular expression!", WScript.ScriptName, 0x10 /*MB_ICONERROR*/);
      WScript.Quit();
    }
  }

  lpCaret   = AkelPad.MemAlloc(_X64 ? 24 : 12); //AECHARINDEX
  lpSelect  = AkelPad.MemAlloc(_X64 ? 56 : 32); //AESELECTION
  lpPoint64 = AkelPad.MemAlloc(_X64 ? 16 :  8); //POINT64
  lpFold    = AkelPad.MemAlloc(_X64 ? 96 : 60); //AEFOLD
  lpPoint1  = AkelPad.MemAlloc(_X64 ? 80 : 44); //AEPOINT
  lpPoint2  = AkelPad.MemAlloc(_X64 ? 80 : 44); //AEPOINT
  lpIndex1  = _PtrAdd(lpPoint1, _X64 ? 16 : 8); //ciPoint
  lpIndex2  = _PtrAdd(lpPoint2, _X64 ? 16 : 8); //ciPoint

  AkelPad.MemCopy(_PtrAdd(lpPoint1, _X64 ? 40 : 20), -2 /*AEPTO_CALC*/, 2 /*DT_QWORD*/); //nPointOffset
  AkelPad.MemCopy(_PtrAdd(lpPoint2, _X64 ? 40 : 20), -2 /*AEPTO_CALC*/, 2 /*DT_QWORD*/); //nPointOffset

  AkelPad.MemCopy(_PtrAdd(lpFold, _X64 ? 40 : 20), lpPoint1, 2 /*DT_QWORD*/); //*lpMinPoint
  AkelPad.MemCopy(_PtrAdd(lpFold, _X64 ? 48 : 24), lpPoint2, 2 /*DT_QWORD*/); //*lpMaxPoint
  AkelPad.MemCopy(_PtrAdd(lpFold, _X64 ? 64 : 36),       -1, 3 /*DT_DWORD*/); //crText
  AkelPad.MemCopy(_PtrAdd(lpFold, _X64 ? 68 : 40),       -1, 3 /*DT_DWORD*/); //crBk

  if (AkelPad.IsPluginRunning("Coder::CodeFold"))
  {
    bCodeFold = true;
    AkelPad.Call("Coder::CodeFold");
  }
  else
  {
    if (nLastHide == 0)
      bCodeFold = false;
  }

  if (AkelPad.SendMessage(hEditWnd, 3406 /*AEM_GETFOLDCOUNT*/, 0 /*AEFC_ALL*/, 0))
  {
    AkelPad.SendMessage(hEditWnd, 3408 /*AEM_DELETEFOLD*/, 0 /*all*/, 0);
    AkelPad.SendMessage(hEditWnd, 3416 /*AEM_UPDATEFOLD*/, 0, nFirstVisLine);
  }

  AkelPad.SendMessage(hEditWnd, 3125 /*AEM_GETSEL*/, lpCaret, lpSelect);
  AkelPad.SendMessage(hEditWnd, 3179 /*AEM_GETSCROLLPOS*/, 0, lpPoint64);
  AkelPad.SendMessage(hMainWnd, 11 /*WM_SETREDRAW*/, 0, 0);
  AkelPad.SendMessage(hEditWnd, 11 /*WM_SETREDRAW*/, 0, 0);
  AkelPad.SetSel(0, 0);

  //reverse selection
  if ((! (/-hide=/i).test(AkelPad.GetArgLine())) && (! (/-text=/i).test(AkelPad.GetArgLine())) && (AkelPad.SendMessage(hEditWnd, 3133 /*AEM_INDEXCOMPARE*/, lpCaret, lpSelect) == 0))
    nHide = 2;

  aFold = [];

  if (nHide == 1)
  {
    nOffset3 = AkelPad.TextFind(0, sText, nFRF);

    while (nOffset3 > -1)
    {
      nLine1 = -1;

      do
      {
        nLine3 = AkelPad.SendMessage(hEditWnd, 3129 /*AEM_GETLINENUMBER*/, 21 /*AEGI_UNWRAPLINEFROMRICHOFFSET*/, nOffset3);
        nLine4 = AkelPad.SendMessage(hEditWnd, 3129 /*AEM_GETLINENUMBER*/, 21 /*AEGI_UNWRAPLINEFROMRICHOFFSET*/, -1 /*caret offset*/);

        if (nLine1 == -1)
        {
          nLine1 = nLine3;
          nLine2 = nLine4;
        }
        else if (nLine3 - nLine2 < 2)
          nLine2 = nLine4;
        else
          break;
      }
      while ((nOffset3 = AkelPad.TextFind(0, sText, nFRF)) > -1);

      if ((nLine1 != 0) || (nLine2 != 0))
      {
        if (nLine1 > 0)
          --nLine1;

        AddFold();
      }
    }
  }
  else
  {
    nOffset3 = 0;
    nLine1   = 0;
    nLine2   = nLastLine;

    while (nOffset3 > -1)
    {
      while ((nOffset3 = AkelPad.TextFind(0, sText, nFRF)) > -1)
      {
        nLine3 = AkelPad.SendMessage(hEditWnd, 3129 /*AEM_GETLINENUMBER*/, 21 /*AEGI_UNWRAPLINEFROMRICHOFFSET*/, nOffset3);
        nLine4 = AkelPad.SendMessage(hEditWnd, 3129 /*AEM_GETLINENUMBER*/, 21 /*AEGI_UNWRAPLINEFROMRICHOFFSET*/, -1 /*caret offset*/);

        if (nLine3 - nLine1 < 2)
          nLine1 = nLine4;
        else
        {
          nLine2 = nLine3 - 1;
          break;
        }
      }

      AddFold();

      nLine1 = nLine2 + 1;
      nLine2 = nLastLine;
    }
  }

  AkelPad.SendMessage(hEditWnd, 3126 /*AEM_SETSEL*/, lpCaret, lpSelect);
  AkelPad.SendMessage(hEditWnd, 3180 /*AEM_SETSCROLLPOS*/, 0, lpPoint64);

  if (aFold.length)
  {
    for (i = 0; i < aFold.length; ++i)
    {
      AkelPad.MemCopy(_PtrAdd(aFold[i], _X64 ?  0 :  0), (i == aFold.length - 1) ? 0 : aFold[i + 1], 2 /*DT_QWORD*/); //*next
      AkelPad.MemCopy(_PtrAdd(aFold[i], _X64 ?  8 :  4), (i == 0) ? 0 : aFold[i - 1], 2 /*DT_QWORD*/); //*prev
      AkelPad.MemCopy(_PtrAdd(aFold[i], _X64 ? 16 :  8), 0, 2 /*DT_QWORD*/); //*parent
      AkelPad.MemCopy(_PtrAdd(aFold[i], _X64 ? 24 : 12), 0, 2 /*DT_QWORD*/); //*firstChild
      AkelPad.MemCopy(_PtrAdd(aFold[i], _X64 ? 32 : 16), 0, 2 /*DT_QWORD*/); //*lastChild
    }

    hFoldStack = AkelPad.SendMessage(hEditWnd, 3405 /*AEM_GETFOLDSTACK*/, 0, 0);
    AkelPad.MemCopy(hFoldStack, aFold[0], 2 /*DT_QWORD*/); //first
    AkelPad.MemCopy(_PtrAdd(hFoldStack, _X64 ?  8 :  4), aFold[aFold.length - 1], 2 /*DT_QWORD*/); //last

    AkelPad.SendMessage(hEditWnd, 3418 /*AEM_SETFOLDHIDEOFFSET*/, MakeLong(1, 0), 0);
    AkelPad.SendMessage(hEditWnd, 3415 /*AEM_COLLAPSEFOLD*/, 0 /*all*/, 0x9 /*AECF_NOCARETCORRECT|AECF_COLLAPSE*/);
  }
  else if (bCodeFold && (! AkelPad.IsPluginRunning("Coder::CodeFold")))
    AkelPad.Call("Coder::CodeFold");

  AkelPad.SendMessage(hMainWnd, 11 /*WM_SETREDRAW*/, 1, 0);
  AkelPad.SendMessage(hEditWnd, 11 /*WM_SETREDRAW*/, 1, 0);
  AkelPad.SendMessage(hEditWnd, 3416 /*AEM_UPDATEFOLD*/, 0, nFirstVisLine);
  oSys.Call("User32::InvalidateRect", hMainWnd, 0, 1);
  oSys.Call("User32::InvalidateRect", hEditWnd, 0, 1);

  AkelPad.MemFree(lpCaret);
  AkelPad.MemFree(lpSelect);
  AkelPad.MemFree(lpPoint64);
  AkelPad.MemFree(lpFold);
  AkelPad.MemFree(lpPoint1);
  AkelPad.MemFree(lpPoint2);
}

WriteIni();

function MakeLong(nLoWord, nHiWord)
{
  return (nLoWord & 0xFFFF) | (nHiWord << 16);
}

function AddFold()
{
  AkelPad.SendMessage(hEditWnd, 3142 /*AEM_GETWRAPLINE*/, nLine1, lpIndex1);
  AkelPad.SendMessage(hEditWnd, 3142 /*AEM_GETWRAPLINE*/, nLine2, lpIndex2);

  aFold.push(AkelPad.SendMessage(hEditWnd, 3407 /*AEM_ADDFOLD*/, 0, lpFold));
}

function ReadIni()
{
  try
  {
    eval(AkelPad.ReadFile(WScript.ScriptFullName.replace(/\.js$/i, ".ini"), 0x10 /*OD_ADT_NOMESSAGES*/, 1200 /*UTF-16LE*/, 1));
  }
  catch (oError)
  {}

  if ((typeof bCodeFold != "boolean") && (typeof bCodeFold != "number"))
    bCodeFold = false;
  if (typeof nLastHide != "number")
    nLastHide = 0;
}

function WriteIni()
{
  var sText =
       'bCodeFold=' + bCodeFold + ';\r\n' +
       'nLastHide=' + nHide + ';\r\n';

  AkelPad.WriteFile(WScript.ScriptFullName.replace(/\.js$/i, ".ini"), sText, sText.length, 1200 /*UTF-16LE*/, 1, 1);
}
