#!/bin/bash
set -eo pipefail

OS=$(uname)

die() {
  echo 'ERROR:' "$@" >&2
  exit 1
}

# beacuse OSX can't do `readlink -f`
declare -i has_readlink
if readlink -f "$0" &> /dev/null; then
  has_readlink=1
else
  has_readlink=0
fi
declare -r has_readlink

readlink_f() {
  : "${1:?'Target file not specified.'}"
  declare src="$1"

  if [ "$has_readlink" -eq 1 ]; then
    readlink -f "$src"
  else
    declare dir=
    while [ -h "$src" ]; do
      dir="$(cd -P "$( dirname "$src")" && pwd)"
      src="$(readlink "$src")"
      [[ $src != /* ]] && src="$dir/$src"
    done
    cd -P "$(dirname "$src")"
    echo "$(pwd)/$(basename "$src")"
  fi
}

### INITIALIZE ###

# the absolute path of this executable on the system
node_deb_dir="$(dirname "$(readlink_f "${BASH_SOURCE[0]}")")"
declare -r node_deb_dir

# the absolute path of the target project's source code on the system
source_dir="$(readlink_f "$(pwd)")"
declare -r source_dir

declare -i log_verbose=0

log_debug() {
  if [ "$log_verbose" -eq 1 ]; then
    echo 'DEBUG:' "$@"
  fi
}

# this is dumb, but it makes for consistency
log_info() {
  echo 'INFO:' "$@"
}

log_warn() {
  echo 'WARN:' "$@"
}

# All the variables that are injected into templates
architecture=
cli_entrypoint=
daemon_entrypoint=
package_name=
package_version=
executable_name=
user=
group=
package_description=
package_maintainer=
package_dependencies=
node_deb_version=$(jq -r '.version' "$node_deb_dir/package.json")
init=
no_rebuild=0
install_strategy=

# Other variables
no_delete_temp=0

usage() {
  # Local var because of grep
  declare helpdoc='HELP'
  helpdoc+='DOC'

  echo 'Usage: node-deb [opts] -- file [file ...]'
  echo 'Opts:'
  grep "$helpdoc" "$node_deb_dir/node-deb" -B 1 | egrep -v '^--$' | sed -e 's/^  //g' -e "s/# $helpdoc: //g"
}

cat_template() {
  declare -r template="$1"
  declare file
  file=$(find "$node_deb_dir/templates" -maxdepth 1 -type f -name "$template" | head -n 1)

  if [ -z "$file" ]; then
    die "$template is not a valid template"
  else
    cat "$file"
  fi
}

zero_check() {
  if [ -z "$1" ]; then
    die "Cannot pass zero length string to option $2"
  fi
}

show_file() {
  : "${1:?'Argument missing'}"
  echo "$PAGER"
  if [ -n "$PAGER" ]; then
    pager_cmd="$PAGER"
  elif hash less &> /dev/null; then
    pager_cmd='less'
  elif hash more &> /dev/null; then
    pager_cmd='more'
  else
    pager_cmd='cat'
  fi
  "$pager_cmd" "$1"
}

while [ -n "$1" ]; do
  param="$1"
  value="$2"

  case $param in

# HELPDOC: Package options:
    --arch)
      # TODO remove me
      log_warn 'The --arch option is deprecated. Please use --architecture. This will be removed in a future released.'
      zero_check "$value" "$param"
      architecture="$value"
      shift
      ;;
    -a | --architecture)
      # HELPDOC: Set the architecture of the Debian package (default: 'node_deb.architecture' from package.json then 'all')
      zero_check "$value" "$param"
      architecture="$value"
      shift
      ;;
    -d | --description)
      # HELPDOC:  The description of the Debian package (default: 'node_deb.description' then 'description' from package.json)
      zero_check "$value" "$param"
      package_description="$value"
      shift
      ;;
    -e | --executable-name)
      # HELPDOC: The name of the executable (default: 'node_deb.executable_name' from package.json then $package-name)
      zero_check "$value" "$param"
      executable_name="$value"
      shift
      ;;
    --extra-files)
      # HELPDOC: A directory of files to be added without subustituions to the Debian package at the root of the filesystem (default: 'node_deb.extra_files')
      extra_files="$value"
      shift
      ;;
    -g | --group)
      # HELPDOC:  The Unix group the process will run as (default: 'node_deb.group' from package.json then $user)
      zero_check "$value" "$param"
      group="$value"
      shift
      ;;
    -i | --init)
      # HELPDOC: Init script type {auto, upstart, systemd, sysv, none}. 'auto' chooses upstart, systemd or sysv. 'none' makes no init script. (default: 'node_deb.init' from package.json then 'auto')
      zero_check "$value" "$param"
      init="$value"
      shift
      ;;
    --install-strategy)
      # HELPDOC: How to install dependendencies {auto, copy, npm-install}. See README for details. (default: 'node_deb.install_strategy' from package.json, then 'auto')
      zero_check "$value" "$param"
      install_strategy="$value"
      shift
      ;;
    -m | --maintainer)
      # HELPDOC: The maintainer of the Debian package (default: 'node_deb.maintainer' then 'author' from package.json)
      zero_check "$value" "$param"
      package_maintainer="$value"
      shift
      ;;
    -n | --package-name)
      # HELPDOC: The name of the Debian package (default: 'node_deb.package_name' then 'name' from package.json)
      zero_check "$value" "$param"
      package_name="$value"
      shift
      ;;
    -o | --output-deb-name)
      # HELPDOC: The name of the Debian binary package file (default: 'node_deb.output_deb_name' then dpkg-deb default)
      zero_check "$value" "$param"
      output_deb_name="$value"
      shift
      ;;
    --no-rebuild)
      # HELPDOC: On package installation, do not attempt `npm rebuild`.
      no_rebuild=1
      ;;
    --package-dependencies)
      # HELPDOC: The dependencies for the Debian package (default: 'node_deb.dependencies')
      zero_check "$value" "$param"
      package_dependencies="$value"
      shift
      ;;
    --no-default-package-dependencies)
      # HELPDOC: Do not add default package depndencies.
      no_default_package_dependencies=1
      shift
      ;;
    -u | --user)
      # HELPDOC: The Unix user the process will run as (default: 'node_deb.user' from package.json then $package-name)
      zero_check "$value" "$param"
      user="$value"
      shift
      ;;
    -v | --version)
      # HELPDOC: The version of the Debian package (default: 'node_deb.version' then 'version' from package.json)
      zero_check "$value" "$param"
      package_version="$value"
      shift
      ;;
    --)
      # HELPDOC: Delimiter separating options from files and directories
      shift
      break
      ;;

# HELPDOC: Template options:
    --cat-template)
      # HELPDOC: Print the contents of a given template then exit
      zero_check "$value" "$param"
      cat_template "$value"
      exit 0
      ;;
    --list-json-overrides)
      # HELPDOC: List all fields of the 'node_deb' object in 'package.json' that can be used as an override then exit
      # TODO this is likely to break
      grep 'jq ' "$node_deb_dir/node-deb" | grep '\.node_deb\.' | cut -d "'" -f 2 | sort | uniq
      exit 0
      ;;
    --list-templates)
      # HELPDOC: Print a list of available templates then exit
      find "$node_deb_dir/templates" -maxdepth 1 -type f -print0 | xargs -0 -n 1 basename | sort
      exit 0
      ;;
    --list-template-variables)
      # HELPDOC: Print a list of variales available to templates then exit
      # TODO this is fragile
      declare -r temp_vars='TEMPLATE_VARS'
      declare -r begin_pattern="### BEGIN $temp_vars ###"
      declare -r end_pattern="### END $temp_vars ###"

      sed -n -e "/$begin_pattern/,/$end_pattern/{/$begin_pattern/d;/$end_pattern/d;p;}" "$node_deb_dir/node-deb" | \
      sed '1d;$d' | \
      sed -e 's:\(.*{{ \)\([a-z_]*\)\( }}.*\):\2:g' | \
      sort

      exit 0
      ;;
    --template-control)
      # HELPDOC: Override Debian control template (default: 'node_deb.templates.control' from package.json then built-in)
      zero_check "$value" "$param"
      template_control="$value"
      shift
      ;;
    --template-default-variables)
      # HELPDOC: Override default variables file template (default: 'node_deb.templates.default_variables' from package.json then built-in)
      zero_check "$value" "$param"
      template_default_variables="$value"
      shift
      ;;
    --template-executable)
      # HELPDOC: Override executable template (default: 'node_deb.templates.executable' from package.json then built-in)
      zero_check "$value" "$param"
      template_executable="$value"
      shift
      ;;
    --template-postinst)
      # HELPDOC: Override maintainer script postinst template (default: 'node_deb.templates.postinst' from package.json then built-in)
      zero_check "$value" "$param"
      template_postinst="$value"
      shift
      ;;
    --template-postrm)
      # HELPDOC: Override maintainer script postrm template (default: 'node_deb.templates.postrm' from package.json then built-in)
      zero_check "$value" "$param"
      template_postrm="$value"
      shift
      ;;
    --template-prerm)
      # HELPDOC: Override maintainer script prerm template (default: 'node_deb.templates.prem' from package.json then built-in)
      zero_check "$value" "$param"
      template_prerm="$value"
      shift
      ;;
    --template-systemd-service)
      # HELPDOC: Override systemd unit template (default: 'node_deb.templates.systemd_service' from package.json then built-in)
      zero_check "$value" "$param"
      template_systemd="$value"
      shift
      ;;
    --template-sysv-init)
      # HELPDOC: Override System-V init script template (default: 'node_deb.templates.sysv_init' from package.json then built-in)
      zero_check "$value" "$param"
      template_sysv="$value"
      shift
      ;;
    --template-upstart-conf)
      # HELPDOC: Override upstart conf template (default: 'node_deb.templates.upstart_conf' from package.json then built-in)
      zero_check "$value" "$param"
      template_upstart="$value"
      shift
      ;;

# HELPDOC: Misc. options:
    -h | --help)
      # HELPDOC: Display this message and exit
      usage
      exit 0
      ;;
    --no-delete-temp)
      # HELPDOC: Do not delete temp directory used to build Debian package
      no_delete_temp=1
      ;;
    --no-md5sums)
      # HELPDOC: DEPRECATED. This option does nothing.
      log_warn "Since 'node-deb' v0.6.0 this option does nothing. Please remove this from your build script" \
          'to ensure future compatibility.'
      ;;
    --show-changelog)
      # HELPDOC: Display the CHANGELOG then exit
      show_file "$node_deb_dir/CHANGELOG.md"
      exit 0
      ;;
    --show-readme)
      # HELPDOC: Display the README then exit
      show_file "$node_deb_dir/README.md"
      exit 0
      ;;
    --verbose)
      # HELPDOC: Print addtional information while packaging
      log_verbose=1
      ;;
    *)
      echo "Invalid option: $param" >&2
      usage >&2
      exit 1
  esac
  shift
done

### VALIDATION ###

if [ -z "$1" ]; then
  die 'You must pick at least one file or directory to add to the Debian package'
fi

for file in "$@"; do
  if ! [ -e "$file" ]; then
    die "File does not exist: '$file'. Aborting"
  fi
done

# Set the package name
if [ -z "$package_name" ]; then
  package_name=$(jq -r '.node_deb.package_name' package.json)
  if [[ "$package_name" == 'null' ]]; then
    package_name=$(jq -r '.name' package.json)
    if [ "$package_name" == 'null' ]; then
      die 'If no override is provided, your package.json must have element "node_deb.package_name" or "name"'
    fi
  fi
fi
log_debug "The package name has been set to: $package_name"

# Set the output deb name
if [ -z "$output_deb_name" ]; then
  output_deb_name="$(jq -r '.node_deb.output_deb_name' package.json)"
  if [[ "$output_deb_name" == 'null' ]]; then
    output_deb_name=""
  fi
fi
log_debug "The deb filename name has been set to: ${output_deb_name:-dbkg-deb default}"

# Set the package version
if [ -z "$package_version" ]; then
  package_version=$(jq -r '.node_deb.version' package.json)
  if [[ "$package_version" == 'null' ]]; then
    package_version=$(jq -r '.version' package.json)
    if [ "$package_version" == 'null' ]; then
      die 'If no override is provided, your package.json must have element "node_deb.package_version" "version"'
    fi
  fi
fi
log_debug "The package version has been set to: $package_version"

# Set the package description
if [ -z "$package_description" ]; then
  package_description=$(jq -r '.node_deb.description' package.json)
  if [[ "$package_description" == 'null' ]]; then
    package_description=$(jq -r '.description' package.json)
    if [[ "$package_description" == null ]]; then
      die 'If no override is provided, your package.json must have element "node_deb.package_description" or "description"'
    fi
  fi
fi
log_debug "The package description has been set and starts with: $(echo "$package_description" | head -1 | cut -c-40)"

# Set the package maintainer
if [ -z "$package_maintainer" ]; then
  package_maintainer=$(jq -r '.node_deb.maintainer' package.json)
  if [[ "$package_maintainer" == 'null' ]]; then
    package_maintainer=$(jq -r '.author' package.json)
  fi
fi
log_debug "The package maintainer has been set to: $package_maintainer"

# Set the package dependencies
if [ -z "$package_dependencies" ]; then
  package_dependencies=$(jq -r '.node_deb.dependencies' package.json)
  default_package_dependencies="nodejs, sudo"
  if [[ "$package_dependencies" != 'null' ]]; then
    default_package_dependencies="${default_package_dependencies}, "
  else
    package_dependencies=""
  fi
  if [[ $no_default_package_dependencies == 1 ]]; then
    default_package_dependencies=""
  fi
  package_dependencies="${default_package_dependencies}${package_dependencies}"
fi
log_debug "The package dependencies has been set to: $package_dependencies"

# Set the package architecture
if [ -z "$architecture" ]; then
  architecture=$(jq -r '.node_deb.architecture' package.json)
  if [[ "$architecture" == 'null' ]]; then
    architecture="all"
  fi
fi
: ${architecture:='all'}
log_debug "The package architecture has been set to: $architecture"

# Set executable name
if [ -z "$executable_name" ]; then
  executable_name=$(jq -r '.node_deb.executable_name' package.json)
  if [[ "$executable_name" == 'null' ]]; then
    executable_name="$package_name"
  fi
fi
log_debug "The executable name has been set to: $executable_name"

# Set unix user
if [ -z "$user" ]; then
  user=$(jq -r '.node_deb.user' package.json)
  if [[ "$user" == 'null' ]]; then
    user="$package_name"
  fi
fi
log_debug "The Unix user has been set to: $user"

# Set unix group
if [ -z "$group" ]; then
  group=$(jq -r '.node_deb.group' package.json)
  if [[ "$group" == 'null' ]]; then
    group="$user"
  fi
fi
log_debug "The Unix group has been set to: $group"

# Set init type
if [ -z "$init" ]; then
  init=$(jq -r '.node_deb.init' package.json)
  if [[ "$init" == 'null' ]]; then
    init='auto'
  fi
fi
case $init in
  auto|upstart|systemd|sysv|none)
    ;;
  *)
    die "Invalid init type: $init. Must be 'auto', 'upstart', 'systemd', 'sysv', or 'none'"
esac
log_debug "The init type has been set to: $init"

# Set install strategy
if [ -z "$install_strategy" ]; then
  install_strategy=$(jq -r '.node_deb.install_strategy' package.json)
  if [[ "$install_strategy" == 'null' ]]; then
    install_strategy='auto'
  fi
fi
case $install_strategy in
  auto|copy|npm-install)
    ;;
  *)
    die "Invalid install strategy. Must be 'auto', 'copy', or 'npm-install'"
esac
log_debug "The install strategy has been set to: $install_strategy"

# Check for extra files
if [ -z "$extra_files" ]; then
  extra_files=$(jq -r '.node_deb.extra_files' package.json)
  if [[ "$extra_files" == 'null' ]]; then
    extra_files=''
  fi
fi
log_debug "The extra_files directory was set to: $extra_files"

# Set control template
if [ -z "$template_control" ]; then
  template_control=$(jq -r '.node_deb.templates.control' package.json)
  if [[ "$template_control" == 'null' ]]; then
    template_control=''
  fi
fi
: ${template_control:="$node_deb_dir/templates/control"}
log_debug "The control template has been set to: $template_control"

# Set executable template
if [ -z "$template_executable" ]; then
  template_executable=$(jq -r '.node_deb.templates.executable' package.json)
  if [[ "$template_executable" == 'null' ]]; then
    template_executable=''
  fi
fi
: ${template_executable:="$node_deb_dir/templates/executable"}
log_debug "The executable template has been set to: $template_executable"

# Set postinst template
if [ -z "$template_postinst" ]; then
  template_postinst=$(jq -r '.node_deb.templates.postinst' package.json)
  if [[ "$template_postinst" == 'null' ]]; then
    template_postinst=''
  fi
fi
: ${template_postinst:="$node_deb_dir/templates/postinst"}
log_debug "The postinst template has been set to: $template_postinst"

# Set postrm template
if [ -z "$template_postrm" ]; then
  template_postrm=$(jq -r '.node_deb.templates.postrm' package.json)
  if [[ "$template_postrm" == 'null' ]]; then
    template_postrm=''
  fi
fi
: ${template_postrm:="$node_deb_dir/templates/postrm"}
log_debug "The postrm template has been set to: $template_postrm"

# Set prerm template
if [ -z "$template_prerm" ]; then
  template_prerm=$(jq -r '.node_deb.templates.prerm' package.json)
  if [[ "$template_prerm" == 'null' ]]; then
    template_prerm=''
  fi
fi
: ${template_prerm:="$node_deb_dir/templates/prerm"}
log_debug "The prerm template has been set to: $template_prerm"

# Set systemd unit template
if [ -z "$template_systemd" ]; then
  template_systemd=$(jq -r '.node_deb.templates.systemd_service' package.json)
  if [[ "$template_systemd" == 'null' ]]; then
    template_systemd=''
  fi
fi
: ${template_systemd:="$node_deb_dir/templates/systemd.service"}
log_debug "The systemd template has been set to: $template_systemd"

# Set upstart conf template
if [ -z "$template_upstart" ]; then
  template_upstart=$(jq -r '.node_deb.templates.upstart_conf' package.json)
  if [[ "$template_upstart" == 'null' ]]; then
    template_upstart=''
  fi
fi
: ${template_upstart:="$node_deb_dir/templates/upstart.conf"}
log_debug "The upstart template has been set to: $template_upstart"

# set sysv init template
if [ -z "$template_sysv" ]; then
  template_sysv=$(jq -r '.node_deb.templates.sysv_init' package.json)
  if [[ "$template_sysv" == 'null' ]]; then
    template_sysv=''
  fi
fi
: ${template_sysv:="$node_deb_dir/templates/sysv-init"}
log_debug "The System V init script template has been set to: $template_sysv"

# Set default variables (upstart) conf template
if [ -z "$template_default_variables" ]; then
  template_default_variables=$(jq -r '.node_deb.templates.default_variables' package.json)
  if [[ "$template_default_variables" == 'null' ]]; then
    template_default_variables=''
  fi
fi
: ${template_default_variables:="$node_deb_dir/templates/default"}
log_debug "The default variables file template has been set to: $template_default_variables"

# Set the daemon entrypoint
if [ -z "$daemon_entrypoint" ]; then
  daemon_entrypoint=$(jq -r '.node_deb.entrypoints.daemon' package.json)
  if [[ "$daemon_entrypoint" == 'null' ]] && [[ "$init" != 'none' ]]; then
    die 'Daemon entrypoint must be set in .node_deb.entrypoints.daemon in package.json'
  fi
fi
log_debug "The daemon entrypoint has been set to: $daemon_entrypoint"

# Set the CLI entrypoint
if [ -z "$cli_entrypoint" ]; then
  cli_entrypoint=$(jq -r '.node_deb.entrypoints.cli' package.json)
  if [[ "$cli_entrypoint" == 'null' ]]; then
    cli_entrypoint="$daemon_entrypoint"
  fi
fi
log_debug "The CLI entrypoint has been set to: $cli_entrypoint"


deb_dir="${package_name}_${package_version}_${architecture}"

finish() {
  if [ $no_delete_temp -ne 1 ]; then
    rm -rf "$deb_dir"
  fi
}

trap 'finish' EXIT

### BUILD ###

if [ -e "$deb_dir" ]; then rm -rf "$deb_dir"; fi

log_info 'Making install directories'
mkdir -p "$deb_dir/DEBIAN" \
         "$deb_dir/etc/$package_name" \
         "$deb_dir/etc/default" \
         "$deb_dir/usr/share/$package_name/app" \
         "$deb_dir/usr/share/$package_name/bin" \
         "$deb_dir/usr/bin"

if [[ "$init" == 'auto' ]] || [[ "$init" == 'upstart' ]]; then
  mkdir -p "$deb_dir/etc/init"
fi

if [[ "$init" == 'auto' ]] || [[ "$init" == 'systemd' ]]; then
  mkdir -p "$deb_dir/lib/systemd/system"
fi

if [[ "$init" == 'auto' ]] || [[ "$init" == 'sysv' ]]; then
  mkdir -p "$deb_dir/etc/init.d"
fi

escape() {
  sed -e 's/[]\/$*.^|[]/\\&/g' -e 's/&/\\&/g' <<< "$@"
}

if ! [ -z "$extra_files" ]; then
  extra_files_escape="$(escape "$extra_files")"
  declare -r extra_files_escape

  log_info "Copying extra files from '$extra_files'"
  find "$extra_files" -mindepth 1 -type d -print0 | {
    while IFS= read -r -d '' dir; do
      log_debug "Making directory: $dir"
      dir_mod=$(echo "$dir" | sed -e "s/^$extra_files_escape//g")
      mkdir -p "$deb_dir/$dir_mod"
    done
  }
  find "$extra_files" -type f -print0 | {
    while IFS= read -r -d '' file; do
      log_debug "Copying: $file"
      file_mod=$(echo "$file" | sed -e "s/^$extra_files_escape//g")
      cp -pf "$file" "$deb_dir/$file_mod"
    done
  }
fi

replace_vars() {
  : "${1:?'Template file was not defined'}"
  : "${2:?'Target file was not defined'}"
  : "${3:?'Target file permissions were not defined'}"
  declare -r file="$1"
  declare -r target_file="$2"
  declare -r permissions="$3"

  # TODO if you change this section, make sure the --list-template-variables command doesn't break
  ### BEGIN TEMPLATE_VARS ###
  sed < "$file" \
    -e "s/{{ node_deb_package_name }}/$(escape "$package_name")/g" \
    -e "s/{{ node_deb_executable_name }}/$(escape "$executable_name")/g" \
    -e "s/{{ node_deb_package_version }}/$(escape "$package_version")/g" \
    -e "s/{{ cli_entrypoint }}/$(escape "$cli_entrypoint")/g" \
    -e "s/{{ daemon_entrypoint }}/$(escape "$daemon_entrypoint")/g" \
    -e "s/{{ node_deb_package_description }}/$(escape "$package_description")/g" \
    -e "s/{{ node_deb_package_maintainer }}/$(escape "$package_maintainer")/g" \
    -e "s/{{ node_deb_package_dependencies }}/$(escape "$package_dependencies")/g" \
    -e "s/{{ node_deb_package_architecture }}/$(escape "$architecture")/g" \
    -e "s/{{ node_deb_user }}/$(escape "$user")/g" \
    -e "s/{{ node_deb_group }}/$(escape "$group")/g" \
    -e "s/{{ node_deb_init }}/$(escape "$init")/g" \
    -e "s/{{ node_deb_no_rebuild }}/$(escape "$no_rebuild")/g" \
    -e "s/{{ node_deb_version }}/$(escape "$node_deb_version")/g" \
    -e "s/{{ install_strategy }}/$(escape "$install_strategy")/g" \
  > "$target_file"
  ### END TEMPLATE_VARS ###
  chmod "$permissions" "$target_file"
}

log_info 'Rendering templates'
replace_vars "$template_control" "$deb_dir/DEBIAN/control" '0644'
replace_vars "$template_postinst" "$deb_dir/DEBIAN/postinst" '0755'
replace_vars "$template_postrm" "$deb_dir/DEBIAN/postrm" '0755'
replace_vars "$template_prerm" "$deb_dir/DEBIAN/prerm" '0755'
replace_vars "$template_executable" "$deb_dir/usr/share/$package_name/bin/$executable_name" '0755'
replace_vars "$template_default_variables" "$deb_dir/etc/default/$package_name" '0644'

if [ "$init" == 'auto' ] || [ "$init" == 'upstart' ]; then
  replace_vars "$template_upstart" "$deb_dir/etc/init/$package_name.conf" '0644'
fi

if [ "$init" == 'auto' ] || [ "$init" == 'systemd' ]; then
  replace_vars "$template_systemd" "$deb_dir/lib/systemd/system/$package_name.service" '0644'
fi

if [ "$init" == 'auto' ] || [ "$init" == 'sysv' ]; then
  replace_vars "$template_sysv" "$deb_dir/etc/init.d/$package_name" '0755'
fi

# Create conffiles for dpkg, add all files in etc
find "$deb_dir/etc" -type f | sed "s/^$(escape "$deb_dir")//" > "$deb_dir/DEBIAN/conffiles"

log_debug 'Templates rendered successfully'

ln -sf "../share/$package_name/bin/$executable_name" "$deb_dir/usr/bin/$executable_name"

# Copy all files into temporary Debian dir
log_info 'Recursively copying files into Debian directory'
if echo "$@" | grep -q 'node_modules'; then
  log_warn "Since node-deb v0.8.0, 'node_modules' cannot be included on the command line. Use the option" \
    "'--install-strategy' to pick if/how it is included. Inclusion of 'node_modules' will" \
    'result in an error in future releases.'
fi

if echo "$@" | grep -q 'package.json'; then
  log_warn "Since node-deb v0.6.0, 'package.json' is automatically included and should not be specified on the" \
    'command line.'
fi

if echo "$@" | grep -q 'npm-shrinkwrap.json'; then
  log_warn "Since node-deb v0.6.0, 'npm-shrinkwrap.json' is automatically included and should not be specified on the" \
    'command line.'
fi

if echo "$@" | grep -q 'package-lock.json'; then
  log_warn "Since node-deb v0.10.0, 'package-lock.json' is automatically included and should not be specified on the" \
    'command line.'
fi

# copy the main files
find "$@" -type d -print0 | {
  while IFS= read -r -d '' dir; do
    if ! readlink_f "$dir" | grep -Eq "^$source_dir/node_modules/.*"; then
      log_debug "Making directory: $dir"
      mkdir -p "$deb_dir/usr/share/$package_name/app/$dir"
    fi
  done
}
find "$@" -type f -print0 | {
  while IFS= read -r -d '' file; do
    if ! readlink_f "$file" | grep -Eq "$source_dir/node_modules/.*"; then
      log_debug "Copying: $file"
      cp -pf  "$file" "$deb_dir/usr/share/$package_name/app/$file"
    fi
  done
}

copy_node_modules() {
  if [ -d "$source_dir/node_modules" ]; then
    log_debug 'Copying dir: node_modules'
    cp -rfL "$source_dir/node_modules/" "$deb_dir/usr/share/$package_name/app/"
  fi
}

if [[ "$install_strategy" == 'auto' ]]; then
  if [ -d "$source_dir/node_modules" ]; then
    if hash npm 2> /dev/null; then
      mkdir -p "$deb_dir/usr/share/$package_name/app/node_modules"

      # this is a bash-y mess, but basically the last sed-cut-sort strips out nested node_modules in dependencies
      for dir in $(npm ls --parseable --prod | sed -e "s/$(escape "$source_dir")//g" | grep -Ev '^$' | sed -e 's:^/node_modules/::g' | cut -d / -f 1 | sort -u); do
        log_debug "Copying dependency: $dir"
        cp -rf "$source_dir/node_modules/$dir" "$deb_dir/usr/share/$package_name/app/node_modules/"
      done

      if [ -f "$source_dir/node_modules/.bin" ]; then
        cp -rfL "$source_dir/node_modules/.bin/" "$deb_dir/usr/share/$package_name/app/node_modules/.bin/"
        # only keep the links that still point to something
        for e in $(find "$deb_dir/usr/share/$package_name/app/node_modules/.bin/"); do
          stat "$(readlink_f "$e")" || rm -rf "$e"
        done
      fi
    else
      copy_node_modules
    fi
  fi
fi

if [[ "$install_strategy" == 'copy' ]]; then
  copy_node_modules
fi

if ! [ -f "$deb_dir/usr/share/$package_name/app/package.json" ]; then
  log_info "Including 'package.json' in the Debian package."
  cp -pf './package.json' "$deb_dir/usr/share/$package_name/app/"
fi

if [ -f './package-lock.json' ] && ! [ -f "$deb_dir/usr/share/$package_name/app/package-lock.json" ]; then
  log_info "Including 'package-lock.json' in the Debian package."
  cp -pf './package-lock.json' "$deb_dir/usr/share/$package_name/app/"
fi

if [ -f './npm-shrinkwrap.json' ] && ! [ -f "$deb_dir/usr/share/$package_name/app/npm-shrinkwrap.json" ]; then
  log_info "Including 'npm-shrinkwrap.json' in the Debian package."
  cp -pf './npm-shrinkwrap.json' "$deb_dir/usr/share/$package_name/app/"
fi

# Calculate md5sums
log_debug 'Calculating md5 sums'
# Debian/Ubuntu
if hash md5sum 2>/dev/null; then
  find "$deb_dir" -path "$deb_dir/DEBIAN" -prune -o -type f -print0 | xargs -0 md5sum >> "$deb_dir/DEBIAN/md5sums"
# OSX
elif hash md5 2>/dev/null; then
  find "$deb_dir" -path "$deb_dir/DEBIAN" -prune -o -type f -print0 | {
    while IFS= read -r -d '' file; do
      echo "$(md5 -q "$file") $file" >> "$deb_dir/DEBIAN/md5sums"
    done
  }
# OSX with `brew install gmd5sum`
elif hash gmd5sum 2>/dev/null; then
  find "$deb_dir" -path "$deb_dir/DEBIAN" -prune -o -type f -print0 | xargs -0 gmd5sum >> "$deb_dir/DEBIAN/md5sums"
# Oh dear...
else
  log_warn 'Unable to find suitable md5 sum program. No md5sums calculated.'
fi

# strip the build dirname from the Debian package
# and then strip the leading slash again
if [ "$OS" == "Linux" ]; then
    sed -i "s/$(escape "$deb_dir")//" "$deb_dir/DEBIAN/md5sums"
    sed -i s:/:: "$deb_dir/DEBIAN/md5sums"
else
    sed -i '' "s/$(escape "$deb_dir")//" "$deb_dir/DEBIAN/md5sums"
    sed -i '' s:/:: "$deb_dir/DEBIAN/md5sums"
fi

log_info 'Building Debian package'
if [[ -z "$output_deb_name" ]]; then
  fakeroot dpkg-deb --build "$deb_dir" > '/dev/null'
else
  fakeroot dpkg-deb --build "$deb_dir" "$output_deb_name" > '/dev/null'
fi
log_info 'Debian package built.'
exit 0
