#! /bin/sh -

#
# This file is licensed under the MIT No Attribution license.
#

#
# By default, files are not indexed, and directories are indexed and
# recursed into for further indexing. To index a file, add it to the
# "files" file in the same directory. To prevent a directory from being
# indexed and recursed into, add it to the "ignore" file in the parent
# directory. To index a directory but prevent it from being recursed
# into, add it to the "skip" file in the parent directory.
#

set -e || exit $?
. ./prelude.bash

readonly quinngrier=quinngrier

#-----------------------------------------------------------------------

step() {

  declare    current
  declare    d
  declare    h
  declare    ignore
  declare    lastmod
  declare    reverse
  declare    root
  declare    sitemap_end
  declare    sitemap_start
  declare    skip
  declare    title
  declare    x
  declare    xs

  root=${1-./}
  readonly root

  current=${2-./}
  readonly current

  if [[ "$current" == ./ ]]; then
    x=$(git status --ignored --short)
    if [[ "$x" ]]; then
      printf '%s\n' "Repository is not clean." >&2
      exit 1
    fi
  fi

  title="/${current#./} | Quinn's Manuals Archive"
  title=${title#/ | }
  readonly title

  >index.html

  cat <<EOF >>index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!--
This file is licensed under the MIT No Attribution license.
-->
<title>$title</title>
<style>

:root {
  --background_color: #000;
  --line_height: 1.2em;
  --text_color: #CCC;
  --tree_border_color: #333;
  --tree_border_size: 2px;
  --tree_border_triple: var(--tree_border_size) solid var(--tree_border_color);
}

@media (prefers-color-scheme: light) {
  :root {
    --background_color: #FFF;
    --text_color: #333;
    --tree_border_color: #CCC;
  }
}

body {
  background: var(--background_color);
  color: var(--text_color);
  font-family: monospace;
  font-size: 20px;
  line-height: var(--line_height);
  margin: 1em;
}

h1 {
  font-size: 1em;
}

a {
  color: var(--text_color);
  text-decoration: none;
  text-decoration-skip-ink: none;
}

a:hover {
  text-decoration: underline;
}

ol {
  list-style-type: none;
  margin: 0 0 0 calc(0.5ch - var(--tree_border_size) / 2);
  padding: 0;
  position: relative;
}

li:first-child {
  border-left: var(--tree_border_triple);
  display: block;
  height: calc(100% - var(--line_height) / 2);
  left: 0;
  position: absolute;
  right: 100%;
  top: 0;
}

li:not(:first-child)::before {
  border-top: var(--tree_border_triple);
  content: "";
  display: inline-block;
  left: 0;
  margin-right: 0.25ch;
  position: relative;
  transform: translateY(calc((var(--line_height) - var(--tree_border_size)) / 2));
  vertical-align: top;
  width: calc(1.25ch + var(--tree_border_size) / 2);
}

</style>
</head>
<body>
EOF

  h=
  d=./
  x=$current
  while [[ $x != ./ ]]; do
    x=${x%/}
    h="<a href=\"$d\">${x##*/}/</a>$h"
    d+=../
    d=${d#./}
    x=${x%/*}/
  done
  h="<a href=\"$d\">/</a>$h"

  cat <<EOF >>index.html
<h1><a href="$d">Quinn's Manuals Archive</a></h1>
$h
<ol>
<li></li>
EOF

  if [[ -f ignore ]]; then
    ignore=' '$(
      sed $'/^[\t ]*#/ d' <ignore | tr '\t\n\v\f\r' '[ *]'
    )' '
  else
    ignore=
  fi
  readonly ignore

  if [[ -f reverse ]]; then
    reverse=-r
  else
    reverse=
  fi
  readonly reverse

  xs=$(
    for x in */; do
      if [[ $x == .* || "$ignore" == *" $x "* ]]; then
        continue
      fi
      x=${x%/}
      if [[ -f $x/date ]]; then
        d=$(cat $x/date)
      else
        d=0000-00-00
      fi
      printf '%s\n' "$d $x"
    done | sort $reverse | cut -d ' ' -f 2
  )

  for x in $xs; do
    cat <<EOF >>index.html
<li><a href="$x/">$x/</a></li>
EOF
  done

  if [[ -f files ]]; then
    xs=$(cat files)
    for x in $xs; do
      cat <<EOF >>index.html
<li><a href="$x">$x</a></li>
EOF
    done
  fi

  cat <<'EOF' >>index.html
</ol>
</body>
</html>
EOF

  if [[ -f skip ]]; then
    skip=' '$(
      sed $'/^[\t ]*#/ d' <skip | tr '\t\n\v\f\r' '[ *]'
    )' '
  else
    skip=
  fi
  readonly skip

  for d in */; do
    if [[ $d == .* || "$ignore $skip" == *" $d "* ]]; then
      continue
    fi
    (
      cd $d
      step ../$root $current$d
    )
  done

  #---------------------------------------------------------------------

  if [[ $current == ./ ]]; then

    sitemap_start='<?xml version="1.0" encoding="UTF-8"?>

<!--
This file is licensed under the MIT No Attribution license.
-->

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
'
    readonly sitemap_start

    sitemap_end='
</urlset>
'
    readonly sitemap_end

    make -s set_timestamps

    (

      git log --date=unix --format=@%cd --name-only | awk '
        {
          if (/^@/) {
            lastmod = substr($0, 2);
          } else if (/^./ && !seen[$0]) {
            print $0, lastmod;
            seen[$0] = 1;
          }
        }
      ' | ./set_timestamps

      rm set_timestamps

      skip_git='( ! -name .git -o -prune )'

      xs=$(find . $skip_git -type l)
      for x in $xs; do
        cp -L -R -p "$x" flatten.tmp
        rm -f -r "$x"
        mv flatten.tmp "$x"
      done

      f='%p %TY-%Tm-%TdT%TH:%TM:%TS\n'
      for x in './sitemap*' .; do
        TZ=UTC find $x $skip_git -type f -printf "$f" | sort
      done
      echo @EOF

    ) | (

      declare    file
      declare    i
      declare    lastmod
      declare    max_bytes
      declare    max_entries
      declare    n_bytes
      declare    n_entries
      declare -A sitemap_lastmod
      declare    urlstr

      max_bytes=49000000
      readonly max_bytes

      max_entries=49000
      readonly max_entries

      exec 3>sitemap.xml

      printf %s '<?xml version="1.0" encoding="UTF-8"?>

<!--
This file is licensed under the MIT No Attribution license.
-->

<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
' >&3

      i=0
      n_bytes=$max_bytes
      n_entries=0

      while IFS= read -r file; do
        lastmod=${file##* }
        file=${file% *}
        if [[ "$file" == @EOF ]]; then
          break
        fi
        file=${file:2}
        lastmod=${lastmod%.*}+00:00
        if [[ "$file" == sitemap* ]]; then
          sitemap_lastmod[$file]=$lastmod
          continue
        fi
        if ((n_bytes >= max_bytes || n_entries >= max_entries)); then
          if ((i > 0)); then
            printf %s "$sitemap_end" >&4
            exec 4>&-
            printf %s "
  <sitemap>
    <loc>https://manuals.$quinngrier.com/sitemap-$i.xml</loc>
    <lastmod>${sitemap_lastmod[sitemap-$i.xml]-}</lastmod>
  </sitemap>
" >&3
          fi
          ((++i))
          n_bytes=$((${#sitemap_start} + ${#sitemap_end}))
          n_entries=0
          exec 4>sitemap-$i.xml
          printf %s "$sitemap_start" >&4
        fi
        urlstr="
  <url>
    <loc>https://manuals.$quinngrier.com/$file</loc>
    <lastmod>$lastmod</lastmod>
  </url>
"
        ((n_bytes += ${#urlstr}))
        ((++n_entries))
        printf %s "$urlstr" >&4
      done
      if [[ "$file" != @EOF ]]; then
        exit 1
      fi

      if ((i > 0)); then
        printf %s "$sitemap_end" >&4
        exec 4>&-
        printf %s "
  <sitemap>
    <loc>https://manuals.$quinngrier.com/sitemap-$i.xml</loc>
    <lastmod>${sitemap_lastmod[sitemap-$i.xml]-}</lastmod>
  </sitemap>
" >&3
      fi

      printf %s '
</sitemapindex>
' >&3

      exec 3>&-

    )

    git add -u index.html '*/index.html' 'sitemap*'
    git add [!.]*/**/index.html sitemap*
    git checkout -q .
    git clean -dffxq

  fi

  #---------------------------------------------------------------------

}; readonly -f step

#-----------------------------------------------------------------------

main() {

  declare    x

  x=$(git status --ignored --short)
  if [[ "$x" ]]; then
    printf '%s\n' "Repository is not clean." >&2
    exit 1
  fi

  while :; do

    step

    x=$(git status --ignored --short)
    if [[ ! "$x" ]]; then
      break
    fi

    git add .
    git commit -m 'Run ./index'

  done

}; readonly -f main

#-----------------------------------------------------------------------

main "$@"
