#!/bin/bash
# In the current directory:
# * start a documentation repository or
# * update the existing documentation boilerplate.
# The initial setup asks a few questions (which type of document, which
# variant). Mere updates are usually completely automatic.
# Usage:
# $ $0          # Set up/update a documentation repository
# $ $0 --help   # This screen

# project version number
version=0.1

repo="https://github.com/sknorr/doc-kit/raw"
branch="master"
indexfile="KITS"
fileextension=".MANIFEST"
config="doc-kit.conf"


charclass_variants="[-_/a-zA-Z0-9]+"
charclass_files="[-_./a-zA-Z0-9]+"

function geturl() {
  # $1 = file name
  wget -qO - --no-check-certificate "${repo}/${branch}/$1"
}

function saveurl() {
  # $1 = file name at origin
  # $2 = file name to save under
  echo "Saving $1 to $2..."
  if [[ $(echo "$2" | grep -o '/') ]]; then
    dir=$(dirname "$2")
    if [[ -e "$dir" ]] && [[ ! -d "$dir" ]]; then
      echo "$dir exists already but is a file. $dir needs to be a directory."
      exit 1
    fi
    [[ -d "$dir" ]] || mkdir -p "$dir"
  fi
  wget -q --no-check-certificate -O "$2" "${repo}/${branch}/$1"
}

function shortenattop() {
  # $1 - text to cut the first line off of
  echo -e "$1" | tail -$(($(echo "$1" | wc -l) - 1))
}

function sanitizevariants() {
  # $1 - variants string
  echo -e "$1" | sort -u | tr '\n' ' ' | sed -r 's/ $/\n/'
}

function subsetof() {
  # $1 original set (separated by newlines)
  # $2 potential subset (separated by newlines)
  # $3 allow empty subset: 0 - no (default), 1 - yes
  original=$(echo -e "$1" | sort -u)
  potential=$(echo -e "$2" | sort -u)
  emptysubset=0
  [[ ! "$potential" ]] && emptysubset=1
  while [[ "$potential" ]]; do
    local line=$(echo "$potential" | head -1)
    [[ $(echo -e "$original" | grep "^$line$") ]] || break
    potential=$(shortenattop "$potential")
  done
  if [[ ! "$potential" ]] && [[ $3 -eq 1 ]]; then
    echo "is_subset"
  elif [[ ! "$potential" ]] && [[ $emptysubset -eq 0 ]]; then
    echo "is_subset"
  fi
}

function decide() {
  # $1 = prompt
  # $2 = valid answers, separated by newlines
  # $3 = allow multiple choice: 0 - no (default), 1 - yes
  # $4 = allow empty answer: 0 - no (default), 1 - yes
  allowempty=0
  [[ "$4" -eq 1 ]] && allowempty=1
  decision="&INITIALNONSENSEVALUE;"
  while [[ ! $(subsetof "$2" "$decision" "$allowempty") ]]; do
    echo -n "$1"
    [[ $3 -eq 1 ]] && echo -n " (separate multiple values with spaces)"
    echo -n ": "
    read decision
    [[ ! $3 -eq 1 ]] && decision=$(echo "$decision" | sed -r 's/ /#@#/g')
    decision=$(echo "$decision" | sed -r 's/[^-_+.a-z0-9 ]//g' | tr ' ' '\n')
  done
}

function preparemanifest() {
  # $1 manifest
  echo -e "$1" | tr '\t' ' ' | sed -r \
    -e 's/^ +//' \
    -e 's/ +$//' \
    -e 's/^#.*$//' \
    | sed -n '/./ p'
}

function validatemanifest() {
  # $1 = manifest
  # $2 = success message: 0 - hide (default), 1 - show
  # $3 = error mode: 0 - continue on error, 1 - quit on error (default)
  if  [[ ! $(echo -e "$1" | sed -n '/./ p') ]]; then
    echo -e "Manifest file empty."
    exit 1
  fi
  validatedmanifest=$(preparemanifest "$1" | sed -r \
    -e "s/^(initial|always)(\(${charclass_variants}( +${charclass_variants})*\))?: +${charclass_files}( +-> +${charclass_files})?\$//" \
    | sed -n '/./ p')

  if [[ "$validatedmanifest" ]]; then
    echo -e "Manifest validation failed. Failing lines:\n"
    echo -e "$validatedmanifest"
    [[ ! $3 -eq 0 ]] && exit 1
  elif [[ "$2" -eq 1 ]]; then
    echo "Manifest validation successful."
  fi
}


if [[ $1 == '--help' ]] || [[ $1 == '-h' ]]; then
  sed -rn '/#!/{n; p; :loop n; p; /^[ \t]*$/q; b loop}' $0 | sed -r -e 's/^# ?//' -e "s/\\\$0/$(basename $0)/"
  exit
fi

if [[ $1 == '--version' ]] || [[ $1 == '-v' ]]; then
  echo "$(basename $0) $version"
fi

configtype=
configvariants=
manifest=
initial=0
if [[ -f "$config" ]]; then # FIXME: validate config
  configtype=$(sed -r -n 's/^ *type: *// p' "$config")
  configvariants=$(sanitizevariants $(sed -r -n 's/^ *variant: *// p' "$config"))
  manifest=$(geturl "${configtype}${fileextension}")
else
  echo "No $config file found: Running initial setup."
  manifestindex=$(wget -qO - --no-check-certificate "${repo}/${branch}/${indexfile}" | sort -u)
  echo "Available document types:"
  echo "$manifestindex" | tr '\n' ' ' | sed -r 's/ $/\n/'
  echo ""
  decide "Specify the desired document type" "$manifestindex" 0 0
  configtype="$decision"
  echo ""
  manifest=$(geturl "${configtype}${fileextension}")
  validatemanifest "$manifest"
  availablevariants=$(preparemanifest "$manifest" | sed -r \
    -e 's/\)?:.+$//' \
    -e 's/^[^\(]+\(?//' \
    | tr ' ' '\n' | sed -n '/./ p' | sort -u)
  if [[ $availablevariants != '' ]]; then
    echo "Available variants:"
    echo "$availablevariants" | tr '\n' ' ' | sed -r 's/ $/\n/'
    echo ""
    decide "Specify the desired variant(s)" "$availablevariants" 1 1
    configvariants=$(sanitizevariants "$decision")
  else
    echo "This document type has no variants."
    configvariants=''
  fi
  echo -e "type: $configtype\nvariant: $configvariants" > "$config"
  echo "Wrote $config file into current directory. Make sure to commit."
  initial=1
fi

validatemanifest "$manifest"

cleanmanifest=$(preparemanifest "$manifest")
while [[ "$cleanmanifest" ]]; do
  line=$(echo -e "$cleanmanifest" | head -1)

  type=$(echo "$line" | grep -oP '^(initial|always)')
  variants=$(sanitizevariants $(echo "$line" | grep -oP '^[^:]+:' | sed -r -e "s/(^${charclass_variants}\(?|\)?:\$)//g"))
  from=$(echo "$line" | sed -r 's/^[^:]+: +//' | grep -oP "^${charclass_files}")
  to=$(echo "$line" | grep -oP "${charclass_files}+\$")

  if [[ "$type" = "always" ]]; then
    [[ $(subsetof "$configvariants" "$variants" 1) ]] && saveurl "$configtype/$from" "$to"
  elif [[ $initial = 1 ]] && [[ "$type" = "initial" ]]; then
    [[ $(subsetof "$configvariants" "$variants" 1) ]] && saveurl "$configtype/$from" "$to"
  fi

  cleanmanifest=$(shortenattop "$cleanmanifest")
done
