#!/usr/bin/env bash

# Wait for changes to Sass, and recompile.
# Invoke from repo root as `npm run watch-sass`.

# By default, only watches default Sass.
# To watch themes too, provide colon-separated paths in the COMPREHENSIVE_THEME_DIRS environment variable.
# Each path will be treated as a "theme dir", which means that every immediate child directory is watchable as a theme.
# For example:
#
#   COMPREHENSIVE_THEME_DIRS=/openedx/themes:./themes npm run watch-sass
#
# would watch default Sass as well as /openedx/themes/indigo, /openedx/themes/mytheme, ./themes/red-theme, etc.

set -euo pipefail

COL_SECTION="\e[1;36m"  # Section header color (bold cyan)
COL_LOG="\e[36m"        # Log color (cyan)
COL_WARNING="\e[1;33m"  # Warning (bold yellow)
COL_ERROR="\e[1;31m"    # Error (bold red)
COL_CMD="\e[35m"        # Command echoing (magenta)
COL_OFF="\e[0m"         # Normal color

section() {
    # Print a header in bold cyan to indicate sections of output.
    echo -e "${COL_SECTION}$*${COL_OFF}"
}
log() {
    # Info line. Indented by one space so that it appears as nested under section headers.
    echo -e " ${COL_LOG}$*${COL_OFF}"
}
warning() {
    # Bright yellow warning message.
    echo -e "${COL_WARNING}WARNING: $*${COL_OFF}"
}
error() {
    # Bright red error message.
    echo -e "${COL_ERROR}ERROR: $*${COL_OFF}"
}
echo_quoted_cmd() {
    # Echo args, each single-quoted, so that the user could copy-paste and run them as a command.
    # Indented by two spaces so it appears as nested under log lines.
    echo -e "  ${COL_CMD}$(printf "'%s' " "$@")${COL_OFF}"
}

start_sass_watch() {
    # Start a watch for .scss files in a particular dir. Run in the background.
    # Usage: start_sass_watch NAME_FOR_LOGGING HANDLER_COMMAND WATCH_ROOT_PATHS...
    local name="$1"
    local handler="$2"
    shift 2
    local paths=("$@")
    section "Starting Sass watchers for $name:"
    # Note: --drop means that we should ignore any change events that happen during recompilation.
    #       This is good because it means that if you edit 3 files, we won't fire off three simultaneous compiles.
    #       It's not perfect, though, because if you change 3 files, only the first one will trigger a recompile,
    #       so depending on the timing, your latest changes may or may not be picked up. We accept this as a reasonable
    #       tradeoff. Some watcher libraries are smarter than watchdog, in that they wait until the filesystem "settles"
    #       before firing off a the recompilation. This sounds nice but did not seem worth the effort for legacy assets.
    local watch_cmd=(watchmedo shell-command -v --patterns '*.scss' --recursive --drop --command "$handler" "${paths[@]}")
    echo_quoted_cmd "${watch_cmd[@]}"
    "${watch_cmd[@]}" &
}

# Verify execution environment.
if [[ ! -d common/static/sass ]] ; then
    error 'This command must be run from the root of edx-platform!'
    exit 1
fi
if ! type watchmedo 1>/dev/null 2>&1 ; then
    error "command not found: watchmedo"
    log "The \`watchdog\` Python package is probably not installed. You can install it with:"
    log "  pip install -r requirements/edx/development.txt"
    exit 1
fi

# Reliably kill all child processes when script is interrupted (Ctrl+C) or otherwise terminated.
trap "exit" INT TERM
trap "kill 0" EXIT

# Watch default Sass.
# If it changes, then recompile the default theme *and* all custom themes,
# as custom themes' Sass is based on the default Sass.
start_sass_watch "default theme" \
    'npm run compile-sass-dev' \
    lms/static/sass \
    lms/static/certificates/sass \
    cms/static/sass \
    common/static \
    node_modules \
    xmodule/assets

# Watch each theme's Sass.
# If it changes, only recompile that theme.
export IFS=":"
for theme_dir in ${COMPREHENSIVE_THEME_DIRS:-} ; do
    for theme_path in "$theme_dir"/* ; do
        theme_name="${theme_path#"$theme_dir/"}"
        lms_sass="$theme_path/lms/static/sass"
        cert_sass="$theme_path/lms/static/certificates/sass"
        cms_sass="$theme_path/cms/static/sass"
        theme_watch_dirs=()
        if [[ -d "$lms_sass" ]] ; then
            theme_watch_dirs+=("$lms_sass")
        fi
        if [[ -d "$cert_sass" ]] ; then
            theme_watch_dirs+=("$cert_sass")
        fi
        if [[ -d "$cms_sass" ]] ; then
            theme_watch_dirs+=("$cms_sass")
        fi
        # A directory is a theme if it as LMS Sass *and/or* CMS Sass *and/or* certificate Sass.
        if [[ -n "${theme_watch_dirs[*]}" ]] ; then
            start_sass_watch "theme '$theme_name'" \
                "npm run compile-sass-dev -- -T $theme_dir -t $theme_name --skip-default" \
                "${theme_watch_dirs[@]}"
        fi
    done
done

# Wait until interrupted/terminated.
sleep infinity &
echo
echo "Watching Open edX LMS & CMS Sass for changes."
echo "Use Ctrl+c to exit."
echo
echo
wait $!

