// http://akelpad.sourceforge.net/forum/viewtopic.php?p=9169#9169
// Version: 2015-09-19
// Author: KDJ
//
// *** Align selected lines to the left, right, center or justify ***
//
// Usage:
//   Call("Scripts::Main", 1, "AlignJustify.js", "Action LineLength")
//
// Arguments:
//   Action:
//     L - left
//     R - right
//     C - center
//     J - justify (default)
//   LineLength:
//     - line length in characters
//     - default is length of the longest line in selection (max line length)
//     - value less than max line length - displays input box
//
// Examples:
//   Call("Scripts::Main", 1, "AlignJustify.js")         - justify to max line length
//   Call("Scripts::Main", 1, "AlignJustify.js", "C")    - center in max line length
//   Call("Scripts::Main", 1, "AlignJustify.js", "R 80") - right align, line length 80
//   Call("Scripts::Main", 1, "AlignJustify.js", "L -1") - left align, input box with line length
//
// Remarks:
//   In the case of columnar selection, only selected text will be aligned.
//   To effect was clearly visible, you should use a fixed-width font and turn off word wrap.

var hEditWnd = AkelPad.GetEditWnd();

if (! hEditWnd)
  WScript.Quit();

var bColSel = AkelPad.SendMessage(hEditWnd, 3127 /*AEM_GETCOLUMNSEL*/, 0, 0);
var nMaxLineLen = 0;
var aLines;
var nOffset1;
var nOffset2;
var sAction;
var nLineLen;
var sLineLen;
var nWordsLen;
var aWords;
var aSpaces;
var sPadType;
var i, n;

if (bColSel)
  aLines = AkelPad.GetSelText(1 /*\r*/).replace(/\t/gm, " ").split("\r"); //replace tabs with spaces
else
{
  GetOffsets();
  aLines = AkelPad.GetTextRange(nOffset1, nOffset2, 1 /*\r*/).replace(/\t/gm, " ").split("\r"); //replace tabs with spaces
}

for (i = 0; i < aLines.length; ++i)
{
  if (nMaxLineLen < aLines[i].length)
    nMaxLineLen = aLines[i].length;

  aLines[i] = aLines[i].replace(/(^ +)|( +$)/g, ""); //delete leading and trailing spaces
}

if (nMaxLineLen < 2)
  WScript.Quit();

if (WScript.Arguments.length)
{
  sAction = WScript.Arguments(0).charAt(0).toUpperCase();

  if (WScript.Arguments.length > 1)
    nLineLen = parseInt(WScript.Arguments(1), 10);
}

if ((! sAction) || ("LRCJ".indexOf(sAction) < 0))
  sAction = "J";

if (typeof nLineLen == "undefined")
  nLineLen = nMaxLineLen;
else if (! isFinite(nLineLen))
  nLineLen = -1;

if (nLineLen < nMaxLineLen)
{
  sLineLen = nMaxLineLen.toString();

  for (;;)
  {
    sLineLen = AkelPad.InputBox(hEditWnd, WScript.ScriptName, "Line length (in characters):", sLineLen);

    if (typeof sLineLen == "undefined")
      WScript.Quit();

    nLineLen = parseInt(sLineLen, 10);

    if (nLineLen >= nMaxLineLen)
      break;

    AkelPad.MessageBox(hEditWnd, "Line length must be >= " + nMaxLineLen, WScript.ScriptName, 0x30 /*MB_ICONWARNING*/);
  }
}

if (sAction == "J")
{
  for (i = 0; i < aLines.length; ++i)
  {
    aWords = aLines[i].split(/ +/);

    if (aWords.length > 1)
    {
      nWordsLen = aLines[i].replace(/ +/g, "").length;
      aSpaces   = [];

      for (n = 0; n < aWords.length - 1; ++n)
        aSpaces.push(Pad("", Math.floor((nLineLen - nWordsLen) / (aWords.length - 1)), "R"));

      for (n = 0; n < (nLineLen - nWordsLen) % (aWords.length - 1); ++n)
        aSpaces[n] += " ";

      aLines[i] = aWords[0];
      for (n = 0; n < aSpaces.length; ++n)
        aLines[i] = aLines[i] + aSpaces[n] + aWords[n + 1];
    }
    else
      aLines[i] = Pad(aWords[0], nLineLen, "R");
  }
}
else
{
  if (sAction == "L")
    sPadType = "R";
  else if (sAction == "R")
    sPadType = "L";
  else
    sPadType = sAction;

  for (i = 0; i < aLines.length; ++i)
    aLines[i] = Pad(aLines[i], nLineLen, sPadType);
}

if (! bColSel)
  AkelPad.SetSel(nOffset1, nOffset2);

AkelPad.ReplaceSel(aLines.join("\r"), -1);

function Pad(sString, nLen, sType)
{
  var i = 0;

  if (sType == "R")
  {
    while (sString.length < nLen)
      sString += " ";
  }
  else if (sType == "L")
  {
    while (sString.length < nLen)
      sString = " " + sString;
  }
  else if (sType == "C")
  {
    while (sString.length < nLen)
    {
      if ((i % 2) == 0)
        sString += " ";
      else
        sString = " " + sString;
      ++ i;
    }
  }

  return sString;
}

function GetOffsets()
{
  var lpIndex = AkelPad.MemAlloc(_X64 ? 24 : 12 /*sizeof(AECHARINDEX)*/);

  AkelPad.SendMessage(hEditWnd, 3130 /*AEM_GETINDEX*/,  3 /*AEGI_FIRSTSELCHAR*/, lpIndex);
  AkelPad.SendMessage(hEditWnd, 3130 /*AEM_GETINDEX*/, 18 /*AEGI_WRAPLINEBEGIN*/, lpIndex);
  nOffset1 = AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, lpIndex);

  AkelPad.SendMessage(hEditWnd, 3130 /*AEM_GETINDEX*/,  4 /*AEGI_LASTSELCHAR*/, lpIndex);
  AkelPad.SendMessage(hEditWnd, 3130 /*AEM_GETINDEX*/, 19 /*AEGI_WRAPLINEEND*/, lpIndex);
  nOffset2 = AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, lpIndex);

  AkelPad.MemFree(lpIndex);
}
