#!/usr/bin/bash
#----------------------------------------------------------------------
#
# TODO:
#	- link config files
#	- copy config files
#	- edit system config files
#	- might be a good idea to add gui/no-gui options...
#	- ssh-keygen...
#	- add --system and --user flags to only do system/user stuff...
#
#
#----------------------------------------------------------------------
# options...


#FEATURE_DIR=
#DRY_RUN=
#QUIET=




# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

CMD=$(basename $0)

SYSTEM_RC=${0}rc
USER_RC=~/.${CMD}rc

# XXX do we need to split user and system scripts???
SCRIPT_DIR=${0}.d/
USER_SCRIPT_DIR=${0}.d/user/

# System info...
if ! [ -z "$(type -t dmidecode )" ] ; then
	# XXX should we be more specific here and include model number??
	PLATFORM=$( dmidecode \
		| grep -A3 '^System Information' \
		| grep 'Version:' \
		| sed 's/\s*Version:\s*//' )
else
	PLATFORM=unknown
fi

PLATFORM_SCRIPT_DIR=${SCRIPT_DIR}/platform/${PLATFORM}



#----------------------------------------------------------------------
# configuration....

# see: ALL_FEATURES below...
FEATURES=(
	dir

	dnf
	flatpak
	snap
	npm

	start-services
	start-user-services

	# these can depend on some services like syncthing...
	user-link
	user-copy

	scripts
	platform-scripts
	user-scripts
)




# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

DIR=(
	~/.bashrc.d
	~/bin
	~/.config
	~/.local/share

	~/Sync/ALL
	~/work/EXTERNAL
)

# NOTE: $DIR and these are not run in order -- linking may need other 
#		stuff done before...
# XXX
CFG_USER_SOURCE=~/Sync/CONFIG/Linux/
CFG_USER_LINK=(
	.gitconfig
	.vimrc .gvimrc .vim
	.tmux.conf
	.config/mc
	.config/ulauncher
)
CFG_USER_COPY=(
) 



PKG_DNF=( 
	# tools and recovery...
	gparted gdisk testdisk

	# networking...
	cronie syncthing tor obfs4 
	youtube-dl qbittorrent

	# tools...
	snapd 
	openssl gpm  
	mc tmux bat htop
	keepassxc

	# dev...
	vim gvim vifm 
	git nodejs
	p7zip zip unrar

	# media...
	vlc mpv

	# desktop...
	ulauncher 
	gnome-tweaks 
	gnome-shell-extension-gsconnect

	# apps...
	krita
)

PKG_NPM=(
	tldr 
)

PKG_FLATPAK=(
	# system...
	com.github.tchx84.Flatseal 
	com.mattjakeman.ExtensionManager 
	io.github.realmazharhussain.GdmSettings
	org.gustavoperedo.FontDownloader

	# net...
	org.briarproject.Briar
	com.github.ElostonUngoogledChromium

	# media...
	#io.mpv.Mpv
	#org.videolan.VLC

	# tools...
	ch.openboard.OpenBoard
	org.blender.Blender
)

PKG_SNAP=(
	irfanview
	foobar2000
	snap-store 
)


SERVICES_START=(
	crond sshd gpm
)

SERVICES_USER_START=(
	syncthing
)



# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

SAVE=(
	FEATURES
	DIR
	PKG_DNF PKG_FLATPAK PKG_NPM PKG_SNAP
	SERVICES_START SERVICES_USER_START
	CFG_USER_SOURCE CFG_USER_LINK CFG_USER_COPY
)



#----------------------------------------------------------------------
# Builtin features...

# default 
# XXX this should be split into system and user sections...
ALL_FEATURES=(
	# system...
	dnf
	flatpak
	snap
	npm

	#keyd

	start-services

	# user..
	dir

	user-link
	user-copy

	#syncthing

	start-user-services

	# keep this last...
	scripts
	platform-scripts
	user-scripts
)


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-dir(){
	@setupList mkdir -p - ${DIR[@]}
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-user-link(){
	# XXX
	true
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-user-copy(){
	# XXX
	true
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-dnf(){
	@ sudo dnf update

	@setupList sudo dnf install - ${PKG_DNF[@]}

	# default editor...
	@ sudo dnf install vim-default-editor --allowerasing
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-npm(){
	@setupList sudo npm install -g - ${PKG_NPM[@]}
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-flatpak(){
	@ sudo flatpak remote-add --if-not-exists flathub \
		https://flathub.org/repo/flathub.flatpakrepo

	@setupList sudo flatpak install - ${PKG_FLATPAK[@]}
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-snap(){
	@ sudo ln -s /var/lib/snapd/snap /snap

	@setupList sudo snap install - ${PKG_SNAP[@]}
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-start-services(){
	@setupList sudo systemctl start - ${SERVICES_START[@]}
	@setupList sudo systemctl enable - ${SERVICES_START[@]}
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-start-user-services(){
	@setupList sudo systemctl --user enable - ${SERVICES_USER_START[@]}
	@setupList sudo systemctl --user start - ${SERVICES_USER_START[@]}
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-keyd(){ (
	@ cd ~/work/EXTERNAL
	@ git clone https://github.com/rvaiya/keyd
	@ cd keyd
	@ make \
		&& @ sudo make install
	@ sudo systemctl enable keyd \
		&& @ sudo systemctl start keyd

	# XXX copy config: /etc/keyd/<keyboard-id>.conf...

) }


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-bash(){ (
	@ cd ~/work/
	@ git clone git@github.com:flynx/bashctrl
	@ cd bashctrl
	@ ./bashctrl -y setup \
) }


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-far(){
	@ sudo dnf copr enable polter/far2l	
	@ sudo dnf install far2l far2l-tty
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-gnome(){
	# fractional scaling...
	@ gsettings set org.gnome.mutter experimental-features "['scale-monitor-framebuffer']"

	# disable beeps...
	@ gsettings set org.gnome.desktop.sound event-sounds false
	# visual bell...
	@ gsettings set org.gnome.desktop.wm.preferences visual-bell true
	@ gsettings set org.gnome.desktop.wm.preferences visual-bell-type frame-flash

	# swap language with alt-shift...
	# NOTE: with the way the gui is setup not this is not possible to set there...
	@ gsettings set org.gnome.desktop.wm.keybindings switch-input-source-backward "['<Alt>Shift_L']"
	@ gsettings set org.gnome.desktop.wm.keybindings switch-input-source "['<Shift>Alt_L']"

	# custom keys...
	# XXX
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-syncthing(){
	# XXX can we do this from CLI???
	#		need to:
	#			- link device
	#			- share ALL directory
	#			- wait for sync
	@ echo "Waiting for Syncthing to be connected with other devices manually."
	@ read -p "(press any key when done)"
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function feature-snapshots(){ (
	@ dnf install snapper python3-dnf-plugin-snapper
	@ sudo snapper -c root create-config /
	@ sudo snapper -c home create-config /home
	@ sudo snapper -c boot create-config /boot

	@ cd ~/work/EXTERNAL/
	@ git clone https://github.com/Antynea/grub-btrfs.git
	@ cd grub-btrfs
	@ sudo make install
) }


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# XXX skip scripts starting in "-" 
function feature-scripts(){ (
	if ! [ -z "$1" ] ; then
		local dir=$1
	else
		local dir=$SCRIPT_DIR
	fi

	# no script dir...
	[ -d "$dir" ] \
		|| return 1

	cd "$dir"l
	local script
	for script in * ; do
		# skip README and scripts starting with '-'...
		( [ "${script}" == "README" ] \
				|| [ "${script:0:1}" == "-" ] )\
			&& continue
		echo "# $script:"
		( @ ./$script )
	done
) }

function feature-platform-scripts(){
	feature-scripts "$PLATFORM_SCRIPT_DIR"
}

function feature-user-scripts(){
	feature-scripts "$USER_SCRIPT_DIR"
}




#----------------------------------------------------------------------
# feature api...

function @feature(){
	while ! [ -z "$1" ] ; do
		local feauture="$1"
		for f in "${FEATURES[@]}" ; do
			if [ "$f" == "${feature}" ] ; then
				return 0
			fi
		done
		shift
	done
	false
}

function @feature-add(){
	while ! [ -z "$1" ] ; do
		if ! @feature "$1" ; then
			FEATURES+=("$1")
		fi
		shift
	done
}

function @feature-del(){
	while ! [ -z $1 ] ; do
		FEATURES=( ${FEATURES[@]/$1} )
		shift
	done
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# XXX add an interactive list editor???
# XXX


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# command runners...


# Print command and if $DRY_RUN is not set run it...
#
function @(){
	# XXX this does not preserve quotes...
	[ -z $QUIET ] \
		&& echo "## $@"
	if [ -z $DRY_RUN ] ; then 
		$@

	# need to exit with true status on dry runs...
	else
		true
	fi
}

# Setup list if not empty...
#
# Usage:
#	@setupList CMD - LIST
#
function @setupList(){
	local CMD=
	local LST=
	# get command...
	while true ; do
		if [ $# == 0 ] || [ $1 == "-" ]  ; then
			shift
			break
		fi
		CMD="$CMD $1"
		shift
	done
	# list...
	LST=("$@")

	# run command if list is not empty...
	if [ ${#LST[@]} != 0 ] ; then
		@ $CMD ${LST[@]}
	fi
}


#----------------------------------------------------------------------
# config...

[ -e "${SYSTEM_RC}" ] \
	&& source ${SYSTEM_RC}

[ -e "${USER_RC}" ] \
	&& source ${USER_RC}



#----------------------------------------------------------------------
# help and arguments...

function listScripts(){
	[ -d "$SCRIPT_DIR" ] \
		&& echo "-" \
		&& return 0

	local scripts=( $(ls "$SCRIPT_DIR") )
	echo ${scripts/README}
}

function printhelp(){ cat << EOF
Usage: $CMD [OPTIONS]

Options:
    -h | --help         - print this message and exit.
    -d | --dry-run      - report the actions but do nothing.
    -q | --quiet        - run in quiet mode.

    --list-features     - list enabled features.
    --list-all-features - list supported features.
    --list-scripts      - list available scripts.

External configuration:
    -s | --source FILE  - read config from file.
                          (this flag can be used multiple times)
    -o | --output FILE  - write config to file.

Feature list manipulation:
    -all                - clear features
    +all                - reset features to \$ALL_FEATURES
    -FEATURE            - remove FEATURE from list
    +FEATURE            - add FEATURE to lost

Helpers:
    --make-platform-dir - make platform directory for current platform
                          and exit.


Default features:
  ${FEATURES[@]}

Supported features:
  ${ALL_FEATURES[@]}

Available scripts:
  $( listScripts )


Examples:
    Only create dirs and links (features dir and user-link)...
    \$ $CMD -all +dir +user-link

    Same as above...
    \$ FEATURES=( dir user-link )  $CMD

EOF
}

while ! [ -z "$1" ] ; do
	case "$1" in
		-h|--help)
			printhelp
			exit
			;;
		-d|--dry-run)
			DRY_RUN=1
			shift
			;;
		-q|--quiet)
			QUIET=1
			shift
			;;

		# introspection...
		--list-features)
			echo ${FEATURES[@]}
			exit
			;;
		--list-all-features)
			echo ${ALL_FEATURES[@]}
			exit
			;;
		--list-scripts)
			listScripts
			exit
			;;

		# config files...
		-s|--source)
			[ -z "$2" ] \
				&& source "$2"
			shift
			shift
			;;
		-o|--output)
			SAVE=1
			SAVE_FILE=$2
			shift
			shift
			;;

		# helpers...
		--make-platform-dir)
			echo "# Patform script dir: $PLATFORM_SCRIPT_DIR"
			[ -d "$PLATFORM_SCRIPT_DIR" ] \
				|| @ mkdir -p "$PLATFORM_SCRIPT_DIR"
			exit
			;;


		# feature manipulation...
		# NOTE: keep these last...
		+all)
			FEATURES=( ${ALL_FEATURES[@]} )
			shift
			;;
		-all)
			FEATURES=()
			shift
			;;
		+*)
			@feature-add "${1:1}"
			shift
			;;
		-*)
			@feature-del "${1:1}"
			shift
			;;
	esac
done





#----------------------------------------------------------------------
# install/setup stuff...

echo "# FEATURES: ${FEATURES[@]}"

# built-in functions...
# NOTE: order of execution is set by $ALL_FEATURES...
_features=( "${FEATURES[@]}" )
for feature in "${ALL_FEATURES[@]}" ; do
	# skip...
	@feature "$feature" \
		|| continue

	# functions...
	if [ "$(type -t feature-$feature)" == "function" ] ; then
		echo "# $feature:"
		_features=( ${_features[@]/$feature} )
		feature-$feature
	fi
done
# features not in $ALL_FEATURES...
for feature in "${_features[@]}" ; do
	echo "# $feature:"
	# built-in...
	if [ "$(type -t feature-$feature)" == "function" ] ; then
		feature-$feature

	# feature scripts...
	elif [ -d "$FEATURE_DIR" ] ; then
		"$FEATURE_DIR"/$feature

	# err...
	else
		echo "$CMD: unknown feature: $feature"
	fi
done


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# save...

# XXX






#----------------------------------------------------------------------
# vim:set ts=4 sw=4 spell nowrap :
