#! /bin/bash

set -eo pipefail

VERSION="2.0.4"

# Sane defaults in case of not being set in the config / config not existing.
CREDENTIALS_FILE="$HOME/.config/slip/credentials"
RECORD_PIDFILE="/tmp/slip_record.pid"
IMAGES="$HOME/Pictures"
VIDEOS="$HOME/Videos"
GIF_SCALE="640"
GIF_FPS=15
SOUND=0
NOUPLOAD=0

DMENU_CMD="rofi -dmenu -p slip"

# Load config.
CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/slip/config"
if [ -f $CONFIG ]; then
    source $CONFIG
else
    echo "No config was found - using default options.  Please copy the example configuration to ~/.config/slip/config from https://github.com/Toqozz/slip"
fi

CLIENT_ID="abd3a90bbfb65e9"

# Dmenu prompts.
DMENU_OPTS="screenshot
video
gif
exit"
DMENU_RECORD_OPTS="stop
cancel"
DMENU_UPLOAD_OPTS_BIG="upload (gfycat)
delete
exit"
DMENU_UPLOAD_OPTS="upload (imgur)
upload (gfycat)
delete
exit"

# Maximum size for upload (kb).
# Gfycat does not have a file limit.
MAX_GIF_SIZE_IMGUR=10000

function usage() {
    echo ""
    echo "  slip [ -h | -v | -s | -r | -a | -q ] -nu"
    echo "      Uploads images taken with ffmpeg via slop to imgur.  Quick video recording."
    echo "      That's all."
    echo ""
    echo "      -h  | --help         show this help"
    echo "      -v  | --version      show version"
    echo "      -s  | --screenshot   take a screenshot (skip dmenu)"
    echo "      -g  | --gif          take a gif (skip dmenu)"
    echo "      -r  | --record       take a video (skip dmenu)"
    echo "      -nu | --no-upload   don't upload the result"
    echo "      -q  | --stop         stop recording a video / gif (skip dmenu) (only when recording)"
    echo ""
}

function upload_imgur() {
    file="$1"

    if [ -f "$file" ]; then
        if [ -f "$CREDENTIALS_FILE" ]; then
            # We have an auth token to use.
            check_auth_credentials
            if [ $? -ne 1 ]; then
                # Token valid.
                curl -sH "Authorization: Bearer ${access_token}" -F "image=@$file" "https://api.imgur.com/3/upload"
            fi
        else
            # Imgur needs to know what program is using its services.
            curl -sH "Authorization: Client-ID ${CLIENT_ID}" -F "image=@$file" "https://api.imgur.com/3/upload"
        fi
    else
        echo "File does not exist, what happened?"
    fi
}

function upload_gfycat() {
    local file="$1"

    local result=$(curl -s -XPOST https://api.gfycat.com/v1/gfycats -H "Content-Type: application/json")
    local gfyname=$(parse "gfycat" $result)
    curl -s "https://filedrop.gfycat.com/$gfyname" --upload-file "$file"

    # No new line so we can append it easily.
    echo -n "$gfyname"
}

function check_auth_credentials() {
    token_expire_time=0
    if [ -f "${CREDENTIALS_FILE}" ]; then
        source "${CREDENTIALS_FILE}"
    fi

    if [ ! -z ${access_token} ]; then
        current_time="$(date +%s)"
        preemptive_refresh_time="$((10*60))"
        expired="$((current_time > (token_expire_time - preemptive_refresh_time)))"

        if [ "${expired}" -eq "0" ]; then
            #token has expired, get a new one.
            echo 'The authorization token has expired, please get a new one.'
            return 1;
        fi
    else
        return 1;
    fi
}

function clip_clear() {
    # Clear x cliboard selection.
    xsel -bc    # Ctrl-v / Shift-Insert.
    # xsel -pc  # Middle click.
}

# Run slop and get the geometry line.
function slop_geom() {
    if [ "$1" = "image" ]; then
        slop -f '%wx%h+%x+%y'
    else
        slop -f '%wx%h|%x,%y' # | sed -e s/+/\|/ -e s/+/,/
    fi
    #elif [ "$1" = "video" ]; then
        #slop -f '%wx%h+%x+%y' | sed -e s/+/\|/ -e s/+/,/
        #slop | sed -n 5p | sed -e s/G=// -e s/+/\|/ -e s/+/,/
    #elif [ "$1" = "gif" ]; then
        #slop -f '%wx%h+%x+%y' | sed -e s/+/\|/ -e s/+/,/
}

# Take the shot (or start the video.)
function shot() {
    if [ "$1" = "image" ]; then
        extension=".png"    # img-2016-04-16-153906.png
        filename="$IMAGES/img-`date +%Y-%m-%d-%H%M%S`$extension"    # .png, .jpg
        maim -g "$2" $filename
        # TODO, do we want more dependencies? (optional dependencies?)
        #ffmpeg -f x11grab -video_size "$2" -i "$DISPLAY+$3" -vframes 1 $filename &> /dev/null
    elif [ "$1" = "video" ]; then
        extension=".mkv"    # vid-2016-04-16-153906.mkv
        filename="$VIDEOS/vid-`date +%Y-%m-%d-%H%M%S`$extension"    # .mkv, .mp4
        if [ $SOUND == 0 ]; then
            ffmpeg -f x11grab -video_size "$2" -framerate 60 -i "$DISPLAY+$3" -loglevel quiet -c:v libx264 -preset ultrafast $filename  &
        else
            ffmpeg -f pulse -i 1 -f x11grab -video_size "$2" -framerate 60 -i "$DISPLAY+$3" -loglevel quiet -c:v libx264 -preset ultrafast $filename &
        fi
        echo "$! vid $filename" > "$RECORD_PIDFILE"
    elif [ "$1" = "gif" ]; then
        extension=".gif"    # gif-2016-04-16-153906.gif
        tmpfilename="/tmp/gif-`date +%Y-%m-%d-%H%M%S-tmp`.mkv"
        filename="$VIDEOS/gif-`date +%Y-%m-%d-%H%M%S`$extension"    # .gif
        # Record a video to be converted to gif.
        ffmpeg -f x11grab -video_size "$2" -framerate $GIF_FPS -i "$DISPLAY+$3" -loglevel quiet -c:v libx264 -preset ultrafast $tmpfilename &
        #ffmpeg -f x11grab -video_size "$2" -framerate 15 -i "$DISPLAY+$3" -vf scale="$GIF_SCALE:-1" $filename &> /dev/null &

        # We need to access this information later.
        ratio=$(awk -F"x" '{ print int($1/$2) }' <<< "$2") # We only really need a binary answer.
        if [ "$ratio" -ge 1 ]; then echo "$! gif $tmpfilename $filename 0" > "$RECORD_PIDFILE"
        else echo "$! gif $tmpfilename $filename 1" > "$RECORD_PIDFILE"
        fi
    fi
}

function kill_ffmpeg() {
    # Kill ffmpeg (stopping the recording.)
    kill $1 || echo "Failed to kill ffmpeg, did it crash?  Removing $RECORD_PIDFILE anyway."
    # Remove the pid file so that slip can be used as normal again.
    rm "$RECORD_PIDFILE"
}

function convert_to_gif() {
    local tfn=$1
    local pfn=$2
    local fn=$3
    local wh=$4 # 0 = width larger than height, 1 = height larger than width

    local ratio
    # If the width is larger than the height, we want to scale with the width, otherwise scale with the height.
    if [ $wh -eq 0 ]; then
        ratio="$GIF_SCALE:-1"
    else
        ratio="-1:$GIF_SCALE"
    fi

    notify "Converting to gif…"
    # Give enough time to save the file.
    sleep 1 && ffmpeg -i "$tfn" -loglevel quiet -vf fps=$GIF_FPS,scale="$ratio":flags=lanczos,palettegen "$pfn" &&
    ffmpeg -i "$tfn" -i "$pfn" -loglevel quiet -filter_complex "fps=$GIF_FPS,scale=$ratio:flags=lanczos[x];[x][1:v]paletteuse" "$fn" &&

    echo "$fn"
}

function stop_rec() {
    local choice

    local pid=$1                      # Process id (for killing ffmpeg).
    local tfn=$3                      # Temp file name (for gifs).
    local fn=$4                       # File name for the gif/vid we're saving.
    local pfn="${tfn}-palette.png"    # Palette file name (for gif rendering).

    # Stop recording.
    kill_ffmpeg "$pid"

    # When processing a gif, we canvert it straight away and then upload.
    # NOTE: gfycat does not require the video to be converted, but we convert it anyway to store
    #   the .gif file.
    # Is this actually a good idea?  We could just store the .mkv instead, but then we lose the ability to store .gif.
    if [ "$2" = "gif" ]; then
        fn=$(convert_to_gif "$tfn" "$pfn" "$fn" "$5")
        size=$(du -k $fn | cut -f1)

        if [ $NOUPLOAD == 0 ]; then
            if [ "$size" -lt "${MAX_GIF_SIZE_IMGUR}" ]; then
                choice=$($DMENU_CMD <<< $DMENU_UPLOAD_OPTS)
            else
                choice=$($DMENU_CMD <<< $DMENU_UPLOAD_OPTS_BIG)
            fi

            if [ "$choice" = "upload (gfycat)" ]; then
                clip_clear
                name=$(upload_gfycat "$tfn")
                url="https://gfycat.com/$name"
                notify "Uploading to $url ..."
                echo "$url"
                echo -n "$url" | xsel -bi  # Read to clipboard.
            elif [ "$choice" = "upload (imgur)" ]; then
                clip_clear
                output=$(upload_imgur "$fn")
                url=$(parse "imgur" "$output")
                notify "$url"
                echo "$url"
                echo -n "$url" | xsel -bi  # Read to clipboard.
            elif [ "$choice" = "delete" ]; then
                rm $fn
            fi
        fi
    else
        notify "$fn"
    fi
}

# Parse x,y -- but also imgur.
function parse() {
    if [ "$1" = "geometryx" ]; then
        awk -F"|" '{ print $1 }' <<< "$2" 
    elif [ "$1" = "geometry+" ]; then
        awk -F"|" '{ print $2 }' <<< "$2"
    elif [ "$1" = "imgur" ]; then
        jq -r ".data.link" <<< "$2"
        #sed -e 's/.*\"link\":"\([^"]*\).*/\1/' -e 's/\\//g' <<< "$2"
        #sed -e 's/\"link\":"\([^"]*\)/\1/' -e 's/\\//g' <<< "$2"
    elif [ "$1" = "gfycat" ]; then
        jq -r ".gfyname" <<< "$2"
    fi
}

function notify() {
    notify-send -t 7000 "slip" "$1"
}

function main() {
    if [ "$1" = "screenshot" ]; then
        # Run slop and get geometry from it.
        # maim naturally supports slop's output coordinates.
        geometry=$(slop_geom "image")
        # Take the shot.
        shot "image" "$geometry"
        # Parse imgur json into link.

        # If noupload is not set.
        if [ $NOUPLOAD == 0 ]; then
            #echo "uploading..."
            # Clear cliboard before doing anything, so we can copy link to it later.
            clip_clear
            output=$(upload_imgur "$filename")
            url=$(parse "imgur" "$output")
            # Notify user that upload has finished.
            notify "$url"
            echo "$url"
            # Read to clipboard, removing trailing newline.
            echo -n "$url" | xsel -bi
        else
            notify "$filename"
        fi

        # echo "$url" | xsel -pi  # Read to primary.
    elif [ "$1" = "video" ]; then
        geometry=$(slop_geom "video")
        wxh=$(parse "geometryx" $geometry)
        off=$(parse "geometry+" $geometry)
        shot "video" "$wxh" "$off"
    elif [ "$1" = "gif" ]; then
        geometry=$(slop_geom "gif")
        wxh=$(parse "geometryx" $geometry)
        off=$(parse "geometry+" $geometry)
        shot "gif" "$wxh" "$off"
    elif [ "$1" = "stop" ]; then
        # Get info of recording process.
        local info
        # Get infor from the pidfile.
        info=$(cat "$RECORD_PIDFILE")
        # Stop with parameters ("<pid> <type> <filename> <ratio>")
        stop_rec $info
        exit 0
    else
        exit 0
    fi
}

# Dependencies.
depends="curl
jq
maim
slop
ffmpeg"
while read line
do
    if ! type $line &> /dev/null ; then
        echo "$line not found, expect unexpected or breakage."
    fi
done <<< "$depends"

# Main.
# Yes, we could call this stuff with integers, but this is much clearer.
if [ "$1" = "-nu" -o "$1" = "--no-upload" -o "$2" = "-nu" -o "$2" = "--no-upload" ]; then
    NOUPLOAD=1
fi

if [ "$1" = "-h" -o "$1" = "--help" ]; then
    usage
    exit 0
elif [ "$1" = "-v" -o "$1" = "--version" ]; then
    echo "Version: $VERSION"
    exit 0
elif [ "$1" = "-s" -o "$1" = "--screenshot" ]; then
    main "screenshot"
elif [ "$1" = "-g" -o "$1" = "--gif" ]; then
    main "gif"
elif [ "$1" = "-r" -o "$1" = "--record" ]; then
    main "video"
elif [ "$1" = "-q" -o "$1" = "--stop" ]; then
    main "stop"
elif [ $# == 0 ]; then
    if [ -a "$RECORD_PIDFILE" ]; then
        main $($DMENU_CMD <<< "$DMENU_RECORD_OPTS")
    else
        main $($DMENU_CMD <<< "$DMENU_OPTS")
    fi
fi
