/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
#pragma once

#include "cmConfigure.h" // IWYU pragma: keep

#include <iosfwd>
#include <string>

#include "cmProcessOutput.h"
#include "cmProcessTools.h"

class cmCTest;
class cmXMLWriter;

/** \class cmCTestVC
 * \brief Base class for version control system handlers
 *
 */
class cmCTestVC : public cmProcessTools
{
public:
  /** Construct with a CTest instance and update log stream.  */
  cmCTestVC(cmCTest* ctest, std::ostream& log);

  virtual ~cmCTestVC();

  /** Command line tool to invoke.  */
  void SetCommandLineTool(std::string const& tool);

  /** Top-level source directory.  */
  void SetSourceDirectory(std::string const& dir);

  /** Get the date/time specification for the current nightly start time.  */
  std::string GetNightlyTime();

  /** Prepare the work tree.  */
  bool InitialCheckout(const std::string& command);

  /** Perform cleanup operations on the work tree.  */
  void Cleanup();

  /** Update the working tree to the new revision.  */
  bool Update();

  /** Get the command line used by the Update method.  */
  std::string const& GetUpdateCommandLine() const
  {
    return this->UpdateCommandLine;
  }

  /** Write Update.xml entries for the updates found.  */
  bool WriteXML(cmXMLWriter& xml);

  /** Enumerate non-trivial working tree states during update.  */
  enum PathStatus
  {
    PathUpdated,
    PathModified,
    PathConflicting
  };

  /** Get the number of working tree paths in each state after update.  */
  int GetPathCount(PathStatus s) const { return this->PathCount[s]; }

protected:
  // Internal API to be implemented by subclasses.
  virtual void CleanupImpl();
  virtual bool NoteOldRevision();
  virtual bool UpdateImpl();
  virtual bool NoteNewRevision();
  virtual void SetNewRevision(std::string const& revision);
  virtual bool WriteXMLUpdates(cmXMLWriter& xml);

#if defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x510
  // Sun CC 5.1 needs help to allow cmCTestSVN::Revision to see this
public:
#endif
  /** Basic information about one revision of a tree or file.  */
  struct Revision
  {
    std::string Rev;
    std::string Date;
    std::string Author;
    std::string EMail;
    std::string Committer;
    std::string CommitterEMail;
    std::string CommitDate;
    std::string Log;
  };

protected:
  friend struct File;

  /** Represent change to one file.  */
  struct File
  {
    PathStatus Status = PathUpdated;
    Revision const* Rev = nullptr;
    Revision const* PriorRev = nullptr;
    File() = default;
    File(PathStatus status, Revision const* rev, Revision const* priorRev)
      : Status(status)
      , Rev(rev)
      , PriorRev(priorRev)
    {
    }
  };

  /** Convert a list of arguments to a human-readable command line.  */
  static std::string ComputeCommandLine(char const* const* cmd);

  /** Run a command line and send output to given parsers.  */
  bool RunChild(char const* const* cmd, OutputParser* out, OutputParser* err,
                const char* workDir = nullptr,
                Encoding encoding = cmProcessOutput::Auto);

  /** Run VC update command line and send output to given parsers.  */
  bool RunUpdateCommand(char const* const* cmd, OutputParser* out,
                        OutputParser* err = nullptr,
                        Encoding encoding = cmProcessOutput::Auto);

  /** Write xml element for one file.  */
  void WriteXMLEntry(cmXMLWriter& xml, std::string const& path,
                     std::string const& name, std::string const& full,
                     File const& f);

  // Instance of cmCTest running the script.
  cmCTest* CTest;

  // A stream to which we write log information.
  std::ostream& Log;

  // Basic information about the working tree.
  std::string CommandLineTool;
  std::string SourceDirectory;

  // Record update command info.
  std::string UpdateCommandLine;

  // Placeholder for unknown revisions.
  Revision Unknown;

  // Count paths reported with each PathStatus value.
  int PathCount[3];
};
