#!/bin/sh


# In order to use this script, you will need to:
# 1. install 'curl' and 'jq',
# 2. fill in the following variables.

USERID=           # for instance, my userId is 512097
MAIN_DIR=         # full path of the directory where the files will be saved


# You may want to modify the filenames used for the different languages.
# You can comment a line if you don't want to back up your codes in the corresponding language.

getFileName() {
    case "$1" in
        "Bash")       echo 'main.sh'      ;;
        "C")          echo 'main.c'       ;;
        "C#")         echo 'main.cs'      ;;
        "C++")        echo 'main.cpp'     ;;
        "Clojure")    echo 'main.clj'     ;;
        "D")          echo 'main.d'       ;;
        "Dart")       echo 'main.dart'    ;;
        "F#")         echo 'main.fs'      ;;
        "Go")         echo 'main.go'      ;;
        "Groovy")     echo 'main.groovy'  ;;
        "Haskell")    echo 'main.hs'      ;;
        "Java")       echo 'main.java'    ;;
        "Javascript") echo 'main.js'      ;;
        "Kotlin")     echo 'main.kt'      ;;
        "Lua")        echo 'main.lua'     ;;
        "OCaml")      echo 'main.ml'      ;;
        "ObjectiveC") echo 'main.C'       ;;
        "PHP")        echo 'main.php'     ;;
        "Pascal")     echo 'main.pas'     ;;
        "Perl")       echo 'main.pl'      ;;
        "Python")     echo 'legacy.py '   ;;  # Python2 is not available anymore on CG
        "Python3")    echo 'main.py'      ;;
        "Ruby")       echo 'main.rb'      ;;
        "Rust")       echo 'main.rs'      ;;
        "Scala")      echo 'main.scala'   ;;
        "Swift")      echo 'main.swift'   ;;
        "Swift3")     echo 'legacy.swift' ;;  # Swift3 is not available anymore on CG
        "TypeScript") echo 'main.ts'      ;;
        "VB.NET")     echo 'main.vb'      ;;
    esac
}


# You should not need to modify this script past this point.

LAST_BACKUP_FILE=$MAIN_DIR/last_backup.txt
MIN_SUBMIT_ID=0

init() {
    if [ -z "$USERID" ] ; then
        echo >&2 "You must fill in a userId at the top of this script."
        exit 1
    fi

    if [ -z "$MAIN_DIR" ] ; then
        echo >&2 "You must fill in MAIN_DIR at the top of this script."
        exit 2
    fi

    if [ -e "$MAIN_DIR" ] ; then
        if [ -f "$LAST_BACKUP_FILE" ] ; then
            echo >&2 "Let's update the backup found at '$MAIN_DIR'."
            MIN_SUBMIT_ID=$(cat $LAST_BACKUP_FILE)
        else
            echo >&2 "'$MAIN_DIR' already exists, aborting."
            exit 3
        fi
    else
        mkdir -pv $MAIN_DIR
        echo 0 > $LAST_BACKUP_FILE
    fi

    if [ ! -e "$MAIN_DIR" ] ; then
        echo >&2 "Cannot create '$MAIN_DIR', aborting."
        exit 4
    fi
}

askCookie() {
    read -p 'rememberMe: ' REMEMBER_ME
    read -p 'cgSession:  ' CG_SESSION
    COOKIE="rememberMe=$REMEMBER_ME; cgSession=$CG_SESSION"
}

checkCookie() {
    curl 'https://www.codingame.com/services/Puzzle/generateSessionFromPuzzlePrettyId' \
        -s -X POST -H 'Content-Type: application/json;charset=utf-8' \
        -H "Cookie: $COOKIE" -d "[$USERID,\"onboarding\",false]" \
    | grep -q -e 'Oops' && echo>&2 "Invalid cookie data, aborting." && exit 5
    sleep 5
}

getPuzzleIdsByCategory() {
    curl 'https://www.codingame.com/services/Puzzle/findAllMinimalProgress' \
        -s -X POST -H 'Content-Type: application/json;charset=utf-8' \
        -H "Cookie: $COOKIE" -d "[$USERID]" \
    | jq -c -r 'group_by(.level,.communityCreation) | .[]
                | "\(.[0].level) \(.[0].communityCreation) \(map(.id))"'
}

generateSessionFromPuzzlePrettyId() {
    curl 'https://www.codingame.com/services/Puzzle/generateSessionFromPuzzlePrettyId' \
        -s -X POST -H 'Content-Type: application/json;charset=utf-8' \
        -H "Cookie: $COOKIE" -d "[$USERID,\"$1\",false]" \
    | jq -r .handle
}

getPrettyIdsFromPuzzleIdList() {
    curl 'https://www.codingame.com/services/Puzzle/findProgressByIds' \
        -s -X POST -H 'Content-Type: application/json;charset=utf-8' \
        -H "Cookie: $COOKIE" -d "[$1,$USERID,2]" \
    | jq -r 'map(.prettyId) | .[]'
}

findAllSubmissions() {
    curl 'https://www.codingame.com/services/TestSessionQuestionSubmission/findAllSubmissions' \
        -s -X POST -H 'Content-Type: application/json;charset=utf-8' \
        -H "Cookie: $COOKIE" -d "[\"$1\"]" \
    | jq -r 'if .[0]?|has("score") then map(select(.score == 100)) else . end
             | group_by(.programmingLanguageId) | map(min_by(.elapsedTime))
             | .[] | [.programmingLanguageId, .testSessionQuestionSubmissionId]
             | @tsv'
}

retrieveCode() {
    curl 'https://www.codingame.com/services/TestSessionQuestionSubmission/findSubmission' \
        -s -X POST -H 'Content-Type: application/json;charset=utf-8' \
        -H "Cookie: $COOKIE" -d "[$1]" \
    | jq -r '.code' > $2
}

handlePuzzleByPrettyId() {
    DIR=$1
    PRETTY_ID=$2

    mkdir -pv $DIR/$PRETTY_ID

    findAllSubmissions $(generateSessionFromPuzzlePrettyId $PRETTY_ID) \
    | while read LANGUAGE SUBMIT_ID ; do
        FILENAME=$(getFileName $LANGUAGE)
        if [ -n "$FILENAME" ] ; then
            if [ $SUBMIT_ID -le $MIN_SUBMIT_ID -a -f "$DIR/$PRETTY_ID/$FILENAME" ] ; then
                echo >&2 "[$PRETTY_ID] $LANGUAGE $SUBMIT_ID ... no update needed."
            else
                echo >&2 -n "[$PRETTY_ID] $LANGUAGE $SUBMIT_ID ..."
                retrieveCode $SUBMIT_ID "$DIR/$PRETTY_ID/$FILENAME"
                sleep 1
                echo >&2 " done"
                NEW_MIN_SUBMIT_ID=$(cat $LAST_BACKUP_FILE)
                if [ $NEW_MIN_SUBMIT_ID -le $SUBMIT_ID ] ; then
                    echo $SUBMIT_ID > $LAST_BACKUP_FILE
                fi
            fi
        else
            echo >&2 "[$PRETTY_ID_FILE] WARNING: $LANGUAGE $SUBMIT_ID skipped (language unknown)"
        fi
      done
}

handleCategory() {
    LEVEL=$1
    COMMU=$2
    PUZZLE_IDS=$3

    DIR=$MAIN_DIR/$LEVEL/CG
    if [ "$COMMU" = "true" ] ; then
        DIR=$MAIN_DIR/$LEVEL/Community
    fi
    mkdir -pv $DIR

    getPrettyIdsFromPuzzleIdList $PUZZLE_IDS \
    | while read PRETTY_ID ; do
        handlePuzzleByPrettyId $DIR $PRETTY_ID
        sleep 3
      done
}



init
askCookie
checkCookie

getPuzzleIdsByCategory \
| while read LEVEL COMMU IDS ; do
    handleCategory $LEVEL $COMMU $IDS
  done

