// http://akelpad.sourceforge.net/forum/viewtopic.php?p=19835#19835
// Version: 1.4
// Author: Shengalts Aleksander aka Instructor
//
//
// Description(1033): Draw tables with pseudographic symbols.
//                    Lines are drawn with Shift/Ctrl/Alt+Arrow key.
//                    Second script call will turn off drawing.
//
// Arguments:
// -DoubleLine=true  -Draw with double line (default is false).
// -Shift=true       -Lines are drawn with Arrow + Shift key (default is true).
// -Ctrl=true        -Lines are drawn with Arrow + Ctrl key (default is false).
// -Alt=true         -Lines are drawn with Arrow + Alt key (default is false).
//
// Usage:
// Call("Scripts::Main", 1, "DrawLine.js", `-DoubleLine=true -Shift=false -Ctrl=true`)

//Arguments
var bDoubleLine=AkelPad.GetArgValue("DoubleLine", false);
var bShift=AkelPad.GetArgValue("Shift", true);
var bCtrl=AkelPad.GetArgValue("Ctrl", false);
var bAlt=AkelPad.GetArgValue("Alt", false);

//Variables
var hMainWnd=AkelPad.GetMainWnd();
var oSys=AkelPad.SystemFunction();
var hSubClass;
var hScript;
var lpCaretIndex;
var lpLeftIndex;
var lpUpIndex;
var lpRightIndex;
var lpDownIndex;
var nCaretChar;
var nCharInLine;
var nLeftChar;
var nRightChar;
var nUpChar;
var nDownChar;
var nInsertChar;
var dwOptions;
var dwExOptions;
var bMove;
var bInit;
var aCharsConnect;
var aLeftConnect;
var aUpConnect;
var aRightConnect;
var aDownConnect;

if (!bShift && !bCtrl && !bAlt)
{
  AkelPad.MessageBox(hMainWnd, GetLangString(0), WScript.ScriptName, 48 /*MB_ICONEXCLAMATION*/);
  WScript.Quit();
}

if ((hScript=AkelPad.ScriptHandle(WScript.ScriptName, 3 /*SH_FINDSCRIPT*/)) && AkelPad.ScriptHandle(hScript, 13 /*SH_GETMESSAGELOOP*/))
{
  //Script is running, second call close it.
  AkelPad.ScriptHandle(hScript, 33 /*SH_CLOSESCRIPT*/);
}
else
{
  if (bDoubleLine)
  {
    aCharsConnect=[9574, 9552, 9559, 9580, 9571, 9577, 9565, 9568, 9553, 9562, 9556];
    aLeftConnect=[9574, 9552, 9559, 9580, 9571, 9577, 9565];
    aUpConnect=[9568, 9580, 9571, 9553, 9562, 9577, 9565];
    aRightConnect=[9556, 9552, 9574, 9568, 9580, 9562, 9577];
    aDownConnect=[9556, 9574, 9559, 9553, 9568, 9580, 9571];
  }
  else
  {
    aCharsConnect=[9516, 9472, 9488, 9532, 9508, 9524, 9496, 9500, 9474, 9492, 9484];
    aLeftConnect=[9516, 9472, 9488, 9532, 9508, 9524, 9496];
    aUpConnect=[9500, 9532, 9508, 9474, 9492, 9524, 9496];
    aRightConnect=[9484, 9472, 9516, 9500, 9532, 9492, 9524];
    aDownConnect=[9484, 9516, 9488, 9474, 9500, 9532, 9508];
  }

  lpCaretIndex=AkelPad.MemAlloc(_X64?24:12 /*sizeof(AECHARINDEX)*/);
  lpLeftIndex=AkelPad.MemAlloc(_X64?24:12 /*sizeof(AECHARINDEX)*/);
  lpUpIndex=AkelPad.MemAlloc(_X64?24:12 /*sizeof(AECHARINDEX)*/);
  lpRightIndex=AkelPad.MemAlloc(_X64?24:12 /*sizeof(AECHARINDEX)*/);
  lpDownIndex=AkelPad.MemAlloc(_X64?24:12 /*sizeof(AECHARINDEX)*/);

  if (hSubClass=AkelPad.WindowSubClass(2 /*WSC_EDITPROC*/, EditCallback, 256 /*WM_KEYDOWN*/))
  {
    //Allow other scripts running.
    AkelPad.ScriptNoMutex();

    //Message loop
    AkelPad.WindowGetMessage();

    AkelPad.WindowUnsubClass(2 /*WSC_EDITPROC*/);
  }
  AkelPad.MemFree(lpCaretIndex);
  AkelPad.MemFree(lpLeftIndex);
  AkelPad.MemFree(lpUpIndex);
  AkelPad.MemFree(lpRightIndex);
  AkelPad.MemFree(lpDownIndex);
}

function EditCallback(hWnd, uMsg, wParam, lParam)
{
  if (uMsg == 256 /*WM_KEYDOWN*/)
  {
    if (wParam == 0x25 /*VK_LEFT*/ ||
        wParam == 0x26 /*VK_UP*/ ||
        wParam == 0x27 /*VK_RIGHT*/ ||
        wParam == 0x28 /*VK_DOWN*/)
    {
      if ((!bShift || (oSys.Call("user32::GetKeyState", 0x10 /*VK_SHIFT*/) & 0x8000)) &&
          (!bCtrl || (oSys.Call("user32::GetKeyState", 0x11 /*VK_CONTROL*/) & 0x8000)) &&
          (!bAlt || (oSys.Call("user32::GetKeyState", 0x12 /*VK_MENU*/) & 0x8000)))
      {
        dwExOptions=AkelPad.SendMessage(hWnd, 3233 /*AEM_EXGETOPTIONS*/, 0, 0)
        if (!(dwExOptions & 0x4 /*AECOE_OVERTYPE*/))
        {
          AkelPad.SendMessage(hWnd, 3379 /*AEM_LOCKUPDATE*/, 0x2 /*AELU_CARET*/, 1);
          AkelPad.SendMessage(hWnd, 3234 /*AEM_EXSETOPTIONS*/, 2 /*AECOOP_OR*/, 0x4 /*AECOE_OVERTYPE*/);
        }
        dwOptions=AkelPad.SendMessage(hWnd, 3227 /*AEM_GETOPTIONS*/, 0, 0);
        if (!(dwOptions & 0x200 /*AECO_CARETOUTEDGE*/))
          AkelPad.SendMessage(hWnd, 3228 /*AEM_SETOPTIONS*/, 2 /*AECOOP_OR*/, 0x200 /*AECO_CARETOUTEDGE*/);
        bInit=true;

        for (;;)
        {
          nLeftChar=0;
          nRightChar=0;
          nUpChar=0;
          nDownChar=0;
          bMove=false;

          AkelPad.SendMessage(hWnd, 3130 /*AEM_GETINDEX*/, 5 /*AEGI_CARETCHAR*/, lpCaretIndex);
          nCaretChar=AkelPad.SendMessage(hWnd, 3046 /*AEM_CHARAT*/, lpCaretIndex, 0);
          nCharInLine=AkelPad.MemRead(_PtrAdd(lpCaretIndex, _X64?16:8) /*offsetof(AECHARINDEX, nCharInLine)*/, 3 /*DT_DWORD*/);

          oSys.Call("kernel32::RtlMoveMemory", lpLeftIndex, lpCaretIndex, _X64?24:12 /*sizeof(AECHARINDEX)*/);
          if (AkelPad.SendMessage(hWnd, 3130 /*AEM_GETINDEX*/, 21 /*AEGI_PREVCHARINLINE*/, lpLeftIndex))
            nLeftChar=AkelPad.SendMessage(hWnd, 3046 /*AEM_CHARAT*/, lpLeftIndex, 0);

          oSys.Call("kernel32::RtlMoveMemory", lpRightIndex, lpCaretIndex, _X64?24:12 /*sizeof(AECHARINDEX)*/);
          if (AkelPad.SendMessage(hWnd, 3130 /*AEM_GETINDEX*/, 20 /*AEGI_NEXTCHARINLINE*/, lpRightIndex))
            nRightChar=AkelPad.SendMessage(hWnd, 3046 /*AEM_CHARAT*/, lpRightIndex, 0);

          oSys.Call("kernel32::RtlMoveMemory", lpUpIndex, lpCaretIndex, _X64?24:12 /*sizeof(AECHARINDEX)*/);
          if (AkelPad.SendMessage(hWnd, 3130 /*AEM_GETINDEX*/, 25 /*AEGI_PREVLINE*/, lpUpIndex))
          {
            AkelPad.MemCopy(_PtrAdd(lpUpIndex, _X64?16:8) /*offsetof(AECHARINDEX, nCharInLine)*/, nCharInLine, 3 /*DT_DWORD*/);
            nUpChar=AkelPad.SendMessage(hWnd, 3046 /*AEM_CHARAT*/, lpUpIndex, 0);
          }

          oSys.Call("kernel32::RtlMoveMemory", lpDownIndex, lpCaretIndex, _X64?24:12 /*sizeof(AECHARINDEX)*/);
          if (AkelPad.SendMessage(hWnd, 3130 /*AEM_GETINDEX*/, 24 /*AEGI_NEXTLINE*/, lpDownIndex))
          {
            AkelPad.MemCopy(_PtrAdd(lpDownIndex, _X64?16:8) /*offsetof(AECHARINDEX, nCharInLine)*/, nCharInLine, 3 /*DT_DWORD*/);
            nDownChar=AkelPad.SendMessage(hWnd, 3046 /*AEM_CHARAT*/, lpDownIndex, 0);
          }

          if (wParam == 0x25 /*VK_LEFT*/)
          {
            if (InArray(nDownChar, aUpConnect) && InArray(nUpChar, aDownConnect) && InArray(nRightChar, aLeftConnect))
              nInsertChar=aCharsConnect[3];
            else if (InArray(nDownChar, aUpConnect) && InArray(nRightChar, aLeftConnect))
              nInsertChar=aCharsConnect[0];
            else if (InArray(nUpChar, aDownConnect) && InArray(nRightChar, aLeftConnect))
              nInsertChar=aCharsConnect[5];
            else if (InArray(nDownChar, aUpConnect) && InArray(nUpChar, aDownConnect))
              nInsertChar=aCharsConnect[4];
            else if (InArray(nDownChar, aUpConnect))
              nInsertChar=aCharsConnect[2];
            else if (InArray(nUpChar, aDownConnect))
              nInsertChar=aCharsConnect[6];
            else
              nInsertChar=aCharsConnect[1];
            if (nCaretChar == nInsertChar)
            {
              if (bInit)
              {
                nInsertChar=aCharsConnect[1];
                bMove=true;
              }
              else break;
            }
            if (bMove) AkelPad.SendMessage(hWnd, 3044 /*AEM_KEYDOWN*/, 0x25 /*VK_LEFT*/, 0);
            AkelPad.SendMessage(hWnd, 3045 /*AEM_INSERTCHAR*/, nInsertChar, 0);
            AkelPad.SendMessage(hWnd, 3044 /*AEM_KEYDOWN*/, 0x25 /*VK_LEFT*/, 0);
          }
          else if (wParam == 0x26 /*VK_UP*/)
          {
            if (InArray(nLeftChar, aRightConnect) && InArray(nRightChar, aLeftConnect) && InArray(nDownChar, aUpConnect))
              nInsertChar=aCharsConnect[3];
            else if (InArray(nLeftChar, aRightConnect) && InArray(nDownChar, aUpConnect))
              nInsertChar=aCharsConnect[4];
            else if (InArray(nRightChar, aLeftConnect) && InArray(nDownChar, aUpConnect))
              nInsertChar=aCharsConnect[7];
            else if (InArray(nLeftChar, aRightConnect) && InArray(nRightChar, aLeftConnect))
              nInsertChar=aCharsConnect[5];
            else if (InArray(nLeftChar, aRightConnect))
              nInsertChar=aCharsConnect[6];
            else if (InArray(nRightChar, aLeftConnect))
              nInsertChar=aCharsConnect[9];
            else
              nInsertChar=aCharsConnect[8];
            if (nCaretChar == nInsertChar)
            {
              if (bInit)
              {
                nInsertChar=aCharsConnect[8];
                bMove=true;
              }
              else break;
            }
            if (bMove) AkelPad.SendMessage(hWnd, 3044 /*AEM_KEYDOWN*/, 0x26 /*VK_UP*/, 0);
            AkelPad.SendMessage(hWnd, 3045 /*AEM_INSERTCHAR*/, nInsertChar, 0);
            AkelPad.SendMessage(hWnd, 3044 /*AEM_KEYDOWN*/, 0x25 /*VK_LEFT*/, 0);
          }
          else if (wParam == 0x27 /*VK_RIGHT*/)
          {
            if (InArray(nDownChar, aUpConnect) && InArray(nUpChar, aDownConnect) && InArray(nLeftChar, aRightConnect))
              nInsertChar=aCharsConnect[3];
            else if (InArray(nDownChar, aUpConnect) && InArray(nLeftChar, aRightConnect))
              nInsertChar=aCharsConnect[0];
            else if (InArray(nUpChar, aDownConnect) && InArray(nLeftChar, aRightConnect))
              nInsertChar=aCharsConnect[5];
            else if (InArray(nDownChar, aUpConnect) && InArray(nUpChar, aDownConnect))
              nInsertChar=aCharsConnect[7];
            else if (InArray(nDownChar, aUpConnect))
              nInsertChar=aCharsConnect[10];
            else if (InArray(nUpChar, aDownConnect))
              nInsertChar=aCharsConnect[9];
            else
              nInsertChar=aCharsConnect[1];

            if (nCaretChar == nInsertChar)
            {
              if (bInit)
              {
                nInsertChar=aCharsConnect[1];
                bMove=true;
              }
              else break;
            }
            if (bMove) AkelPad.SendMessage(hWnd, 3044 /*AEM_KEYDOWN*/, 0x27 /*VK_RIGHT*/, 0);
            AkelPad.SendMessage(hWnd, 3045 /*AEM_INSERTCHAR*/, nInsertChar, 0);
            AkelPad.SendMessage(hWnd, 3044 /*AEM_KEYDOWN*/, 0x25 /*VK_LEFT*/, 0);
          }
          else if (wParam == 0x28 /*VK_DOWN*/)
          {
            if (InArray(nLeftChar, aRightConnect) && InArray(nRightChar, aLeftConnect) && InArray(nUpChar, aDownConnect))
              nInsertChar=aCharsConnect[3];
            else if (InArray(nLeftChar, aRightConnect) && InArray(nUpChar, aDownConnect))
              nInsertChar=aCharsConnect[4];
            else if (InArray(nRightChar, aLeftConnect) && InArray(nUpChar, aDownConnect))
              nInsertChar=aCharsConnect[7];
            else if (InArray(nLeftChar, aRightConnect) && InArray(nRightChar, aLeftConnect))
              nInsertChar=aCharsConnect[0];
            else if (InArray(nLeftChar, aRightConnect))
              nInsertChar=aCharsConnect[2];
            else if (InArray(nRightChar, aLeftConnect))
              nInsertChar=aCharsConnect[10];
            else
              nInsertChar=aCharsConnect[8];
            if (nCaretChar == nInsertChar)
            {
              if (bInit)
              {
                nInsertChar=aCharsConnect[8];
                bMove=true;
              }
              else break;
            }
            if (bMove)
            {
              if (!nDownChar) AppendText("\n");
              AkelPad.SendMessage(hWnd, 3044 /*AEM_KEYDOWN*/, 0x28 /*VK_DOWN*/, 0);
            }
            AkelPad.SendMessage(hWnd, 3045 /*AEM_INSERTCHAR*/, nInsertChar, 0);
            AkelPad.SendMessage(hWnd, 3044 /*AEM_KEYDOWN*/, 0x25 /*VK_LEFT*/, 0);
          }
          if (bInit && bMove)
            bInit=false;
          else
            break;
        }
        if (!(dwOptions & 0x200 /*AECO_CARETOUTEDGE*/))
          AkelPad.SendMessage(hWnd, 3228 /*AEM_SETOPTIONS*/, 4 /*AECOOP_XOR*/, 0x200 /*AECO_CARETOUTEDGE*/);
        if (!(dwExOptions & 0x4 /*AECOE_OVERTYPE*/))
        {
          AkelPad.SendMessage(hWnd, 3234 /*AEM_EXSETOPTIONS*/, 4 /*AECOOP_XOR*/, 0x4 /*AECOE_OVERTYPE*/);
          AkelPad.SendMessage(hWnd, 3379 /*AEM_LOCKUPDATE*/, 0x2 /*AELU_CARET*/, false);
          AkelPad.SendMessage(hWnd, 3377 /*AEM_UPDATECARET*/, 0, 0);
        }
        AkelPad.WindowNoNextProc(hSubClass);
        return 0;
      }
    }
  }
}

function InArray(nItem, lpArray)
{
  var i;

  for (i=0; i < lpArray.length; ++i)
  {
    if (lpArray[i] == nItem)
      return true;
  }
  return false;
}

function AppendText(pText)
{
  var lpAppend;

  if (lpAppend=AkelPad.MemAlloc(_X64?24:12 /*sizeof(AEAPPENDTEXTW)*/))
  {
    AkelPad.MemCopy(_PtrAdd(lpAppend, 0) /*offsetof(AEAPPENDTEXTW, pText)*/, AkelPad.MemStrPtr(pText), 2 /*DT_QWORD*/);
    AkelPad.MemCopy(_PtrAdd(lpAppend, _X64?8:4) /*offsetof(AEAPPENDTEXTW, dwTextLen)*/, pText.length, 2 /*DT_QWORD*/);
    AkelPad.MemCopy(_PtrAdd(lpAppend, _X64?16:8) /*offsetof(AEAPPENDTEXTW, nNewLine)*/, 1 /*AELB_ASINPUT*/, 3 /*DT_DWORD*/);
    AkelPad.SendMessage(AkelPad.GetEditWnd(), 3028 /*AEM_APPENDTEXTW*/, 0, lpAppend);
    AkelPad.MemFree(lpAppend);
  }
}

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

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

  if (nLangID == 0x19) //LANG_RUSSIAN
  {
    if (nStringID == 0)
      return "\x0425\x043E\x0442\x044F\x0020\x0431\x044B\x0020\x043E\x0434\x0438\x043D\x0020\x0438\x0437\x0020\x043C\x043E\x0434\x0438\x0444\x0438\x043A\x0430\x0442\x043E\x0440\x043E\x0432 Ctrl, Shift, Alt \x0434\x043E\x043B\x0436\x0435\x043D\x0020\x0431\x044B\x0442\x044C\x0020\x0432\x043A\x043B\x044E\x0447\x0435\x043D\x002E";
  }
  else
  {
    if (nStringID == 0)
      return "At least one of the modifiers Ctrl, Shift, Alt must be enabled.";
  }
  return "";
}
