/*=========================================================================

Program:   ParaView
Module:    pvpython.cxx

Copyright (c) Kitware, Inc.
All rights reserved.
See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

extern "C"
{
  void vtkPVInitializePythonModules();
}

#include "vtkCLIOptions.h"
#include "vtkDistributedEnvironment.h"
#include "vtkMultiProcessController.h"
#include "vtkPVApplicationOptions.h"
#include "vtkPVPythonApplication.h"
// FIXME ASYNC
//#include "vtkPVPluginTracker.h"
#include "vtkPythonInterpreter.h"
#include "vtkPythonRunLoop.h"

#include <vector>
#include <vtk_cli11.h>
#include <vtksys/SystemTools.hxx>

#include "ParaView_paraview_plugins.h"

namespace ParaViewPython
{

//---------------------------------------------------------------------------

inline void ProcessArgsForPython(std::vector<char*>& pythonArgs,
  const std::vector<std::string>& args, int vtkNotUsed(argc), char** argv)
{
  pythonArgs.clear();

  // push the executable name first.
  pythonArgs.push_back(vtksys::SystemTools::DuplicateString(argv[0]));

  // now push the unparsed arguments.
  if (args.empty())
  {
    return;
  }

  // here we handle a special case when the filename specified is a zip
  // archive.
  if (vtksys::SystemTools::GetFilenameLastExtension(args[0]) == ".zip")
  {
    // add the archive to sys.path
    vtkPythonInterpreter::PrependPythonPath(args[0].c_str());
    pythonArgs.push_back(vtksys::SystemTools::DuplicateString("-m"));

    std::string modulename = vtksys::SystemTools::GetFilenameWithoutLastExtension(
      vtksys::SystemTools::GetFilenameName(args[0]));
    pythonArgs.push_back(vtksys::SystemTools::DuplicateString(modulename.c_str()));
  }
  else
  {
    pythonArgs.push_back(vtksys::SystemTools::DuplicateString(args[0].c_str()));
  }

  for (size_t cc = 1, max = args.size(); cc < max; ++cc)
  {
    pythonArgs.push_back(vtksys::SystemTools::DuplicateString(args[cc].c_str()));
  }
}

//---------------------------------------------------------------------------
inline int Run(int processType, int argc, char* argv[])
{
  // initialize the MPI environment.
  vtkDistributedEnvironment environment(&argc, &argv);

  const std::string applicationName = "Parat";

  // process command line arguments.
  CLI::App cli(applicationName);

  // let's use nicer formatter for help text.
  vtkPVApplicationOptions::SetupDefaults(cli);

  // ASYNC proper way to pass processType ?
  vtkNew<vtkPVPythonApplication> app;

  // populate CLI with ParaView options
  app->GetOptions()->Populate(cli);

  auto* options = app->GetOptions();

  // accept positional arguments like script name
  cli.allow_extras(true);

  try
  {
    cli.parse(argc, argv);
  }
  catch (const CLI::ParseError& e)
  {
    app->Exit(app->GetRank() == 0 ? cli.exit(e) : EXIT_SUCCESS);
  }

  std::vector<std::string> extraArguments = cli.remaining();

  if (app->GetApplicationType() == vtkPVCoreApplication::BATCH && extraArguments.empty())
  {
    vtkLogF(ERROR, "No script specified. Please specify a batch script or use 'pvpython'.");
    return EXIT_FAILURE;
  }

  vtkNew<vtkPythonRunLoop> runLoop;

  app->Initialize(applicationName, runLoop->observe_on_run_loop(), environment.GetController());
  // register callback to initialize modules statically. The callback is
  // empty when BUILD_SHARED_LIBS is ON.
  vtkPVInitializePythonModules();

  // register static plugins
  ParaView_paraview_plugins_initialize();

  // FIXME ASYNC
  // vtkPVPluginTracker::GetInstance()->LoadPluginConfigurationXMLs("paraview");

  int ret_val = 0;
  if (app->GetRank() == 0)
  {
    // Process arguments
    std::vector<char*> pythonArgs;
    ProcessArgsForPython(pythonArgs, extraArguments, argc, argv);
    pythonArgs.push_back(nullptr);

    // if user specified verbosity option on command line, then we make vtkPythonInterpreter post
    // log information as INFO, otherwise we leave it at default which is TRACE.
    vtkPythonInterpreter::SetLogVerbosity(
      options->GetLogStdErrVerbosity() != vtkLogger::VERBOSITY_INVALID
        ? vtkLogger::VERBOSITY_INFO
        : vtkLogger::VERBOSITY_TRACE);

    ret_val =
      vtkPythonInterpreter::PyMain(static_cast<int>(pythonArgs.size()) - 1, &pythonArgs.front());

    // Free python args
    for (auto& ptr : pythonArgs)
    {
      delete[] ptr;
    }
  }

  // Exit application
  app->Finalize();
  if (app->GetExitCode() == EXIT_SUCCESS)
  {
    return ret_val;
  }
  return app->GetExitCode();
}
}
