#!/bin/bash

SYS_CONF="/etc/bluetooth.conf"
BOOT_CONF="/boot/bluetooth.conf"
CONF="/data/etc/bluetooth.conf"
RUN_CONF="/var/lib/bluetooth.conf"

ADAPTER="hci0"
ADAPTER_PATH="/sys/class/bluetooth/${ADAPTER}"
ADAPTER_TIMEOUT=10
PROG="/usr/libexec/bluetooth/bluetoothd"
PROG_CTL="/usr/bin/bluetoothctl"
PROG_BDADDR="/usr/libexec/bdaddr"
DATA_DIR="/var/lib/bluetooth"
RUN_DATA_DIR="/data/bluetooth"
NO_ON_BOARD_BT="/tmp/.no_on_board_bt"  # used by RPi to explicitly indicate that no on-board BT was detected


test -x ${PROG} || exit 0

test -n "${OS_VERSION}" || source /etc/init.d/base

prepare_conf ${CONF} ${SYS_CONF} ${BOOT_CONF}
test -s ${CONF} || exit 0


function configure() {
    mkdir -p ${RUN_DATA_DIR}
    ln -sf ${RUN_DATA_DIR} ${DATA_DIR}
    cp ${CONF} ${RUN_CONF}

    # if no specific name configured, use hostname
    if ! grep -E 'Name\s*=' ${RUN_CONF} &>/dev/null; then   
        sed -ri "s/(\[General\])/\1\nName = $(hostname)/" ${RUN_CONF}
    fi
}

function ensure_addr() {
    # Some adapters may come with configured address AA:AA:AA:AA:AA:AA.
    # We use the serial number and ETH MAC address to build a BT address.
    if [[ -x ${PROG_BDADDR} ]] && grep -qiE 'AA:AA:AA:AA:AA:AA' /sys/class/bluetooth/${ADAPTER}/address; then
        serial_number=$(cat /proc/device-tree/serial-number | cut -c9-)
        b1=$(echo ${serial_number} | cut -c3-4)
        b2=$(echo ${serial_number} | cut -c5-6)
        b3=$(echo ${serial_number} | cut -c7-8)
        vendor_part=$(cat /sys/class/net/${OS_ETH}/address | cut -c1-8)
        addr=$(printf "${vendor_part}:%02x:%02x:%02x" $((0x${b3} ^ 0xAA)) $((0x${b2} ^ 0xAA)) $((0x${b1} ^ 0xAA)))
        
        # Temporarily start `bluetoothd` so we can use `bdaddr`.
        ${PROG} &>/dev/null &
        sleep 1       
        
        # Actually set address using `bdaddr`.
        ${PROG_BDADDR} -r -i ${ADAPTER} ${addr} >/dev/null
        
        # `bluetoothd` needs to be restarted a couple of times.
        killall -q $(basename ${PROG})
        sleep 1
        ${PROG} &>/dev/null &
        sleep 1
        killall -q $(basename ${PROG})
    fi
}

function watch_adapter() {
    count=0
    while true; do
        sleep 60
        output=$(timeout 1 bluetoothctl pairable on 2>/dev/null)
        if [[ -n "${output}" ]] && [[ $? == 0 ]]; then
            count=0
        else
            if [[ ${count} -lt 5 ]]; then
                count=$((count + 1))
                logger -t bluetooth "bluetooth adapter not responding"
            else
                panic action bluetooth "bluetooth adapter not responding"
            fi
        fi
    done
}

function start() {
    msg_begin "Configuring bluetooth"

    # adapter explicitly marked as unavailable from previous boot steps
    if [[ -f "${NO_ON_BOARD_BT}" ]]; then
        msg_fail "no adapter"
        return 1
    fi

    # wait for adapter to be reported by OS
    count=0
    while ! test -e ${ADAPTER_PATH}; do
        sleep 1
        count=$((count + 1))
        if [[ ${count} -ge ${ADAPTER_TIMEOUT} ]]; then
            msg_fail "no adapter"
            return 1
        fi
    done

    if configure; then
        msg_done
    else
        msg_fail
        return 1
    fi
    
    rfkill unblock bluetooth &>/dev/null
    
    ensure_addr
    
    msg_begin "Starting bluetoothd"
    ${PROG} &>/dev/null &

    # wait for adapter to be reported by bluetoothctl
    count=0
    while test -z "$(${PROG_CTL} list 2>/dev/null)"; do
        sleep 1
        count=$((count + 1))
        if [[ ${count} -ge ${ADAPTER_TIMEOUT} ]]; then
            msg_fail "no adapter"
            return 1
        fi
    done

    msg_done

    # if DiscoverableTimeout is set to 0, make adapter discoverable from boot time
    if grep -E '^DiscoverableTimeout\s*=\s*0$' ${RUN_CONF} &>/dev/null; then
        sleep 1
        ${PROG_CTL} discoverable on >/dev/null
    fi
    
    # some adapters must be explicitly powered on
    ${PROG_CTL} power on >/dev/null

    watch_adapter &
}

function stop() {
    msg_begin "Stopping bluetoothd"
    killall -q $(basename ${PROG})
    ps | grep bluetooth | grep -v $$ | grep -v grep | tr -s ' ' | sed -e 's/^\s//' | cut -d ' ' -f 1 | xargs -r kill
    msg_done
}

case "$1" in
    start)
        start
        ;;
        
    stop)
        stop
        ;;
        
    restart)
        stop
        start
        ;;
    
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit 0
