//===-- ProcessLaunchInfo.h -------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_HOST_PROCESSLAUNCHINFO_H
#define LLDB_HOST_PROCESSLAUNCHINFO_H

// C++ Headers
#include <string>

// LLDB Headers
#include "lldb/Utility/Flags.h"

#include "lldb/Host/FileAction.h"
#include "lldb/Host/Host.h"
#ifdef _WIN32
#include "lldb/Host/windows/PseudoConsole.h"
#include "lldb/Host/windows/WindowsFileAction.h"
#else
#include "lldb/Host/PseudoTerminal.h"
#endif
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/ProcessInfo.h"

namespace lldb_private {

#if defined(_WIN32)
using PTY = PseudoConsole;
using FileActionImpl = WindowsFileAction;
#else
using PTY = PseudoTerminal;
using FileActionImpl = FileAction;
#endif

// ProcessLaunchInfo
//
// Describes any information that is required to launch a process.

class ProcessLaunchInfo : public ProcessInfo {
public:
  ProcessLaunchInfo();

  ProcessLaunchInfo(const FileSpec &stdin_file_spec,
                    const FileSpec &stdout_file_spec,
                    const FileSpec &stderr_file_spec,
                    const FileSpec &working_dir, uint32_t launch_flags);

  void AppendFileAction(const FileActionImpl &info) {
    m_file_actions.push_back(info);
  }

  bool AppendCloseFileAction(int fd);

  bool AppendDuplicateFileAction(int fd, int dup_fd);

#ifdef _WIN32
  bool AppendDuplicateFileAction(HANDLE fh, HANDLE dup_fh);
#endif

  bool AppendOpenFileAction(int fd, const FileSpec &file_spec, bool read,
                            bool write);

  bool AppendSuppressFileAction(int fd, bool read, bool write);

  // Redirect stdin/stdout/stderr to a pty, if no action for the respective file
  // descriptor is specified. (So if stdin and stdout already have file actions,
  // but stderr doesn't, then only stderr will be redirected to a pty.)
  llvm::Error SetUpPtyRedirection();

#ifdef _WIN32
  // Redirect stdin/stdout/stderr to anonymous pipes instead of a ConPTY.
  // Used when terminal emulation is not needed (e.g. lldb-dap internalConsole).
  llvm::Error SetUpPipeRedirection();
#endif

  bool HasPTY() const { return m_pty != nullptr; }

  size_t GetNumFileActions() const { return m_file_actions.size(); }

  const FileAction *GetFileActionAtIndex(size_t idx) const;

  const FileAction *GetFileActionForFD(int fd) const;

  /// Returns true if fd has an explicit file action, or is the destination of a
  /// duplicate action.
  bool IsFDRedirected(int fd) const;

  Flags &GetFlags() { return m_flags; }

  const Flags &GetFlags() const { return m_flags; }

  const FileSpec &GetWorkingDirectory() const;

  void SetWorkingDirectory(const FileSpec &working_dir);

  llvm::StringRef GetProcessPluginName() const;

  void SetProcessPluginName(llvm::StringRef plugin);

  const FileSpec &GetShell() const;

  void SetShell(const FileSpec &shell);

  uint32_t GetResumeCount() const { return m_resume_count; }

  void SetResumeCount(uint32_t c) { m_resume_count = c; }

  bool GetLaunchInSeparateProcessGroup() const {
    return m_flags.Test(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
  }

  void SetLaunchInSeparateProcessGroup(bool separate);

  bool GetShellExpandArguments() const {
    return m_flags.Test(lldb::eLaunchFlagShellExpandArguments);
  }

  void SetShellExpandArguments(bool expand);

  void Clear();

  bool ConvertArgumentsForLaunchingInShell(Status &error, bool will_debug,
                                           bool first_arg_is_full_shell_command,
                                           uint32_t num_resumes);

  void SetMonitorProcessCallback(Host::MonitorChildProcessCallback callback) {
    m_monitor_callback = std::move(callback);
  }

  const Host::MonitorChildProcessCallback &GetMonitorProcessCallback() const {
    return m_monitor_callback;
  }

  /// A Monitor callback which does not take any action on process events. Use
  /// this if you don't need to take any particular action when the process
  /// terminates, but you still need to reap it.
  static void NoOpMonitorCallback(lldb::pid_t pid, int signal, int status);

  // If the LaunchInfo has a monitor callback, then arrange to monitor the
  // process. Return true if the LaunchInfo has taken care of monitoring the
  // process, and false if the caller might want to monitor the process
  // themselves.

  bool MonitorProcess() const;

  PTY &GetPTY() const { return *m_pty; }

  std::shared_ptr<PTY> TakePTY() { return std::move(m_pty); }

  /// Returns whether if lldb should read information from the PTY. This is
  /// always true on non Windows.
  bool ShouldUsePTY() const {
#ifdef _WIN32
    if (!m_pty)
      return false;
    return GetPTY().GetMode() != PseudoConsole::Mode::None &&
           GetNumFileActions() == 0;
#else
    return true;
#endif
  }

  void SetLaunchEventData(const char *data) { m_event_data.assign(data); }

  const char *GetLaunchEventData() const { return m_event_data.c_str(); }

  void SetDetachOnError(bool enable);

  bool GetDetachOnError() const {
    return m_flags.Test(lldb::eLaunchFlagDetachOnError);
  }

  /// Terminal window dimensions to use when the launcher creates a
  /// pseudo-terminal for the inferior's stdio.
  struct STDIOWindowSize {
    uint16_t cols = 0;
    uint16_t rows = 0;
  };

  void SetSTDIOWindowSize(uint16_t cols, uint16_t rows) {
    m_stdio_window_size.cols = cols;
    m_stdio_window_size.rows = rows;
    m_stdio_window_size_explicit = true;
  }

  bool IsSTDIOWindowSizeExplicit() const {
    return m_stdio_window_size_explicit;
  }

  STDIOWindowSize GetSTDIOWindowSize() const { return m_stdio_window_size; }

protected:
  FileSpec m_working_dir;
  std::string m_plugin_name;
  FileSpec m_shell;
  Flags m_flags; // Bitwise OR of bits from lldb::LaunchFlags
  std::vector<FileActionImpl>
      m_file_actions; // File actions for any other files
  std::shared_ptr<PTY> m_pty;
  uint32_t m_resume_count = 0; // How many times do we resume after launching
  Host::MonitorChildProcessCallback m_monitor_callback;
  std::string m_event_data; // A string passed to the plugin launch, having no
                            // meaning to the upper levels of lldb.
  STDIOWindowSize m_stdio_window_size;
  bool m_stdio_window_size_explicit = false;
};
}

#endif // LLDB_HOST_PROCESSLAUNCHINFO_H
