#!/bin/bash

# This script generates the following files:
#
# - long.c.txt
# - long_signed.c.txt
# - long_decimal.c.txt
# - long_signed_decimal.c.txt
# - hashes.cmake
#
# Run with --very-long to generate the following additional files:
#
# - very_long_params.cmake
#
# Any changes to those files should be made here, and then the script rerun.

set -euo pipefail

readonly dir="$(realpath "$(dirname "$0")")"
readonly jack_count="$(((1024 * 1024 * 1024 * 4 / 64) + 1))"
readonly jack_byte_count="$((jack_count * 64))"

generate_very_long=0

for arg in "${@}"; do
  case "$arg" in
    --very-long)
      generate_very_long=1
      ;;
  esac
done

random_bytes() {
  python3 -c '
import random
import sys

buf = random.Random(12345).randbytes(65536)
for i in range(256):
    assert i in buf

sys.stdout.buffer.write(buf)
'
}

bin2c() {
  od -vtx1 -Anone -w32 \
    | awk '{ print toupper($0) }' \
    | sed -E \
      -e 's/^ /  0x/g' \
      -e 's/([0-9A-F]) ([0-9A-F])/\1, 0x\2/g' \
      -e 's/$/,/g' \
      -e '$ s/,$//g'
}

bin2c_signed() {
  local sed_args=()
  local i
  for i in {128..255}; do
    sed_args+=(-e "$(python3 -c 'import sys; n = int(sys.argv[1]); print(f"s/ 0x{n:02X}/-0x{256-n:02X}/g")' "$i")")
  done
  bin2c | sed -E "${sed_args[@]}"
}

bin2c_decimal() {
  local sed_args=()
  local i
  for i in {0..255}; do
    sed_args+=(-e "$(python3 -c 'import sys; n = int(sys.argv[1]); print(f"s/ 0x{n:02X}/{n: 4}/g")' "$i")")
  done
  bin2c | sed -E "${sed_args[@]}"
}

bin2c_signed_decimal() {
  local sed_args=()
  local i
  for i in {0..127}; do
    sed_args+=(-e "$(python3 -c 'import sys; n = int(sys.argv[1]); print(f"s/ 0x{n:02X}/{n: 4}/g")' "$i")")
  done
  for i in {128..156}; do
    sed_args+=(-e "$(python3 -c 'import sys; n = int(sys.argv[1]); print(f"s/ 0x{n:02X}/-{256-n}/g")' "$i")")
  done
  for i in {157..246}; do
    sed_args+=(-e "$(python3 -c 'import sys; n = int(sys.argv[1]); print(f"s/ 0x{n:02X}/ -{256-n}/g")' "$i")")
  done
  for i in {247..255}; do
    sed_args+=(-e "$(python3 -c 'import sys; n = int(sys.argv[1]); print(f"s/ 0x{n:02X}/  -{256-n}/g")' "$i")")
  done
  bin2c | sed -E "${sed_args[@]}"
}

cmake_hash() {
  local name="$1"
  echo "  Computing ${name}_hash..." >&2
  local hash="$(sha256sum | cut -d' ' -f1)"
  echo "set(${name}_hash $hash)"
}

very_long_c() {
  local nl="$1"
  echo -n "static unsigned char const array[] = {$nl"
  python3 -c '
import sys

jack_count = int(sys.argv[1])
nl = sys.argv[2]

def jack(trailing_comma):
    sys.stdout.buffer.write(
        (
            "  "
            "0x20, 0x41, 0x6C, 0x6C, 0x20, 0x20, 0x20, 0x77, "
            "0x6F, 0x72, 0x6B, 0x20, 0x20, 0x20, 0x61, 0x6E, "
            "0x64, 0x20, 0x20, 0x20, 0x6E, 0x6F, 0x20, 0x20, "
            "0x20, 0x70, 0x6C, 0x61, 0x79, 0x20, 0x20, 0x20,"
            f"{nl}"
            "  "
            "0x6D, 0x61, 0x6B, 0x65, 0x73, 0x20, 0x20, 0x20, "
            "0x4A, 0x61, 0x63, 0x6B, 0x20, 0x20, 0x20, 0x61, "
            "0x20, 0x20, 0x20, 0x64, 0x75, 0x6C, 0x6C, 0x20, "
            f"0x20, 0x20, 0x62, 0x6F, 0x79, 0x2E, 0x20, 0x0A{trailing_comma}"
            f"{nl}"
        ).encode()
    )

for _ in range(jack_count - 1):
    jack(",")
jack("")
' "$jack_count" "$nl"
  echo -n "};$nl"
  echo -n "$nl"
  echo -n "static size_t const length = $jack_byte_count;$nl"
}

echo "Generating long.c.txt..." >&2
random_bytes | bin2c > "$dir/long.c.txt"

echo "Generating long_signed.c.txt..." >&2
random_bytes | bin2c_signed > "$dir/long_signed.c.txt"

echo "Generating long_decimal.c.txt..." >&2
random_bytes | bin2c_decimal > "$dir/long_decimal.c.txt"

echo "Generating long_signed_decimal.c.txt..." >&2
random_bytes | bin2c_signed_decimal > "$dir/long_signed_decimal.c.txt"

echo "Generating hashes.cmake..." >&2
(
  echo -en '# Automatically generated by generate_files.sh. Do not edit!\n\n'
  echo -en '\xFC\xFD\xFE\xFF\x00\x01\x02\x03' | cmake_hash basic
  echo -en '' | cmake_hash empty
  echo -en 'All work and no play makes Jack a dull boy.\n' | cmake_hash text_lf
  echo -en 'All work and no play makes Jack a dull boy.\r\n' | cmake_hash text_crlf
  echo -en 'This exactly 32 characters long!' | cmake_hash text_align
  random_bytes | cmake_hash long
  cat "$dir/long.c.txt" | cmake_hash long_c_lf
  cat "$dir/long.c.txt" | unix2dos | cmake_hash long_c_crlf
  cat "$dir/long_signed.c.txt" | cmake_hash long_signed_c_lf
  cat "$dir/long_signed.c.txt" | unix2dos | cmake_hash long_signed_c_crlf
  cat "$dir/long_decimal.c.txt" | cmake_hash long_decimal_c_lf
  cat "$dir/long_decimal.c.txt" | unix2dos | cmake_hash long_decimal_c_crlf
  cat "$dir/long_signed_decimal.c.txt" | cmake_hash long_signed_decimal_c_lf
  cat "$dir/long_signed_decimal.c.txt" | unix2dos | cmake_hash long_signed_decimal_c_crlf
) > "$dir/hashes.cmake"

if [[ "$generate_very_long" == 1 ]]; then
  echo "Generating very_long_params.cmake..." >&2
  (
    echo -en '# Automatically generated by generate_files.sh. Do not edit!\n\n'
    echo "set(jack_count $jack_count)"
    python3 -c '
import sys

for _ in range(int(sys.argv[1])):
    sys.stdout.buffer.write(b" All   work   and   no   play   makes   Jack   a   dull   boy. \n")
' "$jack_count" | cmake_hash very_long
    very_long_c $'\n' | cmake_hash very_long_c_lf
    very_long_c $'\r\n' | cmake_hash very_long_c_crlf
  ) > "$dir/very_long_params.cmake"
fi
