// http://akelpad.sourceforge.net/forum/viewtopic.php?p=12548#12548
// Version: 1.9
// Author: Shengalts Aleksander aka Instructor
//
//
// Description(1033): Open/Convert files.
// Description(1049): Открыть/Конвертировать файлы.
//
// Arguments:
// -OpenMask="C:\MyFolder\*.*" -Search any files in "C:\MyFolder".
// -OpenList="C:\files.lst"    -Open files specified in "files.lst".
// -SubDir=true                -Search recursively (default is false).
// -OpenCodepage=-1            -Open codepage, if -1 it will be autodetected (default is -1).
// -OpenBOM=-1                 -File byte order mark (one of the following):
//                                1  exist.
//                                0  doesn't exist.
//                               -1  autodetect BOM (default).
// -OpenBinary=-1              -Open binary file (one of the following):
//                                1  open if binary.
//                                0  don't open if binary (default).
//                               -1  prompt every.
//                               -2  prompt once.
// -SaveDir=""                 -Save directory, if "" files will be saved in place.
// -SaveCodepage=65001         -Save codepage, if -1 current codepage will be used (default is -1).
// -SaveBOM=1                  -File byte order mark (one of the following):
//                                1  exist.
//                                0  doesn't exist.
//                               -1  current BOM will be used (default).
// -Silent=true                -Display no messages (default is false).
// -CloseNoFiles=false         -Don't close program, if after script ending no files are opened (default is true).
//
// Remark:
// If SaveCodepage and SaveBOM not specified when no convertion operation will occur and files will be just opened.
//
// Usage:
// -"Open" Call("Scripts::Main", 1, "OpenSaveMask.js", `-OpenMask="C:\MyFolder\*.txt" -SubDir=true`)
// -"Convert" Call("Scripts::Main", 1, "OpenSaveMask.js", `-OpenMask="C:\MyFolder\*.txt" -SubDir=true -SaveCodepage=65001 -SaveBOM=1`)

//Arguments
var pOpenMask=AkelPad.GetArgValue("OpenMask", "");
var pOpenList=AkelPad.GetArgValue("OpenList", "");
var bSubDir=AkelPad.GetArgValue("SubDir", false);
var nOpenCodepage=AkelPad.GetArgValue("OpenCodepage", -1);
var nOpenBOM=AkelPad.GetArgValue("OpenBOM", -1);
var nOpenBinary=AkelPad.GetArgValue("OpenBinary", 0);
var pSaveDir=AkelPad.GetArgValue("SaveDir", "");
var nSaveCodepage=AkelPad.GetArgValue("SaveCodepage", -1);
var nSaveBOM=AkelPad.GetArgValue("SaveBOM", -1);
var bSilent=AkelPad.GetArgValue("Silent", false);
var bCloseNoFiles=AkelPad.GetArgValue("CloseNoFiles", true);

//Variables
var hMainWnd=AkelPad.GetMainWnd();
var oSys=AkelPad.SystemFunction();
var hSubClass=0;
var pFileList="";
var dwCmdOptions;
var nAllFiles=0;
var nDoneFiles=0;
var nErrors=0;

if (pOpenMask || pOpenList)
{
  SetBinary();
  if (nOpenBinary == -2)
    hSubClass=AkelPad.WindowSubClass(1 /*WSC_MAINPROC*/, MainCallback, 0x43E /*AKDN_MESSAGEBOXEND*/);

  if (pOpenMask)
  {
    Locate(pOpenMask, bSubDir);
  }
  if (pOpenList)
  {
    var pFilesText;
    var pLinesArray;
    var nIndex;

    if (pFilesText=AkelPad.ReadFile(pOpenList))
    {
      pFilesText=pFilesText.replace(/\r\r\n|\r\n|\r|\n/g, "\n");
      if (pLinesArray=pFilesText.split("\n"))
      {
        for (nIndex=0; nIndex < pLinesArray.length; ++nIndex)
        {
          if (pLinesArray[nIndex])
          {
            if (DoFile(pLinesArray[nIndex], pSaveDir))
              break;
          }
        }
      }
    }
  }
  if (hSubClass)
    AkelPad.WindowUnsubClass(1 /*WSC_MAINPROC*/);
  AkelPad.SendMessage(hMainWnd, 1146 /*AKD_SETCMDLINEOPTIONS*/, dwCmdOptions, 0);

  if (nSaveCodepage != -1 || nSaveBOM != -1)
  {
    if (nAllFiles && AkelPad.IsMDI())
      AkelPad.Command(4325 /*IDM_WINDOW_FILEEXIT*/);
    if (!bSilent)
      WScript.Echo("Converted: " + nDoneFiles + " of " + nAllFiles + " files (" + nErrors + " errors)");
  }
  if (bCloseNoFiles)
  {
    if (AkelPad.SendMessage(hMainWnd, 1292 /*AKD_FRAMENOWINDOWS*/, 0, 0) ||
        (AkelPad.IsMDI() == 1 && AkelPad.SendMessage(hMainWnd, 1291 /*AKD_FRAMESTATS*/, 0 /*FWS_COUNTALL*/, 0) == 1 &&
         !AkelPad.GetEditFile(0) && !AkelPad.GetEditModified(0)))
    {
      AkelPad.Command(4109 /*IDM_FILE_EXIT*/);
    }
  }
}

function Locate(pSearchFor, bSubDir)
{
  var lpFindData=AkelPad.MemAlloc(592 /*sizeof(WIN32_FIND_DATAW)*/);
  var hSearch;
  var pDir;
  var pWildcard;
  var pFileName;
  var dwAttributes;
  var nOffset;
  var bStop=false;

  if ((nOffset=pSearchFor.lastIndexOf("\\")) != -1)
  {
    pDir=pSearchFor.substr(0, nOffset);
    pWildcard=pSearchFor.substr(nOffset + 1);

    //Enumerate directory
    if ((hSearch=oSys.Call("kernel32::FindFirstFile" + _TCHAR, pSearchFor, lpFindData)) != -1 /*INVALID_HANDLE_VALUE*/)
    {
      do
      {
        pFileName=AkelPad.MemRead(_PtrAdd(lpFindData, 44) /*offsetof(WIN32_FIND_DATAW, cFileName)*/, _TSTR);
        if (pFileName == "." || pFileName == "..") continue;
        dwAttributes=AkelPad.MemRead(_PtrAdd(lpFindData, 0) /*offsetof(WIN32_FIND_DATAW, dwAttributes)*/, 3 /*DT_DWORD*/);

        if (!(dwAttributes & 0x10 /*FILE_ATTRIBUTE_DIRECTORY*/))
        {
          if (bStop=LocateCallback(pDir, pFileName, dwAttributes, lpFindData))
            break;
        }
      }
      while (oSys.Call("kernel32::FindNextFile" + _TCHAR, hSearch, lpFindData));

      oSys.Call("kernel32::FindClose", hSearch);
    }

    //Go to subdirectory
    if (!bStop && bSubDir)
    {
      if ((hSearch=oSys.Call("kernel32::FindFirstFile" + _TCHAR, pDir + "\\" + "*.*", lpFindData)) != -1 /*INVALID_HANDLE_VALUE*/)
      {
        do
        {
          pFileName=AkelPad.MemRead(_PtrAdd(lpFindData, 44) /*offsetof(WIN32_FIND_DATAW, cFileName)*/, _TSTR);
          if (pFileName == "." || pFileName == "..") continue;
          dwAttributes=AkelPad.MemRead(_PtrAdd(lpFindData, 0) /*offsetof(WIN32_FIND_DATAW, dwAttributes)*/, 3 /*DT_DWORD*/);

          if (dwAttributes & 0x10 /*FILE_ATTRIBUTE_DIRECTORY*/)
          {
            if (bSubDir)
            {
              //Recursive call
              if (bStop=Locate(pDir + "\\" + pFileName + "\\" + pWildcard, true))
                break;
            }
          }
        }
        while (oSys.Call("kernel32::FindNextFile" + _TCHAR, hSearch, lpFindData));

        oSys.Call("kernel32::FindClose", hSearch);
      }
    }
  }

  AkelPad.MemFree(lpFindData);
  return bStop;
}

function LocateCallback(pDir, pFileName, dwAttributes, lpFindData)
{
  //var lpWriteTime=[];
  //var nSize;
  //var nFileSizeHigh;
  //var nFileSizeLow;
  //var lpLocalFileTime=AkelPad.MemAlloc(8 /*sizeof(FILETIME)*/);
  //var lpSystemTime=AkelPad.MemAlloc(16 /*sizeof(SYSTEMTIME)*/);
  //
  ////File size
  //nFileSizeHigh=AkelPad.MemRead(lpFindData + 28 /*offsetof(WIN32_FIND_DATAW, nFileSizeHigh)*/, 3 /*DT_DWORD*/);
  //nFileSizeLow=AkelPad.MemRead(lpFindData + 32 /*offsetof(WIN32_FIND_DATAW, nFileSizeLow)*/, 3 /*DT_DWORD*/);
  //nSize=(nFileSizeHigh * (0xFFFFFFFF + 1)) + nFileSizeLow;
  //
  ////Get write time
  //oSys.Call("kernel32::FileTimeToLocalFileTime", lpFindData + 20 /*offsetof(WIN32_FIND_DATAW, ftLastWriteTime)*/, lpLocalFileTime);
  //oSys.Call("kernel32::FileTimeToSystemTime", lpLocalFileTime, lpSystemTime);
  //lpWriteTime.wYear=AkelPad.MemRead(lpSystemTime /*offsetof(SYSTEMTIME, wYear)*/, 4 /*DT_WORD*/);
  //lpWriteTime.wMonth=AkelPad.MemRead(lpSystemTime + 2 /*offsetof(SYSTEMTIME, wMonth)*/, 4 /*DT_WORD*/);
  //lpWriteTime.wDayOfWeek=AkelPad.MemRead(lpSystemTime + 4 /*offsetof(SYSTEMTIME, wDayOfWeek)*/, 4 /*DT_WORD*/);
  //lpWriteTime.wDay=AkelPad.MemRead(lpSystemTime + 6 /*offsetof(SYSTEMTIME, wDay)*/, 4 /*DT_WORD*/);
  //lpWriteTime.wHour=AkelPad.MemRead(lpSystemTime + 8 /*offsetof(SYSTEMTIME, wHour)*/, 4 /*DT_WORD*/);
  //lpWriteTime.wMinute=AkelPad.MemRead(lpSystemTime + 10 /*offsetof(SYSTEMTIME, wMinute)*/, 4 /*DT_WORD*/);
  //lpWriteTime.wSecond=AkelPad.MemRead(lpSystemTime + 12 /*offsetof(SYSTEMTIME, wSecond)*/, 4 /*DT_WORD*/);
  //lpWriteTime.wMilliseconds=AkelPad.MemRead(lpSystemTime + 14 /*offsetof(SYSTEMTIME, wMilliseconds)*/, 4 /*DT_WORD*/);
  //
  //AkelPad.MemFree(lpLocalFileTime);
  //AkelPad.MemFree(lpSystemTime);

  //Return true to stop
  return DoFile(pDir + "\\" + pFileName, pSaveDir);
}

function DoFile(pOpenFile, pSaveFile)
{
  var nOpenResult;
  var dwOpenFlags=0x1|0x400 /*OD_ADT_BINARY_ERROR|OD_MULTIFILE*/;
  var bStop=false;

  if (nOpenCodepage == -1)
    dwOpenFlags|=0x4 /*OD_ADT_DETECT_CODEPAGE*/;
  if (nOpenBOM == -1)
    dwOpenFlags|=0x8 /*OD_ADT_DETECT_BOM*/;

  if (!(nOpenResult=AkelPad.OpenFile(pOpenFile, dwOpenFlags, nOpenCodepage, nOpenBOM)))
  {
    if (nSaveCodepage != -1 || nSaveBOM != -1)
    {
      if ((nSaveCodepage != -1 && AkelPad.GetEditCodePage(0) != nSaveCodepage) ||
          (nSaveBOM != -1 && AkelPad.GetEditBOM(0) != nSaveBOM))
      {
        if (!pSaveFile)
          pSaveFile=pOpenFile;
        else if (oSys.Call("kernel32::GetFileAttributes" + _TCHAR, pSaveFile) & 0x10 /*FILE_ATTRIBUTE_DIRECTORY*/)
          pSaveFile=pSaveFile + "\\" + AkelPad.GetFilePath(pOpenFile, 2 /*CPF_FILENAME*/);

        if (!AkelPad.SaveFile(0, pSaveFile, nSaveCodepage, nSaveBOM))
          ++nDoneFiles;
        else
          ++nErrors;
      }
      AkelPad.Command(4324 /*IDM_WINDOW_FILECLOSE*/);
    }
  }
  else
  {
    if (nOpenResult == -21 /*EOD_MSGCANCELCREATE*/ || nOpenResult == -22 /*EOD_MSGCANCELBINARY*/)
      bStop=true;
    if (nOpenResult != -23 /*EOD_MSGNOCREATE*/ && nOpenResult != -24 /*EOD_MSGNOBINARY*/)
      ++nErrors;
  }
  ++nAllFiles;
  return bStop;
}

function MainCallback(hWnd, uMsg, wParam, lParam)
{
  if (uMsg == 0x43E /*AKDN_MESSAGEBOXEND*/)
  {
    if (nOpenBinary == -2)
    {
      var nLoadStringID=AkelPad.MemRead(_PtrAdd(lParam, _X64?36:20) /*offsetof(NMESSAGEBOX, dwLoadStringID)*/, 3 /*DT_DWORD*/);

      if (nLoadStringID == 5 /*MSG_ERROR_BINARY*/)
      {
        var nResult=AkelPad.MemRead(_PtrAdd(lParam, _X64?40:24) /*offsetof(NMESSAGEBOX, nResult)*/, 3 /*DT_DWORD*/);

        if (nResult == 6 /*IDYES*/)
          nOpenBinary=1;
        else if (nResult == 7 /*IDNO*/)
          nOpenBinary=0;
        if (nOpenBinary != -2)
          SetBinary();
      }
    }
  }
}

function SetBinary()
{
  dwCmdOptions=AkelPad.SendMessage(hMainWnd, 1145 /*AKD_GETCMDLINEOPTIONS*/, 0, 0);
  if (nOpenBinary == 1)
    AkelPad.SendMessage(hMainWnd, 1146 /*AKD_SETCMDLINEOPTIONS*/, (dwCmdOptions | 0x10 /*CLO_MSGOPENBINARYYES*/) & ~0x20 /*CLO_MSGOPENBINARYNO*/, 0);
  else if (nOpenBinary == 0)
    AkelPad.SendMessage(hMainWnd, 1146 /*AKD_SETCMDLINEOPTIONS*/, (dwCmdOptions & ~0x10 /*CLO_MSGOPENBINARYYES*/) | 0x20 /*CLO_MSGOPENBINARYNO*/, 0);
  else if (nOpenBinary == -1 || nOpenBinary == -2)
    AkelPad.SendMessage(hMainWnd, 1146 /*AKD_SETCMDLINEOPTIONS*/, (dwCmdOptions & ~0x10 /*CLO_MSGOPENBINARYYES*/) & ~0x20 /*CLO_MSGOPENBINARYNO*/, 0);
}
