#!/usr/bin/env bash
# Copyright 2014 Vladimir Ivanov <ivvl82@gmail.com>
# Distributed under the terms of the GNU General Public License v2

# Specify the tournament here
TOURNAMENT=

# Version information
VERSION="0.2"

argv0=${0##*/}

function usage {
    cat <<EOF
Store a chess game played on lichess.org

Usage:
  $argv0 -t <num> <url>
  $argv0 -h
  $argv0 -v

The first form fills the result of a chess game and stores its PGN
file. Assumes that the game is available at <url> (lichess.org) and
corresponds to tournament tour <num>.

The second form shows this help output. The third form shows version
information.
EOF

    exit "${1:-0}"
}

function version {
    exec echo "${argv0}-${VERSION}"
}

function game_setup {
    REPO_DIR=`dirname "$0"`
    # Convert REPO_DIR to an absolute path
    REPO_DIR=$(cd ${REPO_DIR}; pwd)

    # If no tournament given, set it to the last one
    if [[ -z $TOURNAMENT ]]; then
        local year_dir=$(ls -1 -d ${REPO_DIR}/[0-9][0-9][0-9][0-9]/ | tail -1)
        TOURNAMENT=$(ls -1 -d ${year_dir}[0-9]-*/ | tail -1 \
            | sed -E "s|${REPO_DIR}/(.*)|\1|")
        # Remove the trailing slash
        TOURNAMENT=${TOURNAMENT%/}
    fi

    # Configuration file for players
    ply_ini=${REPO_DIR}/${TOURNAMENT}/players.ini
}

function game_get_players {
    # Extract players on Lichess
    local w_lichess=$(sed -En "s/\[White \"([^\"]*)\"\]/\1/p" < $tmp_pgn)
    local b_lichess=$(sed -En "s/\[Black \"([^\"]*)\"\]/\1/p" < $tmp_pgn)

    # Get names of white and black players
    local counter=1 players=()
    game_parse_config
    while eval "config_section_player${counter}" 2>/dev/null; do
        players+=("$name")
        [[ $lichess == $w_lichess ]] && white=$name
        [[ $lichess == $b_lichess ]] && black=$name
        : $((counter += 1))
    done
    [[ -z $white ]] && fix_white_player
    [[ -z $black ]] && fix_black_player
}

function game_parse_config {
    # Copy player INI file to the temporary location
    # NOTE: an empty line is added to the file beginning in order to
    # match the only first occurrence for non-GNU sed
    echo > $tmp_ini
    cat "$ply_ini" >> $tmp_ini

    # Remove tabs or spaces around the `='
    sed -E -i.prev "s/[[:blank:]]*=[[:blank:]]*/=/" "$tmp_ini"

    # Transform section labels into function declaration
    sed -E -i.prev "1,/^\[.*\]/s/^\[([^]]*)\]/config_section_\1() {/" "$tmp_ini"
    sed -E -i.prev "s/^\[([^]]*)\]/}\\"$'\n'"config_section_\1() {/" "$tmp_ini"
    echo -e "\n}" >> $tmp_ini

    # Source the file
    source "$tmp_ini"
}

function fix_white_player {
    local number
    echo "Lichess player '${w_lichess}' not found."
    choose_player
    white=${players[$number]}
}

function fix_black_player {
    local number
    echo "Lichess player '${b_lichess}' not found."
    choose_player
    black=${players[$number]}
}

function choose_player {
    echo "Please choose a proper name from the list below:"
    for ((i=0; i<${#players[@]}; ++i)); do
        echo "$((i+1)) ${players[$i]}"
    done \
        | column -t \
        | sed -E "s/^([0-9]*)/$(tput setaf 6)\1$(tput sgr0)/" # highlight number
    echo -n "Put number> "
    read number

    if (( $number < 1 || $number > ${#players[@]} )); then
        die "Incorrect player number."
    fi
    : $((number += -1))
}

function game_add_to_repo {
    local date_re="[0-9?]{2}\.[0-9?]{2}\.[0-9?]{4}"
    # Check if the tour number is correct
    local correct=false length_max=0
    while IFS= read line; do
        if [[ $line =~ ^(${date_re}\ +([^\ ]+)\ +-\ +([^\ ]+)) ]]; then
            if [[ ${BASH_REMATCH[2]} == $white && ${BASH_REMATCH[3]} == $black ]]; then
                correct=true
                local length_rec=${#BASH_REMATCH[1]}
            fi
            # Determine the maximal length of game record
            local length=${#BASH_REMATCH[1]}
            (( $length > $length_max )) && length_max=$length
        fi
    done < $tour_info
    if ! $correct; then
        die "Game '${white} vs. ${black}' not found in ${tour_info}."
    fi

    [[ -d $pgn_dir ]] && die "Directory ${pgn_dir} already exist."
    echo "Creating directory ${pgn_dir}..."
    mkdir -p "$pgn_dir"

    echo "Storing PGN file..."
    cp "$tmp_pgn" "${pgn_dir}/1.pgn"

    echo "Updating tour_info..."
    local game_date=${pgn_date:8:2}.${pgn_date:5:2}.${pgn_date::4}
    local result=$(sed -En "s/\[Result \"([^\"]*)\"\]/\1/p" < $tmp_pgn)
    local spaces=$((length_max - length_rec + 1))
    local sep=$(printf "%${spaces}s" " ")

    sed -E -i.orig \
        "s/${date_re}( +${white} +- +${black})/${game_date}\1${sep}${result}/" "$tour_info"
    rm "${tour_info}.orig"
}

function game_git_commit {
    cd $REPO_DIR
    git add "${pgn_dir}/1.pgn" "$tour_info"
    git commit -m "Tour ${TOUR#0}: ${white} vs. ${black}."
    git push
}

function die {
    echo "$@" 1>&2
    exit 1
}

function checkargs {
    if [[ $opt == t && $OPTARG =~ ^[0-9]+$ ]]; then
        TOUR=$(printf "%02g" $OPTARG)
    fi
}

while getopts t:hv opt; do
    case $opt in
        t) checkargs ;;
        h) usage     ;;
        v) version   ;;
        *) usage 1   ;;
    esac
done

shift $(($OPTIND - 1))
# For now, tour number should be given explicitly
[[ -z $TOUR || -z $1 ]] && usage 1

game_setup
tour_info=${REPO_DIR}/${TOURNAMENT}/tours/${TOUR}/tour_info
[[ ! -f $tour_info ]] && die "File ${tour_info} not found."

# Ensure that the repository is up-to-date
git pull

# Temporary files
tmp_ini=$(mktemp -t `basename $ply_ini`.XXXXXX)
tmp_pgn=$(mktemp -t pgn.XXXXXX)
trap "rm ${tmp_ini} ${tmp_ini}.prev ${tmp_pgn}" EXIT

# Download PGN file
[[ $1 =~ ^(http://[^/]*)/([^/]*) ]]
pgn_url=${BASH_REMATCH[1]}/${BASH_REMATCH[2]::8}/pgn
curl -q --fail --location --silent "$pgn_url" > $tmp_pgn \
    || die "PGN file not found."

game_get_players

pgn_date=$(sed -En "s/\[Date \"([^\"]*)\"\]/\1/p" < "$tmp_pgn")
pgn_date=$(tr "." "-" <<< "$pgn_date")
pgn_dir="${REPO_DIR}/${TOURNAMENT}/tours/${TOUR}/${pgn_date}-${white}-vs-${black}"

game_add_to_repo
game_git_commit

exit 0