#!/usr/bin/zsh

###############################################################################
###############################################################################
#
# Quick Site Generator 2 is a static website generator inspired by Nikola.
# It is written for the Z shell (zsh) because that's what I use and also because
# I like it better than Bash.
#
# This script is an almost complete rewrite of my old script because it became
# overly complicated and had way too many bugs, even though it worked on simple
# sites.
# 
# https://github.com/kekePower/qsgen2/
# 
###############################################################################
###############################################################################

VERSION="0.5.1" # Sun-2025-05-18
QSGEN="Quick Site Generator 2"

# Set to true or false
# This will show debug information from every function in this script
# You can also set debug=true in a single function if you want to debug only that specific one.
globaldebug=false

# Use Zsh fpath to set the path to some extra functions
fpath=(${HOME}/bin/include/common ${HOME}/bin/include/qsgen2/lang $fpath)
# In this case, let's load the 'include' function
autoload include
autoload zini

# Including some colors to the script
include common/colors

echo "${magenta}${blue_bg} ${QSGEN} ${end}${bold_white}${blue_bg}${VERSION} ${end}"

# Check for, and source, the config file for this specific website
config_loaded=false
if [[ -f $(pwd)/site.conf ]]; then
  if (${globaldebug}); then _msg debug "Config file found and sourced: $(pwd)/site.conf"; fi
  if zini $(pwd)/site.conf; then
    config_loaded=true
  else
    _msg info "Failed to load site.conf"
  fi
fi

# Fallback to 'config' if site.conf not found or failed to load
if [[ ${config_loaded} == false && -f $(pwd)/config ]]; then
  if (${globaldebug}); then _msg debug "Legacy config file found and sourced: $(pwd)/config"; fi
  _msg info "Warning: Using legacy 'config' file. Consider renaming to 'site.conf'"
  if zini $(pwd)/config; then
    config_loaded=true
  else
    _msg info "Failed to load legacy config file"
  fi
fi

# Exit if no config file was loaded
if [[ ${config_loaded} == false ]]; then
  _msg error "Cannot find or load configuration file."
  _msg info "Please create 'site.conf' in your project directory."
  _msg info "You can use 'config.example' as a template."
  exit 1
fi

# Load language as defined in config
typeset -A qsgenlang
lang_found=false
for dir in $fpath; do
  if [[ -f "${dir}/${config[project_lang]}" ]]; then
    # echo "Language file: ${dir}/${config[project_lang]}"
    source "${dir}/${config[project_lang]}"
    lang_found=true
    break
  fi
done
if [[ ${lang_found} == "false" ]]; then
      # Fall back to en_US if defined language isn't found
      echo "Defined language, ${config[project_lang]}, not found. Using en_US."
      source "${HOME}/bin/include/qsgen2/lang/en_US"
    fi

# Debug: Show loaded configuration
if (${globaldebug}); then
  _msg debug "=== Loaded Configuration ==="
  for key value in ${(kv)config}; do
    _msg debug "${key}: ${value}"
  done
  _msg debug "==========================="
  
  # Also show raw config file content
  local config_file="$(pwd)/site.conf"
  [[ ! -f "${config_file}" ]] && config_file="$(pwd)/config"
  if [[ -f "${config_file}" ]]; then
    _msg debug "=== Raw Config File ==="
    cat "${config_file}" | grep -v '^\s*#' | while read -r line; do
      _msg debug "${line}"
    done
    _msg debug "======================"
  fi
fi

function _msg() {
    local type=$1
    shift  # Remove the first argument so $@ now contains only keys or additional strings
    
    local full_msg=""
    for arg in "$@"; do
        if [[ -n "${qsgenlang[$arg]}" ]]; then
            full_msg+="${qsgenlang[$arg]}"
        else
            full_msg+="$arg"
        fi
    done
    
    # Determine the color based on the type
    local color="${end}"  # Default to no color if type is unrecognized
    case $type in
        std) color="${green}" ;;
        info) color="${yellow}" ;;
        debug) color="${red}" ;;
        other) color="${bold_yellow}" ;;
        sub) color="${magenta}" ;;
        main) color="${white}${green_bg}" ;;
    esac

    # Use printf with %b to allow backslash escape interpretation
    printf "${color}%b${end}\n" "${full_msg}"
}

function _version() {
  _msg info "_qsgen2_msg_7" "-$(strftime "%Y")"
  echo "${yellow}- https://github.com/kekePower/qsgen2/${end}"
  _msg info "_qsgen2_msg_8" " '${1} help' " "_qsgen2_msg_8.1"
  exit
}

function _help() {
  # This will also be translated some time in the future
  echo "This is where I'll write the Help documentation."
  exit
}

if [[ "$1" == "version" || "$1" == "-v" || "$1" == "--version" ]]; then
  _version ${0:t}
elif [[ "$1" == "help" || "$1" == "-h" || "$1" == "--help" ]]; then
  _help ${0:t}
fi

# Define cache files for blogs and pages
blog_cache_file="${config[project_root]}/.blog_cache"
pages_cache_file="${config[project_root]}/.pages_cache"

# Validate required configuration
required_configs=(
  "project_generator"
  "project_root"
  "site_root"
  "site_theme"
  "site_name"
  "site_url"
  "project_lang"
)

# Check for missing required configurations
missing_configs=()
for key in ${required_configs[@]}; do
  if [[ -z "${config[$key]}" ]]; then
    missing_configs+=("$key")
  fi
done

if [[ ${#missing_configs[@]} -gt 0 ]]; then
  _msg error "Missing required configuration values:"
  for key in ${missing_configs[@]}; do
    _msg error "- $key"
  done
  exit 1
fi

# Check if we're in a git repository
if [[ -d $(pwd)/.git ]]; then
  _msg info "Warning: Running in a git repository directory. Make sure this is intended."
  _msg info "If you want to generate the site, run from the project root directory."
  exit 1
fi

# Create necessary directories if they don't exist
mkdir -p "${config[project_root]}"
mkdir -p "${config[site_root]}"

# We define the variable 'engine' based on what's in the 'config' file.
if [[ ${config[project_generator]} == "native" ]]; then
  # Usage: ${engine} ${1} - Where 1 is the file you want to convert
  engine=_qstags
  export file_ext="qst"
elif [[ ${config[project_generator]} == "markdown" ]]; then
  if [[ ! -f /usr/local/bin/pandoc ]]; then
    _msg other "_qsgen2_msg_4"
    _msg other "https://github.com/jgm/pandoc/releases"
    exit
  else
    # Usage: ${engine} ${1} - Where 1 is the file you want parsed
    engine="/usr/local/bin/pandoc"
    engine_opts=
    export file_ext="md"
  fi
else
  _msg debug "_qsgen2_msg_5"
  exit
fi

function _run_engine() {
  # Usage: _run_engine <input>
  local debug=false
  
  if [[ ${config[project_generator]} == "native" ]]; then
    ${engine} ${1}
  elif [[ ${config[project_generator]} == "markdown" ]]; then
    echo "${1} | ${engine} ${engine_opts}"
  else
    _msg debug "ERROR running engine: ${engine}!"
    _msg info "Usage: _run_engine <input>"
    exit
  fi
}

if (${globaldebug}); then _msg debug "_qsgen2_msg_6"; fi

builtin cd ${config[project_root]}

# Loading Zsh modules
zmodload zsh/files
zmodload zsh/datetime
zmodload zsh/regex

# Let's put these here for now.
export today=$(strftime "%Y-%m-%d - %T")
export blogdate=$(strftime "%a-%Y-%b-%d")

# Let's create arrays of all the files we'll be working on

function _list_pages() {

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi

  # Initialize or clear the array to ensure it's empty before adding files
  pages_file_array=()

  export no_pages_found=false

  # Temporarily set null_glob for this function
  setopt local_options null_glob

  # Using an array to directly capture matching files
  local -a pages_files=(*.${file_ext})

  if (( ${#pages_files} == 0 )); then
    if ${debug}; then _msg debug "${0:t}_msg_1" " ${file_ext}."; fi
    export no_pages_found=true
    return
  else
    for file in "${pages_files[@]}"; do
      if ${debug}; then _msg debug "${0:t}_msg_2" " ${file}"; fi
      pages_file_array+=("$file")
    done
  fi

}

function _list_blogs() {

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
    
  # Initialize or clear the blogs array to ensure it's empty before adding files
  blogs_file_array=()

  export no_blogs_found=false

  # Temporarily set null_glob for this function
  setopt local_options null_glob

  # Directly capture matching blog files into an array
  local -a blog_files=(blog/*.blog(On))

  if (( ${#blog_files[@]} == 0 )); then
    if ${debug}; then _msg debug "${0:t}_msg_1"; fi
    export no_blogs_found=true
    return
  else
    for file in "${blog_files[@]}"
      do
        if ${debug}; then _msg debug "${0:t}_msg_2" " $file"; fi
          blogs_file_array+=("$file")
    done
  fi

}


# BLOG CACHE
blog_cache_file="${config[project_root]}/.blogindex.cache"

function _update_blog_cache() {
  # This function updates the blog cache with the current blog index content
  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  if (${debug}) _msg debug "Updating blog cache at ${blog_cache_file}"
  
  # Get the current blog index content
  local blog_index_content="$(<${config[project_root]}/blog/index.tmp.html)"
  
  # Store the content in the cache file
  echo "${blog_index_content}" > "${blog_cache_file}"
  
  if (${debug}) _msg debug "Blog cache updated with ${#blog_index_content} bytes"
}

function _load_blog_cache() {
  # Loads the blog index from cache if it exists
  if [[ -f "${blog_cache_file}" ]]; then
    if (${debug}) _msg debug "Loading blog index from cache"
    cat "${blog_cache_file}"
    return 0
  fi
  return 1
}

function _is_blog_cache_stale() {
  # Returns 0 (success) if cache is stale or doesn't exist, 1 (failure) otherwise
  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi

  # If we have new or updated blogs, cache is considered stale
  if [[ ${new_updated_blogs} == "true" ]]; then
    if (${debug}) _msg debug "Blog cache stale: New or updated blogs detected"
    return 0
  fi

  # If cache file doesn't exist, it's stale
  if [[ ! -f "${blog_cache_file}" ]]; then
    if (${debug}) _msg debug "Blog cache stale: Cache file does not exist"
    return 0
  fi

  # Check if cache is older than 1 hour (3600 seconds)
  local cache_mtime=$(stat -c %Y "${blog_cache_file}" 2>/dev/null || echo 0)
  local current_time=$(date +%s)
  
  if (( current_time - cache_mtime > 3600 )); then
    if (${debug}) _msg debug "Blog cache stale: Cache is older than 1 hour"
    return 0
  fi

  if (${debug}) _msg debug "Blog cache is fresh"
  return 1
}

function _blog_cache() {

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi

  _list_blogs

  # Create an associative array for the blog cache
  typeset -A blog_cache

  # Load the existing blog cache
  if [[ -f $blog_cache_file ]]; then
    while IFS=':' read -r name hash; do
      blog_cache[$name]=$hash
      if (${debug}) _msg debug "${0:t}_msg_1" " ${blog_cache[${name}]}"
    done < "$blog_cache_file"
  fi

  # Initialize the array for storing blog files to process
  make_blog_array=()

  # Process blog files
  for blog_file in ${blogs_file_array[@]}; do
    # Compute the current blog file hash
    current_hash=$(md5sum "$blog_file" | awk '{print $1}')
  
    if (${debug}) _msg debug "${0:t}_msg_2" " ${blog_file}"
    if (${debug}) _msg debug "${0:t}_msg_3" " ${current_hash}"

    # Check if the blog file is new or has changed
    if [[ ${blog_cache[$blog_file]} != "$current_hash" ]]; then
      if (${debug}) _msg debug "${0:t}_msg_4" " ${blog_file}"
      if (${debug}) _msg debug "${0:t}_msg_5" " ${current_hash}"
      # Blog file is new or has changed; add it to the processing array
      make_blog_array+=("$blog_file")

      # Update the blog cache with the new hash
      blog_cache[$blog_file]=$current_hash
    fi
  done

  # Rebuild the blog cache file from scratch
  : >| "$blog_cache_file"  # Truncate the file before writing
  for name in "${(@k)blog_cache}"; do
    echo "$name:${blog_cache[$name]}" >> "$blog_cache_file"
  done

}


# PAGES CACHE
# Returns the array pages_array()
function _pages_cache() {

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  # Create an associative array for the pages cache
  typeset -A pages_cache
    
  _list_pages
    
  # Load the existing pages cache
  if [[ -f $pages_cache_file ]]; then
    while IFS=':' read -r name hash; do
      pages_cache[$name]=$hash
      if (${debug}) _msg debug "${0:t}_msg_1" " ${pages_cache[${name}]}"
    done < "$pages_cache_file"
  fi
    
  # Initialize the array for storing pages files to process
  pages_array=()
    
  # Process pages files
  for file in ${pages_file_array[@]}; do
    # Compute the current blog file hash
    current_hash=$(md5sum "$file" | awk '{print $1}')
        
    if (${debug}) _msg debug "${0:t}_msg_2" " ${pages_cache[$file]}"
    if (${debug}) _msg debug "${0:t}_msg_3" " current_cache: ${current_hash}"
        
    # Check if the pages file is new or has changed
    if [[ ${pages_cache[$file]} != "$current_hash" ]]; then
      if (${debug}) _msg debug "${0:t}_msg_4" " ${pages_cache[$file]}"
      if (${debug}) _msg debug "${0:t}_msg_5" " current_cache: ${current_hash}"
      
      # Pages file is new or has changed; add it to the processing array
      pages_array+=("$file")
      
      # Update the pages cache with the new hash
      pages_cache[$file]=$current_hash
    fi
  done
    
  # Rebuild the pages cache file from scratch
  : >| "$pages_cache_file"  # Truncate the file before writing
  for name in "${(@k)pages_cache}"; do
    echo "$name:${pages_cache[$name]}" >> "$pages_cache_file"
  done

}

function _last_updated() {
  # This function updates #updated and #version tags in the provided string for buffers

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi

  local upd_msg="Last updated ${today} by <a href=\"https://blog.kekepower.com/qsgen2.html\">${QSGEN} ${VERSION}</a>"
  
  if (${debug}); then _msg debug "${0:t}_msg_1"; fi
  if (${debug}); then _msg debug "${0:t}_msg_2" " ${upd_msg}"; fi
    
  local content="${1}"

  # Perform the replacements
  local updated_content=$(echo "${content}" | sed \
    -e "s|#updated|${upd_msg}|")

  # Return the updated content
  echo "${updated_content}"

}

function _f_last_updated() {
  # Updates #updated and #version tags in the provided file using Zsh

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi

  # local file_path="${1}"
  local upd_msg="Last updated ${today} by <a href=\"https://blog.kekepower.com/qsgen2.html\">${QSGEN} ${VERSION}</a>"

  if ${debug}; then
    _msg debug "${0:t}_msg_1" " ${1}"
    _msg debug "${0:t}_msg_2" " ${upd_msg}"
  fi

  # Read the file content into a variable
  local content="$(<${1})"

  # Perform the replacement
  content="${content//#updated/${upd_msg}}"

  if [[ -f "${1}" ]]; then
    sed -i -e "s|#updated|${upd_msg}|" "${1}"
  else
    _msg debug "${0:t}_msg_3" " '${1}' " "${0:t}_msg_3.1"
  fi

}

function _file_to_lower() {
    
  local filename="${1}"

  # Replace spaces with dashes
  filename="${filename// /-}"

  # Convert to lowercase and remove invalid characters
  filename=$(echo "${filename}" | sed -e 's/^[^a-zA-Z0-9_.]+//g' -e 's/[^a-zA-Z0-9_-]+/-/g')

  echo ${filename}

}


function _pages() {
    # This function generates all the new and updated Pages
    
  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  _msg main "${0:t}_msg_3"
  
  # Load the cache for Pages
  if (${debug}) _msg debug "${0:t}_msg_1"
  _pages_cache
    
  if [[ ${no_pages_found} == "true" ]]; then
    _msg sub "${0:t}_msg_1"
    return
  fi
    
  if (( ${#pages_array[@]} > 0 )); then
    
    # If pages_array is not empty, we do work
    if (${debug}) _msg debug "${0:t}_msg_4"
    
    for pages_in_array in ${pages_array[@]}
      do
        if (${debug}) _msg debug "${0:t}_msg_5"
        local pages=${config[project_root]}/themes/${config[site_theme]}/pages.tpl
        
        # Let's check if we can access the pages.tpl file.
        # It not, exit script.
        if [[ ! -f ${pages} ]]; then
          _msg info "${0:t}_msg_6" " ${pages}"
          exit
        else
          # Read template once
          if (${debug}) _msg debug "${0:t}_msg_7"
          local pages_tpl="$(<${pages})"
        fi
        
        # _msg std "  - ${pages_in_array%.*}.html"
        # Read the file once
        if (${debug}) _msg debug "${0:t}_msg_9" " ${pages_in_array}"
        local page_content="$(<${pages_in_array})"
              
        # Grab the title from the Page
        if (${debug}) _msg debug "${0:t}_msg_10"
        if [[ ${config[project_generator]} == "native" ]]; then
          while read -r line
            do
            if [[ "$line" =~ ^#title=(.*) ]]; then
              local page_title=${match[1]}
              break
              #local page_title=$( echo ${page_content} | head -2 | grep \#title | cut -d= -f2 )
            fi
          done <<< "$page_content"
        elif [[ ${config[project_generator]} == "markdown" ]]; then
          while IFS= read -r line
            do
            # Check if the line starts with '#' and capture the line
            if [[ "$line" == \#* ]]; then
              # Remove all leading '#' characters and the first space (if present)
              local page_title="${line#\#}" # Remove the first '#' character
              page_title="${page_title#\#}" # Remove the second '#' character if present
              page_title="${page_title#"${page_title%%[![:space:]]*}"}" # Trim leading whitespace
              break # Exit the loop after finding the first heading
            fi
          done <<< ${page_content}
        fi
        _msg std "  - ${page_title}"
        if (${debug}) _msg debug "${0:t}_msg_11" " ${page_title}"
        
        # Remove the #title line from the buffer. No longer needed.
        if (${debug}) _msg debug "${0:t}_msg_12"
        page_content=$( echo ${page_content} | grep -v \#title )
        
        # HTML'ify the page content
        if (${debug}) _msg debug "${0:t}_msg_13" " ${pages_in_array}"
          page_content=$( _run_engine "$page_content" )
          # Look for links, images and videos and convert them if present.
          if (${debug}) _msg debug "${0:t}_msg_14"
          if [[ $( echo ${page_content} | grep \#link ) ]]; then
            if (${debug}) _msg debug "${0:t}_msg_15"
            page_content=$( _link "${page_content}" )
            fi
          if [[ $( echo ${page_content} | grep \#showimg ) ]]; then
            if (${debug}) _msg debug "${0:t}_msg_16"
            page_content=$( _image "${page_content}" )
          fi
          if [[ $( echo ${page_content} | grep \#linkimg ) ]]; then
            if (${debug}) _msg debug "${0:t}_msg_17"
            page_content=$( _linkimg "${page_content}" )
          fi
          if [[ $( echo ${page_content} | grep \#ytvideo ) ]]; then
            if (${debug}) _msg debug "${0:t}_msg_18"
            page_content=$( _youtube "${page_content}" )
          fi
        
        # Replace every #pagetitle in pages_tpl
        if (${debug}) _msg debug "${0:t}_msg_19"
        pages_tpl=$(echo "${pages_tpl}" | perl -pe "s|#pagetitle|${page_title}|gs; s|#tagline|${config[site_tagline]}|gs; s|#sitename|${config[site_name]}|gs")
        
        if (${debug}) _msg debug "${0:t}_msg_20"
        # Use awk for multi-line and special character handling
        pages_tpl=$( awk -v new_body="$page_content" '{sub(/BODY/, new_body)} 1' <(echo "${pages_tpl}") )
        
        # Replace #updated with today's date and #version with Name and Version to footer
        if (${debug}) _msg debug "${0:t}_msg_21"
        pages_tpl=$( _last_updated ${pages_tpl} )
        
        # Always use lowercase for file names
        if (${debug}) _msg debug "${0:t}_msg_22"
        pages_title_lower=$( _file_to_lower "${pages_in_array}" )
        
        # Clean up unused tags, if any
        if (${debug}) _msg debug "${0:t}_msg_23"
        pages_tpl=$( _cleanup "${pages_tpl}" )
        
        # Write pages_tpl to disk
        # _msg std "Writing ${config[site_root]}/${pages_title_lower%.*}.html to disk."
        echo "${pages_tpl}" > ${config[site_root]}/${pages_title_lower%.*}.html
        
        # Insert the blog to the front page is blog_in_index is true and the file in the array is index.file_ext
        # and if index.tmp.html exist and is not empty
        if [[ ${pages_in_array} == "index.${file_ext}" &&  ${config[site_blog]} == "true" && -s "${config[project_root]}/blog/index.tmp.html" ]]; then
          if (${debug}) _msg sub "${0:t}_msg_24" " ${pages_in_array}"
          if (${debug}) _msg sub "${0:t}_msg_25" " ${config[site_blog]}"
          if (${debug}) _msg sub "${0:t}_msg_26"
          if (${debug}) ls -l ${config[project_root]}/blog/index.tmp.html
          _add_blog_list_to_index
        fi
        
      done
      
      export new_updated_pages=true
      
    else
      # Insert the blog to the front page is blog_in_index is true and the file in the array is index.file_ext
      # and if index.tmp.html exist and is not empty
      if [[ ${config[site_blog]} == "true" && -s "${config[project_root]}/blog/index.tmp.html" ]]; then
        _msg std "${0:t}_msg_27"
        if (${debug}) _msg sub "${0:t}_msg_28" " ${pages_in_array}"
        if (${debug}) _msg sub "${0:t}_msg_29" " ${config[site_blog]}"
        if (${debug}) _msg sub "${0:t}_msg_30"
        if (${debug}) ls -l ${config[project_root]}/blog/index.tmp.html
        _add_blog_list_to_index
      fi
        
      _msg sub "${0:t}_msg_31"
      export new_updated_pages=false

  fi

}

function _blogs() {
  # This function either generates blog files or exports metadata based on the argument
  
  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  _msg main "${0:t}_msg_3"
  
  # Running function _list_blogs
  if (${debug}) _msg debug "${0:t}_msg_1"
  _list_blogs
    
  if [[ ${no_blogs_found} == "true" ]]; then
    _msg sub "${0:t}_msg_2"
    return
  fi
    
  # Running function _blog_cache
  if (${debug}) _msg debug "${0:t}_msg_4"
  _blog_cache

  if (( ${#make_blog_array[@]} > 0 )); then
        
    # Declare the array to hold metadata strings for each blog
    BLOG_META_STR_ARRAY=()

    # Regular blog creation process
                
    if [[ -f ${config[project_root]}/themes/${config[site_theme]}/blogs.tpl ]]; then
      local blog_tpl=$(<"${config[project_root]}/themes/${config[site_theme]}/blogs.tpl")
    else
      _msg info "${0:t}_msg_5"
      exit
    fi

    for blog in "${make_blog_array[@]}"; do
      if (${debug}) _msg info "*************************************************************************"
      if (${debug}) _msg info "**************************FOR LOOP START*********************************"
      if (${debug}) _msg info "*************************************************************************"
      if (${debug}) _msg debug "${0:t}_msg_6" " ${blog}"
                
      local content="$(<"${blog}")"
      local sdate btitle ingress body blog_index blog_dir blog_url
      
      # Initialize variables to track if DATE and BLOG_TITLE are found
      local date_found=false
      local title_found=false

      # Process content line by line
      while IFS= read -r line
        do
        # Check for the DATE line
        if [[ "${line}" == "DATE "* ]]; then
          if (${debug}) _msg debug "${0:t}_msg_7"
          date_found=true
        fi
        # Check for the BLOG_TITLE line
        if [[ "${line}" == "BLOG_TITLE "* ]]; then
          if (${debug}) _msg debug "${0:t}_msg_8"
          title_found=true
        fi
        # If both DATE and BLOG_TITLE are found, no need to continue checking
        if [[ "${date_found}" == true && "${title_found}" == true ]]; then
          break
        fi
      done <<< "${content}"

      # Check if DATE or BLOG_TITLE metadata is missing and log message
      if [[ "${date_found}" == false ]]; then
        if (${debug}) _msg debug "${0:t}_msg_9" " ${blog}."
        continue  # Skip this file and move to the next
      fi
      if [[ "${title_found}" == false ]]; then
        if (${debug}) _msg debug "${0:t}_msg_10" " ${blog}."
        continue  # Skip this file and move to the next
      fi

      # Extract blog information
      sdate=( $( echo ${content} | grep DATE | sed "s|DATE\ ||" | sed "s|\-|\ |g" ) )
      if [[ ${config[project_generator]} == "native" ]]; then
      if (${debug}) _msg debug "* qstags: Fetching BLOG_TITLE"
        while IFS= read -r line; do
          if [[ "$line" == "BLOG_TITLE "* ]]; then
            btitle="${line#BLOG_TITLE }"
            break
          fi
        done <<< "$content"
      elif [[ ${config[project_generator]} == "markdown" ]]; then
      if (${debug}) _msg debug "* markdown: Fetching BLOG_TITLE"
        while IFS= read -r line; do
          if [[ "$line" == \#* ]]; then
            btitle="${line#\#}" # Remove the first '#' character
            btitle="${btitle#\#}" # Remove the second '#' character if present
            btitle="${btitle#"${btitle%%[![:space:]]*}"}" # Trim leading whitespace
            break # Exit the loop after finding the first heading
          fi
        done <<< "$content"
      fi
      if (${debug}) _msg debug "* Fetching INGRESS"
      ingress=$( echo ${content} | sed "s/'/\\\'/g" | xargs | grep -Po "#INGRESS_START\K(.*?)#INGRESS_STOP" | sed "s|\ \#INGRESS_STOP||" | sed "s|^\ ||" )
      if (${debug}) _msg debug "* Fetching BODY"
      body=$( echo ${content} | sed "s/'/\\\'/g" | xargs | grep -Po "#BODY_START\K(.*?)#BODY_STOP" | sed "s|\ \#BODY_STOP||" | sed "s|^\ ||" )

      blog_index=$(echo "${btitle:l}" | sed 's/ /_/g; s/,//g; s/\.//g; s/://g; s/[()]//g')

      blog_dir="/blog/${sdate[2]}/${sdate[3]:l}/${sdate[4]}"
      blog_url="${blog_dir}/${blog_index}.html"
                    
      if (${debug}) _msg debug "${0:t}_msg_11" " ${blog} " "${0:t}_msg_11.1"

        # Concatenate all metadata into a single string for the current blog
        local metadata_str="SDATE: ${sdate[@]}||BTITLE: ${btitle}||INGRESS: ${ingress}||URL: ${blog_url}"
        # Append this metadata string to the array
        BLOG_META_STR_ARRAY+=("${metadata_str}")
                    
        if (${debug}) _msg debug "${0:t}_msg_12" " ${blog}"
                       
          _msg std "  - ${btitle}"

          # Prepare the blog template
          if (${debug}) _msg debug "${0:t}_msg_14" " ${blog}"
          local blog_content=$(
            echo "${blog_tpl}" | \
            perl -pe "\
            s|BLOGTITLE|${btitle}|g; \
            s|BLOGURL|${blog_url}|g; \
            s|\QINGRESS\E|${ingress}|g; \
            s|\QBODY\E|${body}|g \
            ")
            blog_content="${blog_content//CALNDAY/${sdate[4]}}"
            blog_content="${blog_content//CALYEAR/${sdate[2]}}"
            blog_content="${blog_content//CALMONTH/${sdate[3]}}"
            blog_content="${blog_content//CALADAY/${sdate[1]}}"

            if (${debug}) _msg debug "${0:t}_msg_15" " ${engine} " "${0:t}_msg_15_1" " ${blog}"
              blog_content=$( _run_engine "${blog_content}" )
              # Look for links, images and videos and convert them if present.
              if (${debug}) _msg debug "${0:t}_msg_16"
              if [[ $( echo ${blog_content} | grep \#link ) ]]; then
                if (${debug}) _msg debug "${0:t}_msg_17"
                blog_content=$(_link "${blog_content}")
              fi
              if [[ $( echo ${blog_content} | grep \#showimg ) ]]; then
                if (${debug}) _msg debug "${0:t}_msg_18"
                blog_content=$(_image "${blog_content}")
              fi
              if [[ $( echo ${blog_content} | grep \#linkimg ) ]]; then
                if (${debug}) _msg debug "${0:t}_msg_19"
                blog_content=$(_linkimg "${blog_content}")
              fi
              if [[ $( echo ${blog_content} | grep \#ytvideo ) ]]; then
                if (${debug}) _msg debug "${0:t}_msg_20"
                blog_content=$(_youtube "${blog_content}")
              fi
                  
            # Replace every #tagline in blog_content
            if (${debug}) _msg debug "${0:t}_msg_21"
            blog_content=$( echo ${blog_content} | perl -pe "s|#tagline|${config[site_tagline]}|gs; s|#sitename|${config[site_name]}|gs; s|#pagetitle|${page_title}|gs" )
        
            if (${debug}) _msg debug "* Running _last_updated"
            blog_content=$(_last_updated "${blog_content}")
            if (${debug}) _msg debug "* Running _cleanup"
            blog_content=$(_cleanup "${blog_content}")
                   
            # Create directory if it doesn't exist
            if (${debug}) _msg debug "${0:t}_msg_22" " ${config[site_root]}${blog_dir}"
            [[ ! -d "${config[site_root]}/${blog_dir}" ]] && mkdir -p "${config[site_root]}/${blog_dir}"

            # Write to file
            if (${debug}) _msg debug "${0:t}_msg_23" " ${config[site_root]}${blog_url}"
            echo "${blog_content}" > "${config[site_root]}${blog_url}"
                   
            unset sdate btitle ingress body blog_index blog_dir blog_url

    done
    # Now BLOG_META_STR_ARRAY contains the metadata string for each blog post
    export BLOG_META_STR_ARRAY
    if (${debug}) _msg debug "${0:t}_msg_24"
    export new_updated_blogs=true

  else
    _msg sub "${0:t}_msg_25"
    export new_updated_blogs=false
  fi

  if [[ ${new_updated_blogs} == "true" ]]; then
    if (${debug}) _msg sub "${0:t}_msg_26"
    _blog_idx_for_index
    if (${debug}) _msg sub "${0:t}_msg_27"
    _blog_index
  fi

}

function _blog_idx_for_index() {
  # This function generates the file blog/index.tmp.html
    
  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
    
  _msg sub "${0:t}_msg_1" " ${config[project_root]}/blog/index.tmp.html"
    
  if (${debug}) _msg debug "${0:t}_msg_2"
    
  local blog_list_tpl=$(<${config[project_root]}/themes/${config[site_theme]}/blog_list.tpl)
  local blog_list_content=""
    
  # Truncate file before writing new one
  : >| "${config[project_root]}/blog/index.tmp.html"
    
  # if (${debug}) _msg debug "${0:t}_msg_3" " ${BLOG_META_STR_ARRAY[@]}"
    
  for meta_str in ${BLOG_META_STR_ARRAY[@]}
    do
      if (${debug}) _msg debug "${0:t}_msg_4"
      if (${debug}) _msg debug "${0:t}_msg_5" " ${meta_str}"

      # Split meta_str into individual metadata components
      local -a meta_array=("${(@s/||/)meta_str}")

      # Initialize variables to store each component
      local sdate btitle ingress url

      # Iterate over each component and extract information
      if (${debug}) _msg debug "${0:t}_msg_6"
      for component in "${meta_array[@]}"
        do
          case "${component}" in
            SDATE:*) sdate=${component#SDATE: } ;;
            BTITLE:*) btitle=${component#BTITLE: } ;;
            INGRESS:*) ingress=${component#INGRESS: } ;;
            URL:*) url=${component#URL: } ;;
          esac

      done
      
          local adate=( $( echo ${sdate} ) )
          local caladay="${adate[1]}"
          local calyear="${adate[2]}"
          local calmonth="${adate[3]}"
          local calnday="${adate[4]}"
            
          local bdate="${adate[1]} - ${adate[4]}/${adate[3]}/${adate[2]}"
          blog_list_content+=$(
            echo "${blog_list_tpl}" | \
            perl -pe "\
            s|BLOGURL|${config[site_url]}${url}|g; \
            s|BLOGTITLE|${btitle}|g; \
            s|INGRESS|${ingress}|g; \
            s|BLOGDATE|${bdate}|g; \
            s|CALADAY|${caladay}|g; \
            s|CALNDAY|${calnday}|g; \
            s|CALMONTH|${calmonth}|g; \
            s|CALYEAR|${calyear}|g \
            ")
        
      unset sdate btitle ingress url adate caladay calyear calmonth calnday

  done
  if (${debug}) _msg debug "${0:t}_msg_7" " ${engine} " "${0:t}_msg_7.1"
  # Catch any QStags or Markdown in the Ingress
  blog_list_content=$( _run_engine ${blog_list_content} )
  if (${debug}) _msg debug "${0:t}_msg_8" " ${config[project_root]}/blog/index.tmp.html"
  #if (${debug}) _msg debug "${0:t}_msg_9" " ${blog_list_content}"
  echo ${blog_list_content} > ${config[project_root]}/blog/index.tmp.html
  _update_blog_cache

}

function _blog_index() {
  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi

  # Check if we need to update the blog index
  if [[ ${new_updated_blogs} == "true" ]] || _is_blog_cache_stale; then
    if (${debug}) _msg debug "Generating new blog index"
    
    # Get the template
    local blog_index_tpl=$(<${config[project_root]}/themes/${config[site_theme]}/blog_index.tpl)
    
    # Get the blog list content
    local blog_index_list
    if [[ ${new_updated_blogs} == "true" ]]; then
      # If we have new/updated blogs, use the fresh content
      blog_index_list=$(<${config[project_root]}/blog/index.tmp.html)
    else
      # Otherwise try to load from cache
      blog_index_list=$(_load_blog_cache) || {
        # If cache load fails, use the fresh content
        blog_index_list=$(<${config[project_root]}/blog/index.tmp.html)
      }
    fi
    
    # Generate the final content
    local blog_index_content=$(echo "${blog_index_tpl}" | \
      perl -pe "s|#sitename|${config[site_name]}|gs; s|#tagline|${config[site_tagline]}|gs")
    blog_index_content=$(awk -v new_body="$blog_index_list" '{sub(/BODY/, new_body)} 1' <(echo "${blog_index_content}"))
    
    # Create output directory if it doesn't exist
    mkdir -p "${config[site_root]}/blog"
    
    # Write the index file
    echo "$blog_index_content" > "${config[site_root]}/blog/index.html"
    _f_last_updated "${config[site_root]}/blog/index.html"
    
    # Update the cache with the new content
    _update_blog_cache
    
    if (${debug}); then
      _msg debug "Generated new blog index at ${config[site_root]}/blog/index.html"
      _msg debug "Blog index size: ${#blog_index_content} bytes"
    fi
  else
    # Use cached content
    if (${debug}) _msg debug "Using cached blog index"
    local cached_content=$(_load_blog_cache)
    mkdir -p "${config[site_root]}/blog"
    echo "$cached_content" > "${config[site_root]}/blog/index.html"
  fi
}

function _add_blog_list_to_index() {
  
  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  # Let's find the file 'index.qst' and add the blog if blog_in_index is true
  if (${debug}) _msg debug "${0:t}_msg_1"
  local blog_index_list=$(<${config[project_root]}/blog/index.tmp.html)
  local site_index_file=$(<${config[site_root]}/index.html)
  echo "${site_index_file}" | awk -v new_body="${blog_index_list}" '{sub(/BLOGINDEX/, new_body)} 1' > "${config[site_root]}/index.html"

}

function _sitemap() {
  
  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  # Check if sitemap is set to true and if there are updated Blogs, Pages, or if cache is stale
  if ([[ ${config[site_sitemap]} == "true" ]] && 
     ([[ ${new_updated_blogs} == "true" ]] || 
      [[ ${new_updated_pages} == "true" ]] ||
      _is_blog_cache_stale)) || 
     [[ ${sitemap_force} == "true" ]]; then

    setopt extendedglob
    
    _msg main "${0:t}_msg_1"

    local sm_file="sitemap.xml"
    local b_file="sitemap-blogs.xml"
    local p_file="sitemap-pages.xml"
    local sitemap_file="${config[site_root]}/${sm_file}"
    local sitemap_blog="${config[site_root]}/${b_file}"
    local sitemap_page="${config[site_root]}/${p_file}"

    # Find all HTML files and store them in an array
    builtin cd ${config[site_root]}
    local -a html_files=(**/[a-z]*.html(.))
    local -a blog_files=()
    local -a page_files=()
    for file in "${html_files[@]}"; do
      if [[ $file == *blog* ]]; then
        blog_files+=("$file")
      else
        page_files+=("$file")
      fi
    done
    
    # Start of the XML file for BLOGS
    echo '<?xml version="1.0" encoding="UTF-8"?>' > ${sitemap_blog}
    echo "<!-- Sitemap generated by ${QSGEN} ${VERSION} - https://github.com/kekePower/qsgen2 -->" >> ${sitemap_blog}
    echo "<?xml-stylesheet type=\"text/xsl\" href=\"${config[site_url]}/css/default-sitemap.xsl?sitemap=page\"?>" >> ${sitemap_blog}
    echo '<urlset' >> ${sitemap_blog}
    echo '  xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' >> ${sitemap_blog}
    echo '  xmlns:xhtml="http://www.w3.org/1999/xhtml"' >> ${sitemap_blog}
    echo '  xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"' >> ${sitemap_blog}
    echo '>' >> ${sitemap_blog}

    # Add each URL to the sitemap
    for file in "${blog_files[@]}"
      do
        # Remove www_root from the path and prepend site_url
        local url="${config[site_url]}/${file}"
        local lastmod=$(stat -c %y "${file}" 2>/dev/null | cut -d' ' -f1,2 | sed 's/ /T/' | sed 's/\..*$//')

        echo "  <url>" >> ${sitemap_blog}
        echo "    <loc>${url}</loc>" >> ${sitemap_blog}
        echo "    <lastmod><![CDATA[${lastmod}+01:00]]></lastmod>" >> ${sitemap_blog}
        echo "    <changefreq><![CDATA[always]]></changefreq>" >> ${sitemap_blog}
        echo "    <priority><![CDATA[1]]></priority>" >> ${sitemap_blog}
        echo "  </url>" >> ${sitemap_blog}
    done

    # End of the XML file
    echo '</urlset>' >> "${sitemap_blog}"
    _msg std "  - ${b_file}"
    
    # Start of the XML file for PAGES
    echo '<?xml version="1.0" encoding="UTF-8"?>' > ${sitemap_page}
    echo "<!-- Sitemap generated by ${QSGEN} ${VERSION} - https://github.com/kekePower/qsgen2 -->" >> ${sitemap_page}
    echo "<?xml-stylesheet type=\"text/xsl\" href=\"${config[site_url]}/css/default-sitemap.xsl?sitemap=page\"?>" >> ${sitemap_page}
    echo '<urlset' >> ${sitemap_page}
    echo '  xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' >> ${sitemap_page}
    echo '  xmlns:xhtml="http://www.w3.org/1999/xhtml"' >> ${sitemap_page}
    echo '  xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"' >> ${sitemap_page}
    echo '>' >> ${sitemap_page}

    # Add each URL to the sitemap
    for file in "${page_files[@]}"
      do
        # Remove www_root from the path and prepend site_url
        local url="${config[site_url]}/${file}"
        local lastmod=$(stat -c %y "${file}" 2>/dev/null | cut -d' ' -f1,2 | sed 's/ /T/' | sed 's/\..*$//')

        echo "  <url>" >> ${sitemap_page}
        echo "    <loc>${url}</loc>" >> ${sitemap_page}
        echo "    <lastmod><![CDATA[${lastmod}+01:00]]></lastmod>" >> ${sitemap_page}
        echo "    <changefreq><![CDATA[always]]></changefreq>" >> ${sitemap_page}
        echo "    <priority><![CDATA[1]]></priority>" >> ${sitemap_page}
        echo "  </url>" >> ${sitemap_page}
    done

    # End of the XML file
    echo '</urlset>' >> "${sitemap_page}"
    _msg std "  - ${p_file}"
    
    # Update the blog cache after generating sitemap
    _update_blog_cache

    if (${debug}); then _msg debug "${0:t}_msg_2" " ${sitemap_file}"; fi

    # Start of the XML file for the main sitemap
    echo '<?xml version="1.0" encoding="UTF-8"?>' > "${sitemap_file}"
    echo "<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">" >> "${sitemap_file}"
  
    # Add sitemap-blogs.xml to the sitemap
    echo "  <sitemap>" >> "${sitemap_file}"
    echo "    <loc>${config[site_url]}/${b_file}</loc>" >> "${sitemap_file}"
    local lastmod_b=$(stat -c %y "${b_file}" 2>/dev/null | cut -d' ' -f1,2 | sed 's/ /T/' | sed 's/\..*$//')
    echo "    <lastmod>${lastmod_b}</lastmod>" >> "${sitemap_file}"
    echo "  </sitemap>" >> "${sitemap_file}"
  
    # Add sitemap-pages.xml to the sitemap
    echo "  <sitemap>" >> "${sitemap_file}"
    echo "    <loc>${config[site_url]}/${p_file}</loc>" >> "${sitemap_file}"
    local lastmod_p=$(stat -c %y "${p_file}" 2>/dev/null | cut -d' ' -f1,2 | sed 's/ /T/' | sed 's/\..*$//')
    echo "    <lastmod>${lastmod_p}</lastmod>" >> "${sitemap_file}"
    echo "  </sitemap>" >> "${sitemap_file}"
  
    # End of the XML file
    echo "</sitemapindex>" >> "${sitemap_file}"
    _msg std "  - ${sm_file}"

    builtin cd ${config[project_root]}
  
  fi

}

function _link() {
  # This converts #link tags to actual clickable links in a provided string
  
  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  local content="${1}"
  local modified_content=""

  # Process the content line by line
  echo "${content}" | while IFS= read -r line; do
    if [[ ${line} == *"#link"* ]]; then
      if (${debug}) _msg debug "${0:t}_msg_1" " ${line}"

      # Extract the URL and the link text
      local url_full=$(echo "${line}" | awk -F'#link ' '{print $2}' | awk -F'¤' '{print $1 "¤" $2}')
      local url_dest=$(echo "${url_full}" | awk -F'¤' '{print $1}')
      local url_txt=$(echo "${url_full}" | awk -F'¤' '{print $2}')

      if (${debug}) _msg debug "${0:t}_msg_2" " ${url_dest}"
      if (${debug}) _msg debug "${0:t}_msg_3" " ${url_txt}"

      # Form the replacement HTML link
      local modified_link="<a href=\"${url_dest}\">${url_txt}"
      if [[ ${url_dest} =~ ^https?:// ]]; then
        # Add external link icon for external URLs
        modified_link+="<img class=\"exticon\" alt=\"External site icon\" src=\"/images/ext-url.png\" width=\"12\" />"
      fi
      modified_link+="</a>"
      line=${line//"#link ${url_full}"/${modified_link}}
    fi
      modified_content+="${line}\n"
  done

  # Return the modified content
  echo -e "${modified_content}"

}

function _image() {
    # This replaces #showimg tags with actual HTML img tags in a provided string

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  local content="${1}"
  local modified_content=""

  # Process the content line by line
  echo "${content}" | while IFS= read -r line; do
    if [[ ${line} == *"#showimg"* ]]; then
      if (${debug}) _msg debug "${0:t}_msg_1" " ${line}"

      # Extract image link and alt text
      local img_link=$(echo "${line}" | awk -F'#showimg ' '{print $2}')
      local image=$(echo "${img_link}" | awk -F'¤' '{print $1}')
      local img_alt=$(echo "${img_link}" | awk -F'¤' '{print $2}')

      # Determine the source of the image
      local real_image=""
      if [[ ${image} =~ ^https?:// ]]; then
        real_image=${image}
      elif [[ ${image} =~ ^\/ ]]; then
        real_image=${image}
      else
        real_image="/images/${image}"
      fi

      # Form the replacement HTML image tag
      local img_tag="<img src=\"${real_image}\" alt=\"${img_alt}\" width=\"500\" />"
      line=${line//"#showimg ${img_link}"/${img_tag}}
    fi
      modified_content+="${line}\n"
  done

  # Return the modified content
  echo -e "${modified_content}"

}

function _linkimg() {
  # This function replaces #linkimg tags with <a> tags around <img> tags

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  local content="${1}"
  local modified_content=""

  # Process the content line by line
  echo "${content}" | while IFS= read -r line; do
    if [[ ${line} == *"#linkimg"* ]]; then
      if (${debug}) _msg debug "${0:t}_msg_1" " ${line}"

      # Extract link, image, and alt text
      local img_link=$(echo "${line}" | awk -F'#linkimg ' '{print $2}')
      local img_url=$(echo "${img_link}" | awk -F'¤' '{print $1}')
      local img_alt=$(echo "${img_link}" | awk -F'¤' '{print $2}')

      # Determine the source of the image
      local real_image=""
      if [[ ${img_url} =~ ^https?:// ]]; then
        real_image=${img_url}
      elif [[ ${img_url} =~ ^\/ ]]; then
        real_image=${img_url}
      else
        real_image="/images/${img_url}"
      fi

      # Form the replacement HTML link and image tag
      local img_tag="<a href=\"${real_image}\"><img src=\"${real_image}\" alt=\"${img_alt}\" width=\"500\" /></a>"
      line=${line//"#linkimg ${img_link}"/${img_tag}}
    fi
    modified_content+="${line}\n"
  done

  # Return the modified content
  echo -e "${modified_content}"
}

function _youtube() {
  # This embeds a YouTube video in a provided string

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  local content="${1}"
  local modified_content=""

  # Process the content line by line
  echo "${content}" | while IFS= read -r line; do
    if [[ ${line} == *"#ytvideo"* ]]; then
      if (${debug}) _msg debug "${0:t}_msg_1" " ${line}"

      # Extract YouTube video ID
      local yt_id=$(echo "${line}" | awk -F'#ytvideo ' '{print $2}')

      # Form the replacement YouTube iframe embed
      local yt_iframe="<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/${yt_id}\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen></iframe>"
      line=${line//"#ytvideo ${yt_id}"/${yt_iframe}}
    fi
    modified_content+="${line}\n"
  done

  # Return the modified content
  echo -e "${modified_content}"

}

function _cleanup() {
  # This removes tags used in the templates that may be left over for some reason

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  local content="${1}"

  if (${debug}) _msg debug "${0:t}_msg_1"

  # Perform the cleanup
  # -e "s|BLOGINDEX\ ||g"
  local cleaned_content=$(echo "${content}" | sed \
    -e "s|¤||g" \
    -e "s|#showimg\ ||g" \
    -e "s|#ytvideo\ ||g" \
    -e "s|#link\ ||g" \
    -e "s|#linkimg\ ||g" \
    )

  # Return the cleaned content
  echo "${cleaned_content}"

}

function _p_qstags() {

  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  local content="${1}"

  if ${debug}; then
    _msg debug "${0:t}_msg_1"
  fi

  # Use perl to convert QStags to HTML
  perl -0777 -pe '
    BEGIN {
      @qstags = (
        "#BR", "<br/>\n",
        "#BD", "<b>", "#EBD", "</b>",
        "#I", "<i>", "#EI", "</i>\n",
        "#P", "<p>", "#EP", "</p>\n",
        "#Q", "<blockquote>", "#EQ", "</blockquote>\n",
        "#C", "<code>", "#EC", "</code>\n",
        "#H1", "<h1>", "#EH1", "</h1>\n",
        "#H2", "<h2>", "#EH2", "</h2>\n",
        "#H3", "<h3>", "#EH3", "</h3>\n",
        "#H4", "<h4>", "#EH4", "</h4>\n",
        "#H5", "<h5>", "#EH5", "</h5>\n",
        "#H6", "<h6>", "#EH6", "</h6>\n",
        "#STRONG", "<strong>", "#ESTRONG", "</strong>\n",
        "#EM", "<em>", "#SEM", "</em>\n",
        "#DV", "<div>", "#EDV", "</div>\n",
        "#SPN", "<span>", "#ESPN", "</span>\n",
        "#UL", "<ul>", "#EUL", "</ul>\n",
        "#OL", "<ol>", "#EOL", "</ol>\n",
        "#LI", "<li>", "#ELI", "</li>\n",
        "#UD", "<u>", "#EUD", "</u>\n",
        "#TBL", "<table>", "#ETBL", "</table>\n",
        "#TR", "<tr>", "#ETR", "</tr>\n",
        "#TD", "<td>", "#ETD", "</td>\n",
        "#TH", "<th>", "#ETH", "</th>\n",
        "#ART", "<article>", "#EART", "</article>\n",
        "#SEC", "<section>", "#ESEC", "</section>\n",
        "#ASIDE", "<aside>", "#EASIDE", "</aside>\n",
        "#NAV", "<nav>", "#ENAV", "</nav>\n",
        "#BTN", "<button>", "#EBTN", "</button>\n",
        "#SEL", "<select>", "#ESEL", "</select>\n",
        "#OPT", "<option>", "#EOPT", "</option>\n",
        "#LT", "&lt;", "#GT", "&gt;", "#NUM", "&num;"
      );
    }

    for (my $i = 0; $i < $#qstags; $i += 2) {
        my $qstag = $qstags[$i];
        my $html = $qstags[$i + 1];
        s/\Q$qstag\E/$html/g;
    }
    ' <<< "$content"
    
}

function _qstags() {
  
  # This function uses the regex module from Zsh to parse the QStags
  
  if [[ ${globaldebug} == "true" ]]; then
    local debug=true
  else
    local debug=false
  fi
  
  local content="${1}"

  if ${debug}; then
    _msg debug "${0:t}_msg_1"
  fi

  # Load regex module
  # zmodload zsh/regex

  # Define tag replacements as an associative array
  typeset -A qstags=(
    "#BR" "<br/>\n"
    "#BD" "<b>" "#EBD" "</b>"
    "#I" "<i>" "#EI" "</i>\n"
    "#P" "<p>" "#EP" "</p>\n"
    "#Q" "<blockquote>" "#EQ" "</blockquote>\n"
    "#C" "<code>" "#EC" "</code>\n"
    "#H1" "<h1>" "#EH1" "</h1>\n"
    "#H2" "<h2>" "#EH2" "</h2>\n"
    "#H3" "<h3>" "#EH3" "</h3>\n"
    "#H4" "<h4>" "#EH4" "</h4>\n"
    "#H5" "<h5>" "#EH5" "</h5>\n"
    "#H6" "<h6>" "#EH6" "</h6>\n"
    "#STRONG" "<strong>" "#ESTRONG" "</strong>\n"
    "#EM" "<em>" "#SEM" "</em>\n"
    "#DV" "<div>" "#EDV" "</div>\n"
    "#SPN" "<span>" "#ESPN" "</span>\n"
    "#UL" "<ul>" "#EUL" "</ul>\n"
    "#OL" "<ol>" "#EOL" "</ol>\n"
    "#LI" "<li>" "#ELI" "</li>\n"
    "#UD" "<u>" "#EUD" "</u>\n"
    "#TBL" "<table>" "#ETBL" "</table>\n"
    "#TR" "<tr>" "#ETR" "</tr>\n"
    "#TD" "<td>" "#ETD" "</td>\n"
    "#TH" "<th>" "#ETH" "</th>\n"
    "#ART" "<article>" "#EART" "</article>\n"
    "#SEC" "<section>" "#ESEC" "</section>\n"
    "#ASIDE" "<aside>" "#EASIDE" "</aside>\n"
    "#NAV" "<nav>" "#ENAV" "</nav>\n"
    "#BTN" "<button>" "#EBTN" "</button>\n"
    "#SEL" "<select>" "#ESEL" "</select>\n"
    "#OPT" "<option>" "#EOPT" "</option>\n"
    "#LT" "&lt;" "#GT" "&gt;" "#NUM" "&num;"
    )

    #for qstag html (${(kv)qstags})
    #  do
    #    # Escape tag for regex use
    #    local escapedTag=$(printf '%s' "$qstag" | sed 's/[].\[^$*]/\\&/g')
    #    if [[ "$content" =~ "$escapedTag" ]]; then
    #        content=${content//($qstag)/$html}
    #    fi
    #done
    for qstag html (${(kv)qstags}); do
      # Direct replacement without regex check
      content=${content//${qstag}/${html}}
    done

    echo "${content}"

}


case ${1} in
    force)
        _msg sub "_qsgen2_msg_2"
        : >| "$blog_cache_file"  # Truncate the blog cache before doing update
        : >| "$pages_cache_file"  # Truncate the page cache before doing update
    ;;
    sitemap)
        _msg sub "Updating sitemaps"
        export sitemap_force=true
        _sitemap
        exit
    ;;
    *)
        # Nothing
    ;;
esac

_blogs
_pages
_sitemap