/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
#include <cstddef>
#include <iostream>
#include <string>
#include <vector>

#include <cm/memory>

#include <CoreFoundation/CoreFoundation.h>

#include "cmsys/FStream.hxx"
#include "cmsys/Process.h"
#include "cmsys/SystemTools.hxx"

// For the PATH_MAX constant
#include <sys/syslimits.h>

#define DebugError(x)                                                         \
  ofs << x << std::endl;                                                      \
  std::cout << x << std::endl

int main(int argc, char* argv[])
{
  // if ( cmsys::SystemTools::FileExists(
  cmsys::ofstream ofs("/tmp/output.txt");

  CFStringRef fileName;
  CFBundleRef appBundle;
  CFURLRef scriptFileURL;

  // get CF URL for script
  if (!(appBundle = CFBundleGetMainBundle())) {
    DebugError("Cannot get main bundle");
    return 1;
  }
  fileName = CFSTR("RuntimeScript");
  if (!(scriptFileURL =
          CFBundleCopyResourceURL(appBundle, fileName, nullptr, nullptr))) {
    DebugError("CFBundleCopyResourceURL failed");
    return 1;
  }

  // create path string
  auto path = cm::make_unique<UInt8[]>(PATH_MAX);
  if (!path) {
    return 1;
  }

  // get the file system path of the url as a cstring
  // in an encoding suitable for posix apis
  if (!CFURLGetFileSystemRepresentation(scriptFileURL, true, path.get(),
                                        PATH_MAX)) {
    DebugError("CFURLGetFileSystemRepresentation failed");
    return 1;
  }

  // dispose of the CF variable
  CFRelease(scriptFileURL);

  std::string fullScriptPath = reinterpret_cast<char*>(path.get());
  path.reset();

  if (!cmsys::SystemTools::FileExists(fullScriptPath)) {
    return 1;
  }

  std::string scriptDirectory =
    cmsys::SystemTools::GetFilenamePath(fullScriptPath);
  ofs << fullScriptPath << std::endl;
  std::vector<const char*> args;
  args.push_back(fullScriptPath.c_str());
  int cc;
  for (cc = 1; cc < argc; ++cc) {
    args.push_back(argv[cc]);
  }
  args.push_back(nullptr);

  cmsysProcess* cp = cmsysProcess_New();
  cmsysProcess_SetCommand(cp, args.data());
  cmsysProcess_SetWorkingDirectory(cp, scriptDirectory.c_str());
  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
  cmsysProcess_SetTimeout(cp, 0);
  cmsysProcess_Execute(cp);

  char* data;
  int length;
  while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
    // Translate NULL characters in the output into valid text.
    for (int i = 0; i < length; ++i) {
      if (data[i] == '\0') {
        data[i] = ' ';
      }
    }
    std::cout.write(data, length);
  }

  cmsysProcess_WaitForExit(cp, nullptr);

  bool result = true;
  if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
    if (cmsysProcess_GetExitValue(cp) != 0) {
      result = false;
    }
  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
    const char* exception_str = cmsysProcess_GetExceptionString(cp);
    std::cerr << exception_str << std::endl;
    result = false;
  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
    const char* error_str = cmsysProcess_GetErrorString(cp);
    std::cerr << error_str << std::endl;
    result = false;
  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
    const char* error_str = "Process terminated due to timeout\n";
    std::cerr << error_str << std::endl;
    result = false;
  }

  cmsysProcess_Delete(cp);

  return !result;
}
