#!/bin/bash
#
# Copyright 2017 DT42
#
# This file is part of BerryNet.
#
# BerryNet is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BerryNet is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BerryNet.  If not, see <http://www.gnu.org/licenses/>.

# One-click IoT gateway deployment script.

set -e

LOG="$PWD/berrynet-install.log"
DISTRIBUTIONID=`lsb_release -i -s`
CODENAME=`lsb_release -c -s`
INSTALL_TENSORFLOW="Y"
INSTALL_OPENVINO="N"
INSTALL_DARKNET="N"
INSTALL_CAFFE2="N"


install_opencv() {
    # OpenCV sources
    #     x86 Ubuntu 16.04: PPA
    #     Raspbian: BerryNet repo
    if [ "$DISTRIBUTIONID" = "Ubuntu" ]; then
        sudo add-apt-repository -yu ppa:timsc/opencv-3.4
    fi
    sudo apt install -y python3-opencv
}

install_nodejs() {
    # v10.x is LTS, if you want the latest feature, change to "setup_11.x".
    curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
    sudo apt install -y nodejs
}

install_system_dependencies() {
    sudo apt update
    if [ "$CODENAME" = "buster" ]; then
        sudo apt install -y \
            supervisor
    else
        sudo apt install -y \
            apache2 \
            curl \
            dirmngr \
            fswebcam \
            git \
            imagemagick \
            libkrb5-dev \
            libyaml-dev \
            libzmq3-dev \
            lsb-release \
            mosquitto \
            mosquitto-clients \
            python3-dev \
            python3-opengl \
            python3-pip \
            supervisor \
            wget
        sudo -H pip3 install --timeout 60 cython
        sudo -H pip3 install --timeout 60 logzero
        sudo -H pip3 install --timeout 60 paho-mqtt
        sudo -H pip3 install --timeout 60 PyOpenGL
        sudo -H pip3 install --timeout 60 PyOpenGL-accelerate
        sudo -H pip3 install --timeout 60 watchdog

        install_opencv
        install_nodejs
    fi
}

install_berrynet_repository() {
    curl -sL https://raw.githubusercontent.com/DT42/BerryNet-repo/master/setup.sh | sudo -E bash -
}

install_configs() {
    # supervisor
    sudo cp config/supervisor/conf.d/berrynet-tflite.conf /etc/supervisor/conf.d/
    sudo mkdir -p /var/log/berrynet

    # mosquitto
    sudo cp config/etc/mosquitto/conf.d/berrynet.conf /etc/mosquitto/conf.d

    if [ "$DISTRIBUTIONID" = "Raspbian" ]; then
        # Make RPi camera to show at /dev/videoN
        sudo cp config/bcm2835-v4l2.conf /etc/modules-load.d
    fi
}

install_berrynet_py() {
    sudo apt install -y berrynet
    # FIXME: Freeboard dependency
    #sudo npm install -g mime
}

install_berrynet_js() {
    local working_dir="/usr/local/berrynet"
    sudo mkdir -p $working_dir
    sudo cp -a \
        broker.js \
        camera.js \
        config.js \
        data_collector.js \
        inference \
        journal.js \
        localimg.js \
        mail.js \
        line.js \
        package.json \
        $working_dir
    sudo cp berrynet-manager /usr/local/bin
    # FIXME: image dir should be created by program
    mkdir -p /usr/local/berrynet/inference/image
    # install npm dependencies
    pushd $working_dir > /dev/null
    sudo npm install --unsafe-perm
    popd > /dev/null
}

install_model() {
    local model_name="$1"
    echo "Install model $model_name"
    sudo apt install -y $model_name
}

download_classifier_model_caffe2() {
    sh ./utils/install-caffe2-models.sh
}

install_tensorflow() {
    if [ "$DISTRIBUTIONID" = "Ubuntu" ]; then
	sudo -H pip3 install tensorflow
    elif [ "$DISTRIBUTIONID" = "Raspbian" ]; then
        case $CODENAME in
        "buster")
            # Unsatisfied dependencies in Buster, and wheels will be installed
            #     python3-tensorboard             none
            #     python3-wrapt                   1.10.11-1+b1
            #     python3-gast                    none
            #     python3-tensorflow-estimator    none
            #     python3-astor                   0.5-1
            #     python3-absl-py                 none
            #     python3-google-pasta            none
            #     python3-setuptools              40.8.0-1
            sudo apt install -y \
                python3-numpy \
                python3-termcolor \
                python3-six \
                python3-keras-applications \
                python3-protobuf \
                python3-keras-preprocessing \
                python3-wheel \
                python3-grpcio \
                python3-werkzeug \
                python3-markdown \
                python3-h5py

	    TENSORFLOW_PKGNAME="tensorflow-1.15.0-cp37-cp37m-linux_armv7l.whl"
	    if [ ! -e "/tmp/$TENSORFLOW_PKGNAME" ]; then
                wget -O /tmp/$TENSORFLOW_PKGNAME \
                    https://github.com/PINTO0309/Tensorflow-bin/raw/master/$TENSORFLOW_PKGNAME
	    else
                echo "$TENSORFLOW_PKGNAME has existed, skip to download it."
	    fi
	    sudo -H pip3 install /tmp/$TENSORFLOW_PKGNAME
            ;;
        "stretch")
	    TENSORFLOW_PKGNAME="tensorflow-1.15.0-cp35-cp35m-linux_armv7l.whl"
	    if [ ! -e "/tmp/$TENSORFLOW_PKGNAME" ]; then
                wget -O /tmp/$TENSORFLOW_PKGNAME \
                    https://github.com/PINTO0309/Tensorflow-bin/raw/master/$TENSORFLOW_PKGNAME
	    else
                echo "$TENSORFLOW_PKGNAME has existed, skip to download it."
	    fi
	    sudo -H pip3 install /tmp/$TENSORFLOW_PKGNAME
            ;;
        *)
            echo "ERROR: Fail to install TensorFlow, unknown Raspbian codename $CODENAME"
            exit 1
        esac
    fi
}

install_openvino_repository() {
    echo 'Adding the OpenVINO GPG key to your keyring...'
    curl -sL https://apt.repos.intel.com/openvino/2019/GPG-PUB-KEY-INTEL-OPENVINO-2019 | sudo apt-key add -
    sudo curl -sL -o /etc/apt/sources.list.d/intel-openvino-2019.list \
        https://raw.githubusercontent.com/DT42/BerryNet/master/config/intel-openvino-2019.list
    sudo apt update
}

install_openvino() {
    # OpenVINO 2019R2.1
    if [ "$DISTRIBUTIONID" = "Ubuntu" ]; then
        if [ "$CODENAME" = "xenial" ]; then
            sudo apt install intel-openvino-runtime-ubuntu16-2019.2.242
        elif [ "$CODENAMe" = "bionic" ]; then
            sudo apt install intel-openvino-runtime-ubuntu18-2019.2.242
        else
            echo "ERROR: OpenVINO on Ubuntu $CODENAME is not supported by Intel."
        fi
    elif [ "$DISTRIBUTIONID" = "Raspbian" ]; then
        sudo apt install -y openvino-rpi
    fi
}

install_darknet () {
    # For x86, we use pure Darknet.
    # For Raspberry Pi, we use pure Darknet + NNPACK.
    #
    # To understand how Darknet NNPACK packages are built, please refer to
    # https://github.com/DT42/BerryNet/blob/master/doc/darknet-nnpack.md
    sudo apt install -y darknet libdarknet
}

#install_darknet() {
#    local peachpy_version="0.0.1"
#    local confu_version="cc90068"
#    local ninja_version="v1.8.2"
#    local nnpack_darknet_version="1ecda10"
#    local darknet_nnpack_version="fa5bddc"
#
#    # build dependencies
#    pip3 install --user PeachPy==$peachpy_version
#    pip3 install --user git+https://github.com/Maratyszcza/confu@$confu_version
#
#    pushd /tmp > /dev/null
#    git clone https://github.com/ninja-build/ninja.git
#    pushd ninja > /dev/null
#    git reset --hard $ninja_version
#    ./configure.py --bootstrap
#    popd > /dev/null
#    popd > /dev/null
#
#    sudo apt install -y clang
#
#    pushd /tmp > /dev/null
#    git clone https://github.com/thomaspark-pkj/NNPACK-darknet.git
#    pushd NNPACK-darknet > /dev/null
#    git reset --hard $nnpack_darknet_version
#    $HOME/.local/bin/confu setup
#    python ./configure.py --backend auto
#    /tmp/ninja/ninja
#    sudo cp lib/{libgoogletest-core.a,libnnpack.a,libpthreadpool.a} /usr/lib/
#    sudo cp include/nnpack.h /usr/include/
#    sudo cp deps/pthreadpool/include/pthreadpool.h /usr/include/
#    popd > /dev/null
#    popd > /dev/null
#
#    # build detection backend (darknet)
#    pushd inference > /dev/null
#    git clone https://github.com/thomaspark-pkj/darknet-nnpack.git darknet
#    pushd darknet > /dev/null
#    git reset --hard $darknet_nnpack_version
#    patch -p 1 < ../../patch/01-detection-backend.patch
#    make -j
#    popd > /dev/null
#    popd > /dev/null
#
#    cp utils/darknet/detectord.py inference/darknet
#    mkdir inference/darknet/utils
#    cp utils/darknet/utils/localrun.sh inference/darknet/utils
#}

install_caffe2() {
    if [ "$DISTRIBUTIONID" = "Ubuntu" ]; then
	sh ./utils/install-caffe2-ubuntu.sh
    elif [ "$DISTRIBUTIONID" = "Raspbian" ]; then
	sh ./utils/install-caffe2-raspbian.sh
    fi
}

update_system_service() {
    sudo service mosquitto restart
    sudo supervisorctl reload
    sudo a2enconf berrynet-dashboard
}

install_system_dependencies 2>&1 | tee -a $LOG
install_berrynet_repository 2>&1 | tee -a $LOG
install_berrynet_py 2>&1 | tee -a $LOG
install_configs 2>&1 | tee -a $LOG
# FIXME: Raspbian 20190408 breaks some dependencies of BerryNet JS.
#        Disable temporaily before this issue is fixed.
#install_berrynet_js 2>&1 | tee -a $LOG

if [ "$INSTALL_TENSORFLOW" = "Y" ]; then
    install_tensorflow 2>&1 | tee -a $LOG
    #install_model inception
    install_model mobilenet-1.0-224-quantized-tflite 2>&1 | tee -a $LOG
    install_model mobilenet-ssd-coco-tflite 2>&1 | tee -a $LOG
else
    echo "Not install TensorFlow" >> $LOG
fi

if [ "$INSTALL_OPENVINO" = "Y" ]; then
    install_openvino_repository 2>&1 | tee -a $LOG
    install_openvino 2>&1 | tee -a $LOG
    install_model mobilenet-1.0-224-fp16-openvino 2>&1 | tee -a $LOG
    install_model mobilenet-ssd-openvino 2>&1 | tee -a $LOG
else
    echo "Not install OpenVINO" >> $LOG
fi

if [ "$INSTALL_DARKNET" = "Y" ]; then
    install_darknet 2>&1 | tee -a $LOG
    install_model tinyyolovoc 2>&1 | tee -a $LOG
else
    echo "Not install Darknet NNPACK" >> $LOG
fi

if [ "$INSTALL_CAFFE2" = "Y" ]; then
    install_caffe2 2>&1 | tee -a $LOG
    download_classifier_model_caffe2 2>&1 | tee -a $LOG
else
    echo "Not install Caffe2" >> $LOG
fi

update_system_service 2>&1 | tee -a $LOG

{
    echo "Installation is completed successfully!"
    echo ""
    echo "If there is any issue, please open an issue on"
    echo ""
    echo "    https://github.com/DT42/BerryNet/issues"
    echo ""
    echo "and attach these logs:"
    echo ""
    echo "    $LOG"
    echo "    /usr/local/berrynet/npm-debug.log"
    echo ""
    echo "We are happy to check and fix it, thank you!"
} | tee -a $LOG
