#!/bin/bash

set -u

version="0.0.1"

# make us a fifo...
rm -f .bashbotfifo{in,out}
mkfifo .bashbotfifoin
mkfifo .bashbotfifoout

# Setup some fds to talk to IRC
exec 3<> .bashbotfifoin
exec 4<> .bashbotfifoout

irc_process=
keeprunning=1
declare -A reminders

cleanup() {
    send_line "QUIT :Going back to hell"
    keeprunning=0
    sleep 0.2
    if [[ $irc_process -gt 0 ]]; then
        if (ps -p $irc_process > /dev/null); then
            kill $irc_process
        fi
    fi
    rm .bashbotfifo{in,out}
    trap - EXIT
    exit 0
}

my_hostname="$(hostname)"
my_irc_channel="#alug"
my_irc_password=""
my_irc_nick="bashbot"
my_irc_server="irc.oftc.net"
my_irc_port="6697"

# now if there's a settings file, source that...
if [ -e "$(dirname $(readlink -f $0))/.botsettings.sh" ]; then
    . "$(dirname $(readlink -f $0))/.botsettings.sh"
fi

connect_to_server() {
    gnutls-cli --crlf $my_irc_server:$my_irc_port <&4 >&3 2>&3 &
    irc_process=$!
    connected=0
    while [ $connected == 0 ]; do
        read -t 0.2 -u 3 line
        if [[ "$line" =~ NOTICE\  ]]; then
            echo $line
            send_line PASS $my_irc_password
            send_line NICK $my_irc_nick
            send_line USER $my_irc_nick $my_hostname $my_irc_server $my_irc_nick
            send_line JOIN $my_irc_channel
            connected=1
        fi
    done
}

# Format of server messages
#:thingsendingmessage command params
# e.g. PRIVMSG
#:iDunno!~brettp@mail.ipv6.sommitrealweird.co.uk PRIVMSG #bp :hello.

process_irc() {
    line="$@"
    echo "$line"
    ping_regex="^PING "
    privmsg_regex="^[^ ]* PRIVMSG"
    if [[ "${line}" =~ $ping_regex ]]; then
        send_line PONG
        echo PONG
    elif [[ "${line}" =~ $privmsg_regex ]]; then
        parse_privmsg "$line"
    fi
}

parse_privmsg() {
    line="$@"
    echo "Processing $line"
    read -a parts <<-EOF
$line
EOF

    # last character of the last element is going to be a \r so remove it
    parts[-1]=${parts[-1]%$'\r'}

    if [[ ${#parts[@]} -lt 4 ]]; then
        return
    fi

    if [[ "${parts[1]}" == "PRIVMSG" ]]; then
        sender=${parts[0]}
        channel=${parts[2]}
        do_command=0
        case $channel in
            $my_irc_channel)
                if [[ ${parts[3]} =~ [:]*$my_irc_nick[:]*$ ]]; then
                    do_command=1
                else
                    return
                fi
                bot_command=${parts[4]}
                options="${parts[@]:5}"
                ;;
            $my_irc_nick)
                bot_command=${parts[3]}
                bot_command=${bot_command#:}
                channel=${parts[0]%\!*}
                channel=${channel#:}
                options="${parts[@]:4}"
                do_command=1
                ;;
        esac
        if [[ $do_command -gt 0 ]]; then
            case $bot_command in
                help)
                    send_help_message $channel
                    ;;
                quit)
                    cleanup
                    ;;
                version)
                    send_version $channel
                    ;;
                add)
                    add_reminder $channel "$options"
                    ;;
                del)
                    del_reminder $channel "$options"
                    ;;
                list)
                    list_reminders $channel
                    ;;
                *)
                    send_action $channel "is sorry, they don't know how to $bot_command."
                    ;;
            esac
        fi
    fi
}

send_line() {
    echo "$@" >&4
    sleep 0.1
}

send_privmsg() {
    channel="$1"
    content="$2"

    send_line "PRIVMSG $channel :$content"
}

send_action() {
    channel="$1"
    content="$2"
    action_wrapper=$'\001'

    content="${action_wrapper}ACTION ${content}${action_wrapper}"
    send_privmsg "$channel" "$content"
}

send_help_message() {
    channel=$1
    echo "Sending help!"
    send_privmsg $channel "Help:"
    send_privmsg $channel "  An IRC bot written in bash!"
    send_privmsg $channel "  help - display this message"
    send_privmsg $channel "  version - display version number"
    send_privmsg $channel "  quit - make the bot quit IRC"
    send_privmsg $channel "  add YYYY-mm-dd HH:MM reminder text"
    send_privmsg $channel "  del md5sum"
    send_privmsg $channel "  list - list reminders"
}

send_version() {
    channel=$1
    send_privmsg $channel "Version: $version"
}

add_reminder() {
    channel="$1"
    data="$2"
    read -a dataparts <<-EOF
$data
EOF
    # we now have a list of parts, they should be of the form
    # YYYY-mm-dd HH:MM the text of the reminder
    timestamp=$(date +"%s" --date="${dataparts[0]} ${dataparts[1]}")
    if [[ $? -ne 0 ]]; then
        send_privmsg $channel "Couldn't parse date/time ${dataparts[0]} ${dataparts[1]}"
    else
        text="${dataparts[@]:2}"
        md5sum=$(echo -n "$timestamp $text" | md5sum | sed -e 's#[ ][ ]*-##;')
        send_privmsg $channel "Added reminder for [$md5sum] ${dataparts[0]} ${dataparts[1]}: $text"
        reminders[$timestamp-$md5sum]="$text"
    fi
}

check_reminders() {
    current_timestamp="$(date +"%s")"
    # loop through the reminders keys to see if it's past and alert if so
    for tsmd5 in ${!reminders[@]}; do
        ts=${tsmd5%-*}
        if [[ $ts -le $current_timestamp ]]; then
            send_privmsg $my_irc_channel "${reminders[$tsmd5]}"
            # we need to remove this from the array now
            unset "reminders[$tsmd5]"
        fi
    done
}

list_reminders() {
    channel=$1
    count=0
    for tsmd5 in ${!reminders[@]}; do
        ts=${tsmd5%-*}
        md5sum=${tsmd5#*-}
        date_stamp="$(date --date="@$ts" +"%Y-%m-%d %H:%M %Z")"
        send_privmsg $channel "[$md5sum] $date_stamp ${reminders[$tsmd5]}"
        count=$((count+1))
    done

    if [[ $count -eq 0 ]]; then
        send_privmsg $channel "There are currently no reminders set, use add to add one."
    fi
}

del_reminder() {
    channel="$1"
    md5todelete="$2"
    found=0
    for tsmd5 in ${!reminders[@]}; do
        md5sum=${tsmd5#*-}
        if [[ "$md5sum" == "$md5todelete" ]]; then
            ts=${tsmd5%-*}
            date_stamp="$(date --date="@$ts" +"%Y-%m-%d %H:%M %Z")"
            send_privmsg $channel "Removed reminder [$md5sum] $date_stamp ${reminders[$tsmd5]}"
            unset "reminders[$tsmd5]"
            found=1
        fi
    done
    if [[ $found -eq 0 ]]; then
        send_privmsg $channel "Couldn't find reminder $md5todelete"
    fi
}

connect_to_server
trap cleanup EXIT SIGINT

while true; do
    # see if there's anything to read
    read -t 0.2 -u 3 aline
    if [ $? == 0 ]; then
        process_irc "$aline"
        sleep 0.2
    fi
    check_reminders
    # check that the irc_process is still running, restart it if not
    if ( ! kill -0 $irc_process ); then
        connect_to_server
    fi
done
