#!/usr/bin/env bash
# Domoticz: Open Source Home Automation System
# (c) 2012, 2016 by GizMoCuz
# Big thanks to Jacob Salmela! (This is based on the excelent Pi-Hole install script ;)
# http://www.domoticz.com
# Installs Domoticz
#
# Domoticz 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 2 of the License, or
# (at your option) any later version.

# Donations are welcome via the website or application
#
# Install with this command (from your Pi):
#
# curl -L install.domoticz.com | bash

set -e
######## VARIABLES #########
setupVars=/etc/domoticz/setupVars.conf

useUpdateVars=false

Dest_folder=""
IPv4_address=""
Enable_http=true
Enable_https=true
HTTP_port="8080"
HTTPS_port="443"
Current_user=""

lowercase(){
    echo "$1" | sed "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/"
}

OS=`lowercase \`uname -s\``
MACH=`uname -m`
ARCH=`dpkg --print-architecture`
if [ ${MACH} = "armv6l" ] || [ ${ARCH} = "armhf" ]
then
 MACH="armv7l"
fi

# Find the rows and columns will default to 80x24 is it can not be detected
screen_size=$(stty size 2>/dev/null || echo 24 80) 
rows=$(echo $screen_size | awk '{print $1}')
columns=$(echo $screen_size | awk '{print $2}')

# Divide by two so the dialogs take up half of the screen, which looks nice.
r=$(( rows / 2 ))
c=$(( columns / 2 ))
# Unless the screen is tiny
r=$(( r < 20 ? 20 : r ))
c=$(( c < 70 ? 70 : c ))

######## Undocumented Flags. Shhh ########
skipSpaceCheck=false
reconfigure=false

######## FIRST CHECK ########
# Must be root to install
echo ":::"
if [[ ${EUID} -eq 0 ]]; then
	echo "::: You are root."
else
	echo "::: Script called with non-root privileges. The Domoticz installs server packages and configures"
	echo "::: system networking, it requires elevated rights. Please check the contents of the script for"
	echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source."
	echo ":::"
	echo "::: Detecting the presence of the sudo utility for continuation of this install..."

	if [ -x "$(command -v sudo)" ]; then
		echo "::: Utility sudo located."
		exec curl -sSL https://install.domoticz.com | sudo bash "$@"
		exit $?
	else
		echo "::: sudo is needed for the Web interface to run domoticz commands.  Please run this script as root and it will be automatically installed."
		exit 1
	fi
fi

# Compatibility

if [ -x "$(command -v apt-get)" ]; then
	#Debian Family
	#############################################
	PKG_MANAGER="apt-get"
	PKG_CACHE="/var/lib/apt/lists/"
	UPDATE_PKG_CACHE="${PKG_MANAGER} update"
	PKG_UPDATE="${PKG_MANAGER} upgrade"
	PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install"
	# grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE
	PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true"
	INSTALLER_DEPS=( apt-utils whiptail git)
	domoticz_DEPS=( curl unzip wget sudo cron libudev-dev libssl1.1 )

        DEBIAN_ID=$(grep -oP '(?<=^ID=).+' /etc/*-release | tr -d '"')
        DEBIAN_VERSION=$(grep -oP '(?<=^VERSION_ID=).+' /etc/*-release | tr -d '"')

	if test ${DEBIAN_VERSION} -lt 10
	then
		domoticz_DEPS=( ${domoticz_DEPS[@]} libcurl3 )
	else
		domoticz_DEPS=( ${domoticz_DEPS[@]} libcurl4 libusb-0.1)
	fi;

	package_check_install() {
		dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}"
	}
elif [ -x "$(command -v rpm)" ]; then
	# Fedora Family
	if [ -x "$(command -v dnf)" ]; then
		PKG_MANAGER="dnf"
	else
		PKG_MANAGER="yum"
	fi
	PKG_CACHE="/var/cache/${PKG_MANAGER}"
	UPDATE_PKG_CACHE="${PKG_MANAGER} check-update"
	PKG_UPDATE="${PKG_MANAGER} update -y"
	PKG_INSTALL="${PKG_MANAGER} install -y"
	PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l"
	INSTALLER_DEPS=( procps-ng newt git )
	domoticz_DEPS=( curl libcurl4 unzip wget findutils cronie sudo domoticz_DEP)
	if grep -q 'Fedora' /etc/redhat-release; then
		remove_deps=(epel-release);
		domoticz_DEPS=( ${domoticz_DEPS[@]/$remove_deps} );
	fi
	package_check_install() {
		rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}"
	}
else
	echo "OS distribution not supported"
	exit
fi

####### FUNCTIONS ##########
spinner() {
	local pid=$1
	local delay=0.50
	local spinstr='/-\|'
	while [ "$(ps a | awk '{print $1}' | grep "${pid}")" ]; do
		local temp=${spinstr#?}
		printf " [%c]  " "${spinstr}"
		local spinstr=${temp}${spinstr%"$temp"}
		sleep ${delay}
		printf "\b\b\b\b\b\b"
	done
	printf "    \b\b\b\b"
}

find_current_user() {
	# Find current user
	Current_user=${SUDO_USER:-$USER}
	echo "::: Current User: ${Current_user}"
}

find_IPv4_information() {
	# Find IP used to route to outside world
	IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}')
	IPv4_address=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}')
	IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}')
}

welcomeDialogs() {
	# Display the welcome dialog
	whiptail --msgbox --backtitle "Welcome" --title "Domoticz automated installer" "\n\nThis installer will transform your device into a Home Automation System!\n\n
Domoticz is free, but powered by your donations at:  http://www.domoticz.com\n\n
Domoticz is a SERVER so it needs a STATIC IP ADDRESS to function properly.
	" ${r} ${c}
}

displayFinalMessage() {
	# Final completion message to user
	whiptail --msgbox --backtitle "Ready..." --title "Installation Complete!" "Point your browser to either:

HTTP:	${IPv4_address%/*}:${HTTP_port%/*}
HTTPS:	${IPv4_address%/*}:${HTTPS_port}
User/Password:   admin/domoticz 
Modify password asap in menu Setup - MyProfile

Wiki:  https://wiki.domoticz.com
Forum: https://forum.domoticz.com

The install log is in /etc/domoticz." ${r} ${c}
}

verifyFreeDiskSpace() {

	# 50MB is the minimum space needed
	# - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables.
	echo "::: Verifying free disk space..."
	local required_free_kilobytes=51200
	local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}')

	# - Unknown free disk space , not a integer
	if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then
		echo "::: Unknown free disk space!"
		echo "::: We were unable to determine available free disk space on this system."
		echo "::: You may override this check and force the installation, however, it is not recommended"
		echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script"
		echo "::: eg. curl -L https://install.domoticz.com | bash /dev/stdin --i_do_not_follow_recommendations"
		exit 1
	# - Insufficient free disk space
	elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then
		echo "::: Insufficient Disk Space!"
		echo "::: Your system appears to be low on disk space. Domoticz recommends a minimum of $required_free_kilobytes KiloBytes."
		echo "::: You only have ${existing_free_kilobytes} KiloBytes free."
		echo "::: If this is a new install you may need to expand your disk."
		echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'"
		echo "::: After rebooting, run this installation again. (curl -L https://install.domoticz.com | bash)"

		echo "Insufficient free space, exiting..."
		exit 1

	fi

}

chooseServices() {
	Enable_http=false;
	Enable_https=false;
	# Let use enable HTTP and/or HTTPS
	cmd=(whiptail --separate-output --checklist "Select Services (press space to select)" ${r} ${c} 2)
	options=(HTTP "Enables HTTP access" on
	HTTPS "Enabled HTTPS access" on)
	choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
	if [[ $? = 0 ]];then
		for choice in ${choices}
		do
			case ${choice} in
			HTTP  )   Enable_http=true;;
			HTTPS  )   Enable_https=true;;
			esac
		done
		if [ ! ${Enable_http} ] && [ ! ${Enable_https} ]; then
			echo "::: Cannot continue, neither HTTP or HTTPS selected"
			echo "::: Exiting"
			exit 1
		fi
	else
		echo "::: Cancel selected. Exiting..."
		exit 1
	fi
	# Configure the port(s)
	if [ "$Enable_http" = true ] ; then
		HTTP_port=$(whiptail --inputbox "HTTP Port number:" ${r} ${c} ${HTTP_port} --title "Configure HTTP" 3>&1 1>&2 2>&3)
		exitstatus=$?
		if [ $exitstatus = 0 ]; then
			echo "HTTP Port: " $HTTP_port
		else
			echo "::: Cancel selected. Exiting..."
			exit 1
		fi	
	fi    
	if [ "$Enable_https" = true ] ; then
		HTTPS_port=$(whiptail --inputbox "HTTPS Port number:" ${r} ${c} ${HTTPS_port} --title "Configure HTTPS" 3>&1 1>&2 2>&3)
		exitstatus=$?
		if [ $exitstatus = 0 ]; then
			echo "HTTPS Port: " $HTTPS_port
		else
			echo "::: Cancel selected. Exiting..."
			exit 1
		fi	
	fi
}

chooseDestinationFolder() {
	Dest_folder=$(whiptail --inputbox "Installation Folder:" ${r} ${c} ${Dest_folder} --title "Destination" 3>&1 1>&2 2>&3)
	exitstatus=$?
	if [ $exitstatus = 0 ]; then
		echo ":::"
	else
		echo "::: Cancel selected. Exiting..."
		exit 1
	fi	
}

stop_service() {
	# Stop service passed in as argument.
	echo ":::"
	echo -n "::: Stopping ${1} service..."
	if [ -x "$(command -v service)" ]; then
		service "${1}" stop &> /dev/null & spinner $! || true
	fi
	echo " done."
}

start_service() {
	# Start/Restart service passed in as argument
	# This should not fail, it's an error if it does
	echo ":::"
	echo -n "::: Starting ${1} service..."
	if [ -x "$(command -v service)" ]; then
		service "${1}" restart &> /dev/null  & spinner $!
	fi
	echo " done."
}

enable_service() {
	# Enable service so that it will start with next reboot
	echo ":::"
	echo -n "::: Enabling ${1} service to start on reboot..."
	if [ -x "$(command -v service)" ]; then
		update-rc.d "${1}" defaults &> /dev/null  & spinner $!
	fi
	echo " done."
}

update_package_cache() {
	#Running apt-get update/upgrade with minimal output can cause some issues with
	#requiring user input (e.g password for phpmyadmin see #218)

	#Check to see if apt-get update has already been run today
	#it needs to have been run at least once on new installs!
	timestamp=$(stat -c %Y ${PKG_CACHE})
	timestampAsDate=$(date -d @"${timestamp}" "+%b %e")
	today=$(date "+%b %e")

	if [ ! "${today}" == "${timestampAsDate}" ]; then
		#update package lists
		echo ":::"
		echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..."
		${UPDATE_PKG_CACHE} &> /dev/null & spinner $!
		echo " done!"
	fi
}

notify_package_updates_available() {
  # Let user know if they have outdated packages on their system and
  # advise them to run a package update at soonest possible.
	echo ":::"
	echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...."
	updatesToInstall=$(eval "${PKG_COUNT}")
	echo " done!"
	echo ":::"
	if [[ ${updatesToInstall} -eq "0" ]]; then
		echo "::: Your system is up to date! Continuing with Domoticz installation..."
	else
		echo "::: There are ${updatesToInstall} updates available for your system!"
		echo "::: We recommend you run '${PKG_UPDATE}' after installing Domoticz! "
		echo ":::"
	fi
}

install_dependent_packages() {
	# Install packages passed in via argument array
	# No spinner - conflicts with set -e
	declare -a argArray1=("${!1}")

	for i in "${argArray1[@]}"; do
		echo -n ":::    Checking for $i..."
		package_check_install "${i}" &> /dev/null
		echo " installed!"
	done
}

finalExports() {
	#If it already exists, lets overwrite it with the new values.
	if [[ -f ${setupVars} ]]; then
		rm ${setupVars}
	fi
    {
	echo "Dest_folder=${Dest_folder}"
	echo "Enable_http=${Enable_http}"
	echo "HTTP_port=${HTTP_port}"
	echo "Enable_https=${Enable_https}"
	echo "HTTPS_port=${HTTPS_port}"
    }>> "${setupVars}"
}

downloadDomoticzWeb() {
	echo "::: Destination folder=${Dest_folder}"
	if [[ ! -e $Dest_folder ]]; then
		echo "::: Creating ${Dest_folder}"
		mkdir $Dest_folder
		chown "${Current_user}":"${Current_user}" $Dest_folder
	fi
	cd $Dest_folder
	wget -O domoticz_release.tgz "http://www.domoticz.com/download.php?channel=release&type=release&system=${OS}&machine=${MACH}"
	echo "::: Unpacking Domoticz..."
	tar xvfz domoticz_release.tgz
	rm domoticz_release.tgz
	Database_file="${Dest_folder}/domoticz.db"
	if [ ! -f $Database_file ]; then
		echo "Creating database..."
		touch $Database_file
		chmod 644 $Database_file
		chown "${Current_user}":"${Current_user}" $Database_file
	fi
}

makeStartupScript() {
	cp "${Dest_folder}/domoticz.sh" /tmp/domoticz_tmp_ss1

    #configure the script
    cat /tmp/domoticz_tmp_ss1 | sed -e "s/USERNAME=pi/USERNAME=${Current_user}/" > /tmp/domoticz_tmp_ss2
    rm /tmp/domoticz_tmp_ss1
    
    local http_port="${HTTP_port}"
    local https_port="${HTTPS_port}"
	if [ "$Enable_http" = false ] ; then
		http_port="0"
	fi    
	if [ "$Enable_https" = false ] ; then
		https_port="0"
	fi    
    
    cat /tmp/domoticz_tmp_ss2 | sed -e "s/-www 8080/-www ${http_port}/" > /tmp/domoticz_tmp_ss1
    rm /tmp/domoticz_tmp_ss2
    cat /tmp/domoticz_tmp_ss1 | sed -e "s/-sslwww 443/-sslwww ${https_port}/" > /tmp/domoticz_tmp_ss2
    rm /tmp/domoticz_tmp_ss1
    cat /tmp/domoticz_tmp_ss2 | sed -e "s%/home/\$USERNAME/domoticz%${Dest_folder}%" > /tmp/domoticz_tmp_ss1
    rm /tmp/domoticz_tmp_ss2
    
    mv /tmp/domoticz_tmp_ss1 /etc/init.d/domoticz.sh
	chmod +x /etc/init.d/domoticz.sh
	update-rc.d domoticz.sh defaults
}

installdomoticz() {
	# Install base files
	downloadDomoticzWeb
	makeStartupScript
	finalExports
}

updatedomoticz() {
	# Source ${setupVars} for use in the rest of the functions.
	. ${setupVars}
	# Install base files
	downloadDomoticzWeb
}

update_dialogs() {
	# reconfigure
	if [ "${reconfigure}" = true ]; then
		opt1a="Repair"
		opt1b="This will retain existing settings"
		strAdd="You will remain on the same version"
	else
		opt1a="Update"
		opt1b="This will retain existing settings."
		strAdd="You will be updated to the latest version."
	fi
	opt2a="Reconfigure"
	opt2b="This will allow you to enter new settings"

	UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \
	"${opt1a}"  "${opt1b}" \
	"${opt2a}"  "${opt2b}" 3>&2 2>&1 1>&3)

	if [[ $? = 0 ]];then
		case ${UpdateCmd} in
			${opt1a})
				echo "::: ${opt1a} option selected."
				useUpdateVars=true
				;;
			${opt2a})
				echo "::: ${opt2a} option selected"
				useUpdateVars=false
				;;
		esac
	else
		echo "::: Cancel selected. Exiting..."
		exit 1
	fi

}

install_packages() {
	# Update package cache
	update_package_cache

	# Notify user of package availability
	notify_package_updates_available

	# Install packages used by this installation script
	install_dependent_packages INSTALLER_DEPS[@]

	# Install packages used by the Domoticz
	install_dependent_packages domoticz_DEPS[@]
}

main() {
# Check arguments for the undocumented flags
	for var in "$@"; do
		case "$var" in
			"--reconfigure"  ) reconfigure=true;;
			"--i_do_not_follow_recommendations"   ) skipSpaceCheck=false;;
			"--unattended"     ) runUnattended=true;;
		esac
	done

	if [[ -f ${setupVars} ]]; then
		if [[ "${runUnattended}" == true ]]; then
			echo "::: --unattended passed to install script, no whiptail dialogs will be displayed"
			useUpdateVars=true
		else
			update_dialogs
		fi
	fi

	# Start the installer
	# Verify there is enough disk space for the install
	if [[ "${skipSpaceCheck}" == true ]]; then
		echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!"
	else
		verifyFreeDiskSpace
	fi

	install_packages

	if [[ "${reconfigure}" == true ]]; then
		echo "::: --reconfigure passed to install script. Not downloading/updating local installation"
	else
		echo "::: Downloading Domoticz"
	fi
	
	find_current_user
	
	Dest_folder="/home/${Current_user}/domoticz"
	
	find_IPv4_information

	if [[ ${useUpdateVars} == false ]]; then
		# Display welcome dialogs
		welcomeDialogs
		# Create directory for Domoticz storage
		mkdir -p /etc/domoticz/
		# Install and log everything to a file
		chooseServices
		chooseDestinationFolder
		installdomoticz
	else
		updatedomoticz
	fi

	if [[ "${useUpdateVars}" == false ]]; then
	    displayFinalMessage
	fi

	echo "::: Restarting services..."
	# Start services
	enable_service domoticz.sh
	start_service domoticz.sh
	echo "::: done."

	echo ":::"
	if [[ "${useUpdateVars}" == false ]]; then
		echo "::: Installation Complete! Configure your browser to use the Domoticz using:"
		echo ":::     ${IPv4_address%/*}:${HTTP_port}"
		echo ":::     ${IPv4_address%/*}:${HTTPS_port}"
	else
		echo "::: Update complete!"
	fi
}

main "$@"
