/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file LICENSE.rst or https://cmake.org/licensing for details.  */
#if defined(__CYGWIN__)
// For S_IWRITE symbol
#  define _DEFAULT_SOURCE
#endif

#include "cmWIXFilesSourceWriter.h"

#include "cm_sys_stat.h"

#include "cmCMakeToWixPath.h"
#include "cmInstalledFile.h"
#include "cmSystemTools.h"
#include "cmUuid.h"
#include "cmWIXAccessControlList.h"

#ifdef _WIN32
#  include "cmsys/Encoding.hxx"
#endif

cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(unsigned long wixVersion,
                                               cmCPackLog* logger,
                                               std::string const& filename,
                                               GuidType componentGuidType)
  : cmWIXSourceWriter(wixVersion, logger, filename, componentGuidType)
{
}

void cmWIXFilesSourceWriter::EmitShortcut(std::string const& id,
                                          cmWIXShortcut const& shortcut,
                                          std::string const& shortcutPrefix,
                                          size_t shortcutIndex)
{
  std::ostringstream shortcutId;
  shortcutId << shortcutPrefix << id;

  if (shortcutIndex > 0) {
    shortcutId << "_" << shortcutIndex;
  }

  std::string fileId = std::string("CM_F") + id;

  BeginElement("Shortcut");
  AddAttribute("Id", shortcutId.str());
  AddAttribute("Name", shortcut.label);
  std::string target = "[#" + fileId + "]";
  AddAttribute("Target", target);
  AddAttribute("WorkingDirectory", shortcut.workingDirectoryId);
  EndElement("Shortcut");
}

void cmWIXFilesSourceWriter::EmitRemoveFolder(std::string const& id)
{
  BeginElement("RemoveFolder");
  AddAttribute("Id", id);
  AddAttribute("On", "uninstall");
  EndElement("RemoveFolder");
}

void cmWIXFilesSourceWriter::EmitInstallRegistryValue(
  std::string const& registryKey, std::string const& cpackComponentName,
  std::string const& suffix)
{
  std::string valueName;
  if (!cpackComponentName.empty()) {
    valueName = cpackComponentName + "_";
  }

  valueName += "installed";
  valueName += suffix;

  BeginElement("RegistryValue");
  AddAttribute("Root", "HKCU");
  AddAttribute("Key", registryKey);
  AddAttribute("Name", valueName);
  AddAttribute("Type", "integer");
  AddAttribute("Value", "1");
  AddAttribute("KeyPath", "yes");
  EndElement("RegistryValue");
}

void cmWIXFilesSourceWriter::EmitUninstallShortcut(
  std::string const& packageName)
{
  BeginElement("Shortcut");
  AddAttribute("Id", "UNINSTALL");
  AddAttribute("Name", "Uninstall " + packageName);
  AddAttribute("Description", "Uninstalls " + packageName);
  AddAttribute("Target", "[SystemFolder]msiexec.exe");
  AddAttribute("Arguments", "/x [ProductCode]");
  EndElement("Shortcut");
}

std::string cmWIXFilesSourceWriter::EmitComponentCreateFolder(
  std::string const& directoryId, std::string const& guid,
  cmInstalledFile const* installedFile)
{
  std::string componentId = std::string("CM_C_EMPTY_") + directoryId;

  BeginElement("DirectoryRef");
  AddAttribute("Id", directoryId);

  BeginElement("Component");
  AddAttribute("Id", componentId);
  AddAttribute("Guid", guid);

  BeginElement("CreateFolder");

  if (installedFile) {
    cmWIXAccessControlList acl(Logger, *installedFile, *this);
    acl.Apply();
  }

  EndElement("CreateFolder");
  EndElement("Component");
  EndElement("DirectoryRef");

  return componentId;
}

std::string cmWIXFilesSourceWriter::EmitComponentFile(
  std::string const& directoryId, std::string const& id,
  std::string const& filePath, cmWIXPatch& patch,
  cmInstalledFile const* installedFile, int diskId)
{
  std::string componentId = std::string("CM_C") + id;
  std::string fileId = std::string("CM_F") + id;

  std::string guid = CreateGuidFromComponentId(componentId);

  BeginElement("DirectoryRef");
  AddAttribute("Id", directoryId);

  BeginElement("Component");
  AddAttribute("Id", componentId);
  AddAttribute("Guid", guid);

  if (diskId) {
    AddAttribute("DiskId", std::to_string(diskId));
  }

  if (installedFile) {
    if (installedFile->GetPropertyAsBool("CPACK_NEVER_OVERWRITE")) {
      AddAttribute("NeverOverwrite", "yes");
    }
    if (installedFile->GetPropertyAsBool("CPACK_PERMANENT")) {
      AddAttribute("Permanent", "yes");
    }
  }

  patch.ApplyFragment(componentId, *this);
  BeginElement("File");
  AddAttribute("Id", fileId);

  std::string sourcePath = CMakeToWixPath(filePath);
#ifdef _WIN32
  // WiX cannot handle long paths natively, but since v4,
  // it supports long paths via UNC prefixes.
  if (this->WixVersion >= 4) {
    sourcePath = cmsys::Encoding::ToNarrow(
      cmsys::Encoding::ToWindowsExtendedPath(sourcePath));
  }
#endif
  AddAttribute("Source", sourcePath);

  AddAttribute("KeyPath", "yes");

  mode_t fileMode = 0;
  cmSystemTools::GetPermissions(filePath.c_str(), fileMode);

  if (!(fileMode & S_IWRITE)) {
    AddAttribute("ReadOnly", "yes");
  }
  patch.ApplyFragment(fileId, *this);

  if (installedFile) {
    cmWIXAccessControlList acl(Logger, *installedFile, *this);
    acl.Apply();
  }

  EndElement("File");

  EndElement("Component");
  EndElement("DirectoryRef");

  return componentId;
}
