#!/usr/bin/env bash

# HOWTO Documentation on this file is documented in %PEP_HOME%/docs/code/bash/md2mdbook_sh.md

### Prerequisites: ###
# Install Rust if not available using the next line:
# curl https://sh.rustup.rs -sSf | sh
# Install mdbook:
# cargo install mdbook
# Install mdbook-pdf
# cargo install mdbook-pdf
# Install mdbooko-mermaid (generates graphical diagrams in the mdbook output)
# cargo install mdbook-mermaid

# Directory paths:
MDSRCPATH="./mdsrc" # THe path where the called stored the (source) md files.
MDBOOKSRCPATH="./src" # The path where mdbook reads the md input, where all summary files are added (recursively)
MDBOOKTHEMEPATH=0
MDBOOK_TITLE="mdbook generated by md2mdbook.sh"

# Flags that concern bash execution:
DIE_ON_WARN=0
GEN_PDF=0
VERBOSE=0
RENEW_SUMMARY=0

# Settings for the SUMMARY.md output:
SHOW_FULL_FILE_PATHS=0
SHOW_FULL_DIR_PATHS=0

# When a summary.md file is present, replace that filename with this symbol in the index/overview:
SUB_SUMMARY_SYMBOL="📖" #"SUMMARY.md"
SUBDIR_SYMBOL="📁"

# Some colorful output macro's to separate status msg from the rest.
function console_update { LINEEND="${2:-\n}"; MSG_COLOR="${3:-0;102m}"; printf "\033[%s%s\033[0m%b" "$MSG_COLOR" "$1" "$LINEEND"; }
function console_alert { console_update "$1" "$2" "0;41m"; }
function verbose { [[ "$VERBOSE" == "1" ]] && echo "$1"; }

function elvis_preskey { console_update "Press [Enter] to continue or [ctrl]-c to abort" "."; read -r -p ".."; }
function warning_halt {   
  [[ "$DIE_ON_WARN" == "1" ]] && exit ## Exit application if $DIE_ON_WARN flag is 1. 
  elvis_preskey
}

##########################################################################################
############################ Bash script argument parsing ################################
##########################################################################################
function print_cmd_usage { # cli overview of parameters
  echo "Usage: md2mdbook.sh [--dieOnWarning/-W] [--mdSourcePath/-s <path>] [--help/-h] [--verbose/-v] etc...
  
  Switches: 
    --dieOnWarning/-W               Kill the script upon instead of waiting for keystrokes upon warnings. Use this when running on pipelines i.e..
    --mdSourcePath/-s <path>        Instead of using the scripts default './mdsrc', use the given path.
    --genPdf/-P                     Also generate PDF output (default is only HTML). Note that this will introduce subdirectories in './book'.
    --verbose/-v                    Display more detailed information in the output.
    --fullFilePaths/-F              In the mdbook SUMMARY.md show the full path to files.
    --fullDirPaths/-D               In the mdbook SUMMARY.md show the full path to subdirs.
    --help/-h                       This parameter overview.
    --renewSummary/-S               Removes an existing SUMMARY.md in the mdsrc root and generate a new one. If now, an existing SUMMARY.md will be used.
    --theme/-t                      Use (copy) mdbook theme from this path.
    --title/-T                      A title to give to the mdbook
    .";
}
# Substitute long options to short ones (thnx https://stackoverflow.com/a/30026641/3906500 )
for arg in "$@"; do
  shift
  case "$arg" in
    '--dieOnWarning')     set -- "$@" '-W'   ;;
    '--fullFilePaths')    set -- "$@" '-F'   ;;
    '--fullDirPaths')     set -- "$@" '-D'   ;;
    '--mdSourcePath')     set -- "$@" '-s'   ;;
    '--verbose')          set -- "$@" '-v'   ;;
    '--genPdf')           set -- "$@" '-P'   ;;
    '--renewSummary')     set -- "$@" '-S'   ;;
    '--help')             set -- "$@" '-h'   ;;
    '--theme')            set -- "$@" '-t'   ;;
    '--title')            set -- "$@" '-T'   ;;
    *)                    set -- "$@" "$arg" ;;
  esac
done
while getopts 'WhPFDvSs:t:T:' flag; do
  case "${flag}" in
    s)  # MarkDown SOURCE PATH, remove trailing slashes
            # shellcheck disable=SC2001
            MDSRCPATH=$(echo "$OPTARG" | sed 's:/*$::')
            ;;
    t)  # mdbook theme path, remove trailing slashes
            # shellcheck disable=SC2001
            MDBOOKTHEMEPATH_REL=$(echo "$OPTARG" | sed 's:/*$::')
            MDBOOKTHEMEPATH=$(cd "$MDBOOKTHEMEPATH_REL" && pwd)
            ;;
    T)  # mdbook title
            MDBOOK_TITLE="$OPTARG"
            ;;
    S)  # Re-generate a SUMMARY.md if already exists in mdsrc root.
            RENEW_SUMMARY=1
            ;;
    F)  #   In the mdbook SUMMARY.md show the full path to files.
            SHOW_FULL_FILE_PATHS=1
            ;;
    D)  #   In the mdbook SUMMARY.md show the full path to subdirs.
            SHOW_FULL_DIR_PATHS=1
            ;;
    v)  # set verbose
            VERBOSE=1
            ;;
    W)  # set dieOnWarning
            DIE_ON_WARN=1
            ;;
    P)  # set dieOnWarning
            GEN_PDF=1
            ;;
    h) # show help message
            print_cmd_usage
            exit 2
            ;;
    ?)
            console_alert "Unknown parameter in $*."
            print_cmd_usage
            exit 2
            ;;
  esac
done # getopts
############################ End of Bash script argument parsing ################################

# Check if directory with the md source files exists:
if [ ! -d "$MDSRCPATH" ]; then
  console_alert "MD Source directory '$MDSRCPATH' does not exist."; warning_halt
fi

# Function for checking if all required binaries are installed:
function check_binary {
  if ! [ -x "$(command -v "$1")" ]; then
    console_alert "Command $1 not found. Please install using \" $2 \""; warning_halt
  fi
}


# Checking rust:
check_binary "cargo" "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"

# Checking mdbook:
check_binary "mdbook" "cargo install mdbook"

# Checking mdbook-pdf:
check_binary mdbook-pdf "cargo install mdbook-pdf"

# Checking mdbook-mermaid
check_binary mdbook-mermaid "cargo install mdbook-mermaid"

# Add rust executables to path (effectively add ~/.cargo/bin to $PATH):
# shellcheck disable=SC1091
source "$HOME/.cargo/env"


### Starting preparations on generating the ./src dir to run mdbook on:
POPULATE_NEW_BOOK_SRC_DIR=0
COPYSOURCES=0
# Check if 'src' dir (the input for mdbook) exists
if [ ! -d "$MDBOOKSRCPATH" ]; then
  POPULATE_NEW_BOOK_SRC_DIR=1 # When src does not yet exist, set POPULATE_NEW_BOOK_SRC_DIR = 1.
  else 
    console_alert "./src/ directory already exists. To avoid overwriting files or corrupting SUMMARY.md files, this directory will not be modified. If you want a new src directory to be created and populated, remove the old ./src/ dir first."
fi

### Ensuring book.toml and a /src/ dir exists or is created.
if [ ! -f "book.toml" ] ; then
  # Check if SUMMARY.md exists (and backup if so), dito chapter_1.md (without the backup feature)
  if [ -f "$MDBOOKSRCPATH/SUMMARY.md" ]; then
    console_alert "WARNING: a SUMMARY.md already exists in ./src/. This will be regenerated. A backup is created at './src/SUMMARY.md.bak'. I a previous backup already existed, that will be overwritten."; warning_halt
    mv "$MDBOOKSRCPATH/SUMMARY.md" "$MDBOOKSRCPATH/SUMMARY.md.bak"
  fi 
  if [ -f "$MDBOOKSRCPATH/chapter_1.md" ]; then
    console_alert "WARNING: chapter_1.md exists in ./src/. This file will be deleted without backup. Are you okay with that?"; warning_halt
  fi 

  console_update "book.toml does not yet exist, created with 'mdbook init'"
  ################## Create book.toml and src dir ########################
  mdbook init --title "$MDBOOK_TITLE" --force # skip asking for title and .gitignore

  ## Copy theme if supplied:
  if [[ "$MDBOOKTHEMEPATH" != 0 ]]; then
    # Check if supplied theme directory exists:
    if [ -d "$MDSRCPATH" ]; then
      console_update "Copying theme from $MDBOOKTHEMEPATH to $(pwd)/theme.";
      cp -a "$MDBOOKTHEMEPATH" "./theme"
    else
      console_alert "mdbook theme path '$MDBOOKTHEMEPATH' does not exist."; warning_halt
    fi
  fi
  

  # Start with a clean summary. Ignore 'chapter_1.md' that is generated by default by mdbook.
  if [ $POPULATE_NEW_BOOK_SRC_DIR -eq 1 ]; then
    # First do cleanup of unwanted created files. 
    if  [ -f "$MDBOOKSRCPATH/SUMMARY.md" ]; then
      echo "Removing default SUMMARY.md created by 'mdbook init' since we want to create our own."
      rm "$MDBOOKSRCPATH/SUMMARY.md"
      rm "$MDBOOKSRCPATH/chapter_1.md" 
    fi

    COPYSOURCES=1
  fi
else # If book.toml DOES already exist ...
  if [ $POPULATE_NEW_BOOK_SRC_DIR -eq 1 ]; then # ... but ./src/ does not/no longer
    # Creating new src dir in cwd.
    mkdir "$MDBOOKSRCPATH"
    COPYSOURCES=1
  fi
  # if book.toml already exists and so does the src dir for mdbook: COPYSOURCE==0 (by default definition)
fi
# >> book.toml created, source directories assessed (but not processed)


### Copy md sources from mdsrc path to mdbook src dir:
if [ $COPYSOURCES -eq 1 ]; then     # Copy source md files to mdbook defined ./src 'working dir' where absent SUMMARY.md's are generated.
  console_update "Making a copy of $MDSRCPATH to $MDBOOKSRCPATH (where a SUMMARY.md may be generated)"
  cp -R --preserve=all -L "$MDSRCPATH"/* "$MDBOOKSRCPATH"
fi


if [ $POPULATE_NEW_BOOK_SRC_DIR -eq 1 ] && [ -f "$MDBOOKSRCPATH/SUMMARY.md" ]; then
  if [ $RENEW_SUMMARY -eq 1 ]; then
    console_alert "warning: $MDBOOKSRCPATH/SUMMARY.md exists (copied from $MDSRCPATH/SUMMARY.md), but will be rewritten to avoid being appended."
    rm "$MDBOOKSRCPATH/SUMMARY.md"
  else
    console_update "Not generating new SUMMARY.md since one already present. For regeneration add the --renewSummary flag."
  fi
fi

### Looping through all md files in the tree in two runs: list .md files first and subdirectories and their contents second.
if [[ ! -f "$MDBOOKSRCPATH/SUMMARY.md" || ($POPULATE_NEW_BOOK_SRC_DIR -eq 1 && $RENEW_SUMMARY -eq 1 ) ]]; then
  # recursively traverse directories for .md files, except when a directory has a SUMMARY.md, then just use that one (subfolder responsibility)
  function mdbook_collect_sources_from_path() {
      md_files_array=()

      # FIRST RUN: FILES
      for file in "$1"/*; do
          if [ -d "$file" ]; then
            if [ -e "$file/SUMMARY.md" ]; then
              verbose "ignoring $file/SUMMARY.md in first run"
            else
              verbose "ignoring directory $file in first run"
            fi
          elif [ "${file##*.}" = "md" ]; then
              #echo "NORMAL FILE" "$file"
              md_files_array+=("$file")
          fi
      done
      # SECOND RUN: DIRECTORIES
      for file in "$1"/*; do
          if [ -d "$file" ]; then
              # Check if SUMMARY.md exists in the current directory
              if [ -e "$file/SUMMARY.md" ]; then
                  #echo "SUMMARY:" "$file/SUMMARY.md"
                  md_files_array+=("$file/SUMMARY.md")
              else
                  # Recursive call for subdirectories
                  md_files_array+=("$file/.")
                  IFS=$'\n' md_files_array+=("$(mdbook_collect_sources_from_path "$file")")
              fi
          elif [ "${file##*.}" = "md" ]; then
            verbose "ignore regular file $file in second run"
          fi
      done
      # return array, one item per line.
      (IFS=$'\n'; echo "${md_files_array[*]}")
  }

  # Calling recursive function above, fill array $RESULTS one item per line:
  readarray -t -d $'\n' RESULTS < <(mdbook_collect_sources_from_path "$MDBOOKSRCPATH")
  # Function to generate a string of spaces
  generate_spaces() { local length=$(( ($1 - 1) * 4 )); printf "%${length}s" ""; }

  # FIRST RUN: IGNORE DIRECTORIES
  for MDFILENAME in "${RESULTS[@]}"; do
    link_path=${MDFILENAME:${#MDBOOKSRCPATH}}
    verbose "Creating summary entry for $MDFILENAME, filepath: $link_path"

    link_caption="" # Set based on the `if` conditions below.
    slashcount=$(grep -o '/' <<< "$MDFILENAME" | wc -l)
    spacecount=$(( slashcount - 1 ))
    # SUMMARY.md links
    if [[ "$MDFILENAME" == *SUMMARY.md ]]; then
      subdir="${MDFILENAME%SUMMARY\.md}"
      spacecount=$(( spacecount - 1 ))
      if [ $SHOW_FULL_FILE_PATHS -eq 1 ]; then
        link_caption="$SUB_SUMMARY_SYMBOL ${subdir:$((1+${#MDBOOKSRCPATH}))}"
      else
        link_caption="$SUB_SUMMARY_SYMBOL $(basename "${subdir:$((1+${#MDBOOKSRCPATH}))}")"
      fi
    # DIRECTORIES
    elif [[ "$MDFILENAME" == */. ]]; then
      if [ $SHOW_FULL_DIR_PATHS -eq 1 ]; then
        tsubdir="${MDFILENAME%/.}"
        subdir="${tsubdir:$((1+${#MDBOOKSRCPATH}))}"
      else 
        subdir="$(basename "${MDFILENAME%/.}")"
      fi
      spacecount=$(( spacecount - 1 ))
      link_caption="$SUBDIR_SYMBOL $subdir"
      link_path="/SUMMARY.md"
    # ALL OTHER FILES
    else 
      if [ $SHOW_FULL_FILE_PATHS -eq 1 ]; then
        link_caption="${MDFILENAME:$((1+${#MDBOOKSRCPATH}))}" # Show full path instead
      else 
        link_caption=$(basename "$MDFILENAME") # Only show filename (exclude rel path). TODO: exclude mdbooksrc and make this a flag
      fi
    fi
    # Strip the first chars from MDFILENAME, where the number of chars is defined by the length of MDBOOKSRCPATH'
    echo "$(generate_spaces $spacecount)- [$link_caption](.${link_path// /%20})" >> "./src/SUMMARY.md"
  done

fi # end of check whether to write a new SUMMARY.md


################### mdbook-mermaid sets details for mermaid diagrams to book.toml. More details @ https://github.com/badboy/mdbook-mermaid #################
# Adds to book.toml :
## [preprocessor]
## [preprocessor.mermaid]
## command = "mdbook-mermaid"
## [output]
## [output.html]
## additional-js = ["mermaid.min.js", "mermaid-init.js"]
mdbook-mermaid install . 2> /dev/null # Suppress unwanted verbose output on stderr.

### If required, also let mdbook generate PDF output. This will create a 'html' and a 'pdf' subdirectory in ./book. When PDF is not generated, the html will be output directly to './book'.
if [ $GEN_PDF -eq 1 ]; then
  if ! grep -Fxq "[output.pdf]" "book.toml"; then
    console_update "Line [output.pdf] not found in book.toml."
    if grep -Fxq "[output]" "book.toml"; then
      console_update "Found [output] in book.toml, adding [output.pdf] directly after."
      sed -i -e "/\[output\]/a\\" -e "\[output.pdf\]" book.toml
    else
      console_alert "WARNING: No [output.pdf] found in book.toml. Just adding [output.pdf] to the end of the file, please check if correct."
      echo "[output.pdf]" >> "book.toml"
    fi
  fi
fi

# Compile output: (html and pdf)
console_update "All preparations made. Now kicking off the mdbook build process. This may take some time when generating pdf output."
mdbook build
