#!/bin/zsh
# vim: sts=2 sw=2 et
setopt no_unset warn_create_global
zmodload zsh/system || exit $?

typeset -g scandir=/run/service
typeset -g build=/run/urxvt.named.service

main() {
  local container name arg cmd sock s
  local -a quoted
  container=$1
  [[ $container == */* ]] && exit 100
  name=$2
  [[ $name == */* ]] && exit 100
  shift 2 || exit 100
  sock=/run/inbox/$container/run/exec/exec 
  s=urxvt.for-$container.named-$name

  - sv_start $s
  - sv_env EXEC_PATH $sock
  - sv_env TERM_NAME ${container//./:}

  for arg in "$@"; do
          quoted+=( "$(s6-quote -- "$arg")" )
  done
  local -a el_cmd=(
    's6-envdir env'
    'multisubstitute {'
    '  importas -i -u EXEC_PATH EXEC_PATH'
    '  importas -i -u TERM_NAME TERM_NAME'
    '}'
    '/mnt/ns/bin/socketpair 3 1'
    'background -d {'
    '  fdclose 9'
    '  fdclose 3'
    '  s6-sudo $EXEC_PATH'
    '  /mnt/ns/bin/spawn-pty rxvt-unicode-256color {'
    "    ${(j: :)quoted}"
    '  } /mnt/ns/bin/fdsend 1 0 true'
    '}'
    'fdmove -c 1 2'
    '/mnt/ns/bin/fdrecvto 3 4'
    'fdclose 3'
    'backtick -E PTS_NAME { /mnt/ns/bin/ptsname 4 }'
    'if { fdmove 1 9 printf %s\n ${TERM_NAME}:${PTS_NAME} }'
    'fdclose 9'
    'urxvt -name ${TERM_NAME}:${PTS_NAME} -pty-fd 4'
  )
  - sv_el_script run $el_cmd
  - sv_notification_fd 9
  - sv_end

  s6-sudo /run/cmd.s link $container xsession.${DISPLAY#:}.$USER run/exec/exec ||
    die "Failed to link ${(qqq)container} exec socket."

  - s6-svc -wU -T 5000 -o $scandir/$s
}

typeset -f -t main

### Color and text definition {{{1

typeset -g hl_fatal hl_reset

# die messages
if (( $terminfo[colors] >= 8 )); then
        hl_fatal='%F{red}%B'; hl_fatal=${(%)hl_fatal}
        hl_warn='%F{yellow}%B'; hl_warn=${(%)hl_warn}
        hl_reset='%b%f'; hl_reset=${(%)hl_reset}
fi

### Utility functions {{{1
err_msg() {
  local first=$1
  shift
  printf >&2 '%s%s%s%s\n' "$hl_fatal" "$first" "$hl_reset" "$*"
}

warn_msg() {
  local first=$1
  shift
  printf >&2 '%s%s%s%s\n' "$hl_warn" "$first" "$hl_reset" "$*"
}

# helper that prints out stack, error message and exits
die_ret() {
  set +x
  local ret n
  ret=$1
  shift
  print -r - >&2 "${hl_fatal}Fatal$hl_reset error occurend in:"
  for n in {${#funcfiletrace}..1}; do
    printf >&2 '%d> %s (%s)\n' $n "$funcfiletrace[$n]" "$functrace[$n]"
  done
  printf >&2 '%s\n' "${hl_fatal}*$hl_reset $^@"
  exit $ret
}

die() {
  set +x
  die_ret 1 "$@"
}
die100() {  # 100: wrong usage
  set +x
  die_ret 100 "$@"
}
die111() {  # 111: system call failed
  set +x
  die_ret 111 "$@"
}

-() {  # Run command and die on nonzero exitcode
  "$@" || die_ret $? "command failed with exitcode $?: ${(j: :)${(q)@}}"
}

# service builder

sv_start() {
  (($+sv_name)) && \
    die100 "sv_start: previous service definition ${(qqq)sv_name} wasn't ended properly"
  (( $# != 1 )) && die100 "sv_start: incorrect arguments"
  [[ $1 == */* ]] && die100 "sv_start: incorrect argument"

  typeset -g sv_name=$1
  typeset -g sv_dir=$build/$1
  - mkdir -p $sv_dir
  - touch $sv_dir/down
}

sv_end() {
  (($+sv_name)) || die100 "$0: no service definition started"
  (($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
  (( $# != 0 )) && die100 "$0: incorrect arguments"

  if ! [[ -L $scandir/$sv_name ]]; then
    - s6-svlink -t 3000 $scandir $sv_dir
    # - s6-mkfifodir $sv_dir/event
    # - ln -s -T $sv_dir $scandir/$sv_name
    # new_services+=( $scandir/$sv_name )
  fi
  unset sv_name sv_dir
}

sv_link_command() {
  (($+sv_name)) || die100 "$0: no service definition started"
  (($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
  (( $# != 2 )) && die100 "$0: incorrect arguments"

  local script_path=$sv_dir/$1
  - ln -s -f -T ${commands[$2]} $script_path
}

sv_write_lines() {
  (($+sv_name)) || die100 "$0: no service definition started"
  (($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
  (( $# < 2 )) && die100 "$0: incorrect arguments"

  local file_path=$sv_dir/$1
  shift
  printf '%s\n' >$file_path "$@" || \
    die111 "Error writing to ${(qqq)file_path}"
}

sv_el_script() {
  (($+sv_name)) || die100 "$0: no service definition started"
  (($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"

  - sv_write_lines $1 '#!/bin/execlineb -P' "$@[2,-1]"
  - chmod +x $sv_dir/$1
}

sv_notification_fd() {
  (($+sv_name)) || die100 "$0: no service definition started"
  (($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
  (( $# != 1 )) && die100 "$0: incorrect arguments"

  - sv_write_lines notification-fd $1
}

sv_env() {
  (($+sv_name)) || die100 "$0: no service definition started"
  (($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
  (( $# < 2 )) && die100 "$0: incorrect arguments"

  - mkdir -p $sv_dir/env
  local k v
  for k v in "$@"; do
    [[ $k == */* ]] && die100 "$0: invalid env variable ${(qqq)k}"
    - sv_write_lines env/$k $v
  done
}

sv_mkdir() {
  (($+sv_name)) || die100 "$0: no service definition started"
  (($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
  (( $# < 1 )) && die100 "$0: incorrect arguments"

  - mkdir -p $sv_dir/${^@}
}

# end service builder



main "$@"