#!/bin/bash

# To install new device:
# sudo bash -c "$(curl -sN https://install.connect.sixfab.com)" -- -t TOKEN_HERE
# To uninstall device:
# sudo bash -c "$(curl -sN https://install.connect.sixfab.com)" -- --uninstall

clear
cat <<"EOF"
    .&@&.             %%          .%@%           
   #@@@%           *&@@@&.         %@@@#         
  &@@&. .&@@%   .%@@@@@%/.   .%@@&. ,&@@%        
 %@@&. /@@@#  *&@@@@&&&@@@@&,  %@@&* .&@@&       
/@@@* .@@@#  &@@&(       (@@@%  #@@&, *@@@*      
%@@&. #@@&. (@@@,         ,&@@( .@@@(  &@@#      
%@@&. #@@&. /@@@,         *&@@( .@@@(  &@@#      
*@@@* .&@@#  %@@@#       %@@@#  #@@&, /@@@,      
 %@@&, *@@@%  .&@@@@@@@@@@@%. .&@@&, ,&@@%       
  %@@&*  %@@#     *#%%%#*     #@@%  *&@@%        
   (@@@&.                         .&@@&/         
     #@#                           #@#    

  ____  _       __       _        ____               
 / ___|(_)_  __/ _| __ _| |__    / ___|___  _ __ ___ 
 \___ \| \ \/ / |_ / _` | '_ \  | |   / _ \| '__/ _ \
  ___) | |>  <|  _| (_| | |_) | | |__| (_) | | |  __/
 |____/|_/_/\_\_|  \__,_|_.__/   \____\___/|_|  \___|
=====================================================
EOF

AGENT_REPOSITORY="https://github.com/sixfab/core_agent.git"
MANAGER_REPOSITORY="https://github.com/sixfab/core_manager.git"

VERBOSE_SUFFIX="/dev/null"
OS_DISTRO="Raspbian"

SIXFAB_PATH="/opt/sixfab"
CORE_PATH="$SIXFAB_PATH/core"
MANAGER_SOURCE_PATH="$CORE_PATH/manager"
AGENT_SOURCE_PATH="$CORE_PATH/agent"
ENV_PATH="$SIXFAB_PATH/.env.yaml"


print_help() {
    printf "[HELP]  $1\n"
}

print_info() {
    YELLOW='\033[0;33m'
    NC='\033[0m'
    printf "${YELLOW}[INFO]${NC}  $1\n"
}

print_error() {
    RED='\033[0;31m'
    NC='\033[0m'
    printf "${RED}[ERROR]${NC} $1\n"
}

print_done() {
    GREEN='\033[0;32m'
    NC='\033[0m'
    printf "${GREEN}[DONE]${NC}  $1\n"
}

help() {
    print_help "To install new device:"
    print_help 'sudo bash -c "$(curl -sN https://install.connect.sixfab.com)" -- -t TOKEN_HERE'
    print_help "To uninstall device:"
    print_help 'sudo bash -c "$(curl -sN https://install.connect.sixfab.com)" -- --uninstall'
    destroy_scroll_area
}

run_command() {
    for count in {1..3}; do 
        COMMAND=$1
        sudo su sixfab -c "eval $COMMAND" &> $VERBOSE_SUFFIX
        STATUS_CODE=$?
        
        if [ $STATUS_CODE -eq "0" ]; then
            return
        fi
    done

    print_error "Installer faced an error during the following command, please re-run installer"
    print_error "*****************************************************"
    printf "\033[0;31m[ERROR]\033[0m $COMMAND\n"
    print_error "*****************************************************"
    exit 1
}

initialize_parameters() {

    TOKEN=""
    B_TOKEN=""
    REGION="global"
    BOARD="RaspberryPi4"
    IS_DEV=False
    VERBOSE=False

    deployment_type="device"

    while getopts "t:p:r:b:dv" arg; do
        case "$arg" in
        t) TOKEN="$OPTARG" ;;
        p) B_TOKEN="$OPTARG" ;;
        r) REGION="$OPTARG" ;;
        b) BOARD="$OPTARG" ;;
        v) VERBOSE=True ;;
        d) IS_DEV=True ;;
        -) break ;;
        \?) ;;
        esac
    done

    if [ -z "$TOKEN" ] && [ -z "$B_TOKEN" ]; then
        print_error "Device token is missing"
        help
        exit 1
    fi

    if [ -z "$TOKEN" ]; then
        deployment_type="bulk"
    fi

    if [ "$VERBOSE" == True ]; then
        VERBOSE_SUFFIX="/dev/stdout"
    fi
}

check_is_root() {
    if [ $(id -u) != 0 ]; then
        print_error "This script must be run as root"
        exit 1
    fi
}

if [ "$1" = "--uninstall" ]; then
    print_info "Uninstall Sixfab Core..."

    if [ -d "$CORE_PATH" ]; then
        print_info "Removing source..."
        sudo rm -r $CORE_PATH &> $VERBOSE_SUFFIX
        print_info "Source removed."
    fi

    if [ -d "/home/sixfab/.core" ]; then
        print_info "Removing local files..."
        sudo rm -r /home/sixfab/.core &> $VERBOSE_SUFFIX
        print_info "Local files removed."
    fi

    if [ -e "$SIXFAB_PATH/.env.yaml" ]; then
        print_info "Removing environment file..."
        sudo rm -r $SIXFAB_PATH/.env.yaml &> $VERBOSE_SUFFIX
        print_info "Environment file removed."
    fi

    print_info "Stopping & removing services..."
    sudo systemctl stop core_agent &> $VERBOSE_SUFFIX
    sudo systemctl stop core_manager &> $VERBOSE_SUFFIX

    sudo rm /etc/systemd/system/core_agent.service &> $VERBOSE_SUFFIX
    sudo rm /etc/systemd/system/core_manager.service &> $VERBOSE_SUFFIX
    sudo systemctl daemon-reload
    print_info "Removed services."

    print_done "Sixfab Core is uninstalled successfully"

    exit 0
fi


check_distro() {
    OS_DETAILS=$(cat /etc/os-release)
    case "$OS_DETAILS" in
    *Raspbian*|*Debian*)
        OS_DISTRO="Raspbian"
        return
        ;;
    *Ubuntu*|*ubuntu*)
        OS_DISTRO="Ubuntu"
        return
        ;;
    esac

    read -p "[WARNING] The operating system is not one of the supported ones, Sixfab Core may not run properly. Do you want to continue? (y/N) " yn

    case "$yn" in
    *y*|*Y*)
        return
        ;;
    *)
        print_info "Installer is exiting..."
        exit 1
        ;;
    esac
}

check_usb_is_connected() {
    GREEN='\033[0;32m'
    RED='\033[0;31m'
    YELLOW='\033[0;33m'
    NC='\033[0m'

    LSUSB_OUTPUT=$(lsusb) > /dev/null 2>&1

    case "$LSUSB_OUTPUT" in
    *Telit*|*1b7c*|*Quectel*|*2c7c*)
        printf -- "-------\n"
        printf "${RED}Warning\n${NC}"
        printf -- "-------\n"
        printf "You are using the cellular modem. It may cause unintended data costs or failed installation!\n" 
        printf -- "-> Unplug the USB cable from the Sixfab HAT before continuing! (${GREEN}Recommended${NC})\n"
        printf -- "-> Then press ${YELLOW}ENTER${NC} to continue to installation.\n"
        read -r -p "" foo
        return
        ;;
    *)
        return
        ;;
    esac
}

update_system() {
    print_info "Updating system package index..."
    run_command "sudo apt update -y"
}

check_user() {
    create_sixfab_user() {
        sudo adduser --disabled-password --gecos "" sixfab &> $VERBOSE_SUFFIX
    }

    add_usb_permissions_to_plugdev() {
        PERMISSIONS_TO_ADD="SUBSYSTEM==\"usb\", ENV{DEVTYPE}==\"usb_device\", MODE=\"0664\", GROUP=\"plugdev\""
        PLUGDEV_RULES_PATH=/etc/udev/rules.d/plugdev_usb.rules

        if [ ! -f $PLUGDEV_RULES_PATH ]; then
            echo $PERMISSIONS_TO_ADD | sudo tee $PLUGDEV_RULES_PATH &> $VERBOSE_SUFFIX
            sudo udevadm control --reload
            sudo udevadm trigger
        fi
    }

    add_gpio_permissions_for_ubuntu() {   
        GPIO_RULES_PATH=/etc/udev/rules.d/gpio.rules
        
        if [ ! -f $GPIO_RULES_PATH ]; then
            echo "\"SUBSYSTEM==\"gpio\", GROUP=\"gpio\", MODE=\"0660\"
SUBSYSTEM==\"gpio*\", PROGRAM=\"/bin/sh -c '\
chown -R root:gpio /sys/class/gpio && chmod -R 770 /sys/class/gpio;\
chown -R root:gpio /sys/devices/virtual/gpio && chmod -R 770 /sys/devices/virtual/gpio;\
chown -R root:gpio /sys\$devpath && chmod -R 770 /sys\$devpath\
'\"" | sudo tee $GPIO_RULES_PATH &> $VERBOSE_SUFFIX
            
            sudo groupadd gpio
            sudo usermod -aG gpio sixfab
            sudo udevadm control --reload
            sudo udevadm trigger
        fi
    }

    check_sixfab_user_privilege() {
        add_usb_permissions_to_plugdev

        case "$OS_DISTRO" in
        *Raspbian*)
            sudo usermod -aG spi sixfab &> $VERBOSE_SUFFIX
            sudo usermod -aG i2c sixfab &> $VERBOSE_SUFFIX
            sudo usermod -aG gpio sixfab &> $VERBOSE_SUFFIX
            ;;
        *Ubuntu*|*ubuntu*)
            add_gpio_permissions_for_ubuntu
            ;;
        esac

        # common usermods
        sudo usermod -aG sudo sixfab &> $VERBOSE_SUFFIX
        sudo usermod -aG dialout sixfab &> $VERBOSE_SUFFIX
        sudo usermod -aG users sixfab &> $VERBOSE_SUFFIX
        sudo usermod -aG plugdev sixfab &> $VERBOSE_SUFFIX
    }

    if id -u "sixfab" &> $VERBOSE_SUFFIX; then
        print_info "Sixfab user already exists, updating..."
        check_sixfab_user_privilege
    else
        print_info "Creating sixfab user..."
        create_sixfab_user
        check_sixfab_user_privilege
    fi
}

initialize_sudoers() {
    print_info "Updating sudoers..."
    echo "sixfab ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/sixfab_core &> $VERBOSE_SUFFIX
    print_info "Sudoers updated"
}

check_system_dependencies() {
    git --version &> $VERBOSE_SUFFIX
    IS_GIT_INSTALLED=$?
    python3 --version &> $VERBOSE_SUFFIX
    IS_PYTHON_INSTALLED=$?
    pip3 --version &> $VERBOSE_SUFFIX
    IS_PIP_INSTALLED=$?

    if [ ! "$IS_GIT_INSTALLED" = "0" ] || [ ! "$IS_PYTHON_INSTALLED" = "0" ] || [ ! "$IS_PIP_INSTALLED" = "0" ]; then
        install_system_dependencies
    fi
}

install_system_dependencies() {
    print_info "Looking for dependencies..."

    # Check if git installed
    if ! [ -x "$(command -v git)" ]; then
        print_info 'Git is not installed, installing...'
        run_command "sudo apt install git -y"
    fi

    # Check if python3 installed
    if ! [ -x "$(command -v python3)" ]; then
        print_info 'Python3 is not installed, installing...'
        run_command "sudo apt install python3 -y"
    fi

    # Check python3 version, minimum python3.6 required
    version=$(python3 -V 2>&1 | grep -Po '(?<=Python )(.+)' | sed -e 's/\.//g')

    if [ "$version" -lt "360" ]; then
        print_error "Python 3.6 or later version is required to run Sixfab Core. Please upgrade Python and re-try. Using latest version of Raspberry Pi OS (Previously known as Raspbian OS) is recommended."
        exit
    fi

    # Check if pip3 installed
    if ! [ -x "$(command -v pip3)" ]; then
        print_info 'Pip for python3 is not installed, installing...'
        run_command "sudo apt install python3-pip -y"
    fi

    # Install python3-venv
    run_command "sudo apt install python3-venv -y"

    # Check if ifmetric installed
    if ! [ -x "$(command -v ifmetric)" ]; then
        print_info 'ifmetric is not installed, installing...'
        run_command "sudo apt install ifmetric -y"
    fi

    # Check if net-tools installed
    if ! [ -x "$(command -v route)" ]; then
        print_info 'net-tools is not installed, installing...'
        run_command "sudo apt install net-tools -y"
    fi

    # Check if lshw 
    if ! [ -x "$(command -v lshw)" ]; then
        print_info 'lshw is not installed, installing...'
        run_command "sudo apt install lshw -y"
    fi

    check_system_dependencies
}

remove_conflicting_packages() {
    # Check if modemmanager installed
    if [ -x "$(command -v ModemManager)" ]; then
        print_info 'Removing ModemManager...'
        run_command "sudo apt purge modemmanager -y"
    fi
}

check_network_layer_dependencies() {
    
    # Configuration for compatibility of systemd-networkd service
    # on ubuntu-server with raspberry pi hardware
    configure_systemd_networkd_for_ubuntu(){
        NETWORKD_CONF_PATH="/etc/systemd/network/10-allifs.network"
        echo "[Match]
Name=*

[Network]
DHCP=true" | sudo tee $NETWORKD_CONF_PATH &> $VERBOSE_SUFFIX

        sudo systemctl restart systemd-networkd.service
    }


    case "$BOARD" in
    *Raspberry*)
        case "$OS_DISTRO" in
        *Raspbian*)
            print_info "Board: Raspberry Pi - OS: Raspbian"
            ;;
        *Ubuntu*)
            print_info "Board: Raspberry Pi - OS: Ubuntu"
            configure_systemd_networkd_for_ubuntu
            ;;
        esac
        ;;
    *Jetson*)
        print_info "Board: Nvidia Jetson Nano - OS: Ubuntu"
    ;;
    esac
}


install_agent() {
    if [ -d "$AGENT_SOURCE_PATH" ]; then
        print_info "Agent source already exists, updating..."
        git -C $AGENT_SOURCE_PATH reset --hard HEAD &> $VERBOSE_SUFFIX
        git -C $AGENT_SOURCE_PATH pull &> $VERBOSE_SUFFIX
    else
        print_info "Downloading agent source..."
        git clone $AGENT_REPOSITORY $AGENT_SOURCE_PATH &> $VERBOSE_SUFFIX
        sudo chown sixfab:sixfab $AGENT_SOURCE_PATH -R &> $VERBOSE_SUFFIX
        git config --global --add safe.directory $AGENT_SOURCE_PATH &> $VERBOSE_SUFFIX
    fi

    apply_dev_mode_if_necessary "agent"

    print_info "Creating virtual environment and installing agent requirements..."
    sudo python3 -m venv $AGENT_SOURCE_PATH/venv &> $VERBOSE_SUFFIX
    source $AGENT_SOURCE_PATH/venv/bin/activate &> $VERBOSE_SUFFIX
    pip3 install -r $AGENT_SOURCE_PATH/requirements.txt --no-cache-dir &> $VERBOSE_SUFFIX
    deactivate &> $VERBOSE_SUFFIX
    
    print_info "Initializing agent service..."
    sed -i "s|AGENT_SOURCE_PATH|$AGENT_SOURCE_PATH|g" $AGENT_SOURCE_PATH/core_agent.service
    mv $AGENT_SOURCE_PATH/core_agent.service /etc/systemd/system/core_agent.service &> $VERBOSE_SUFFIX
    sudo chown sixfab /etc/systemd/system/core_agent.service &> $VERBOSE_SUFFIX
    sudo systemctl daemon-reload &> $VERBOSE_SUFFIX
    sudo systemctl enable core_agent &> $VERBOSE_SUFFIX
    restart_sixfab_services "agent"

    print_info "Agent service initialized successfully."
}

install_manager() {
    if [ -d "$MANAGER_SOURCE_PATH" ]; then
        print_info "Manager source already exists, updating..."
        git -C $MANAGER_SOURCE_PATH reset --hard HEAD &> $VERBOSE_SUFFIX
        git -C $MANAGER_SOURCE_PATH pull &> $VERBOSE_SUFFIX
    else
        print_info "Downloading manager source..."
        git clone $MANAGER_REPOSITORY $MANAGER_SOURCE_PATH &> $VERBOSE_SUFFIX
        sudo chown sixfab:sixfab $MANAGER_SOURCE_PATH -R &> $VERBOSE_SUFFIX
        git config --global --add safe.directory $MANAGER_SOURCE_PATH &> $VERBOSE_SUFFIX
    fi

    apply_dev_mode_if_necessary "manager"

    print_info "Creating virtual environment and installing manager requirements..."
    sudo python3 -m venv $MANAGER_SOURCE_PATH/venv &> $VERBOSE_SUFFIX
    source $MANAGER_SOURCE_PATH/venv/bin/activate &> $VERBOSE_SUFFIX
    pip3 install -r $MANAGER_SOURCE_PATH/requirements.txt --no-cache-dir &> $VERBOSE_SUFFIX
    deactivate &> $VERBOSE_SUFFIX
    print_info "Virtual environment deactivated."

    print_info "Initializing manager service..."
    sed -i "s|MANAGER_SOURCE_PATH|$MANAGER_SOURCE_PATH|g" $MANAGER_SOURCE_PATH/core_manager.service
    mv $MANAGER_SOURCE_PATH/core_manager.service /etc/systemd/system/core_manager.service &> $VERBOSE_SUFFIX
    sudo chown sixfab /etc/systemd/system/core_manager.service &> $VERBOSE_SUFFIX
    sudo systemctl daemon-reload &> $VERBOSE_SUFFIX
    sudo systemctl enable core_manager &> $VERBOSE_SUFFIX
    restart_sixfab_services "manager"

    print_info "Manager service initialized successfully."
}

check_sixfab_folder() {
    if [ ! -d "$SIXFAB_PATH" ]; then
        sudo mkdir $SIXFAB_PATH
    fi

    if [ ! -d "$CORE_PATH" ]; then
        sudo mkdir $CORE_PATH
    fi

    sudo chown sixfab:sixfab $SIXFAB_PATH -R
}

initialize_environment_file() {
    print_info "Initializing environment file..."

    # Create venv to run python script
    python3 -m venv venv_temp &> $VERBOSE_SUFFIX
    source venv_temp/bin/activate &> $VERBOSE_SUFFIX
    pip3 install pyyaml --no-cache-dir&> $VERBOSE_SUFFIX

    venv_temp/bin/python3 -c "
import yaml

region = '$REGION'
board = '$BOARD'
is_dev = $IS_DEV

try:
    with open('$SIXFAB_PATH/.env.yaml', 'r') as env_file:
        environments = yaml.safe_load(env_file)
except:
    environments = {}

if not 'core' in environments:
    environments['core'] = {}

elif not is_dev and 'MQTT_HOST' in environments['core']:
    del environments['core']['MQTT_HOST']

if '$TOKEN':
    environments['core']['token'] = '$TOKEN'

if '$B_TOKEN':
    environments['core']['b_token'] = '$B_TOKEN'

if is_dev:
    environments['core']['MQTT_HOST'] = 'mqtt.connect.sixfab.dev'

if region == 'global' and environments['core'].get('apn', False):
    environments['core'].pop('apn', False)
elif region == 'emea':
    environments['core']['apn'] = 'de1.super'
elif region == 'apac':
    environments['core']['apn'] = 'sg1.super'

environments['core']['board'] = board

with open('$SIXFAB_PATH/.env.yaml', 'w') as env_file:
    yaml.dump(environments, env_file)
"
    print_info "Initialized environment file"

    print_info "Temporary environment removing"
    deactivate &> $VERBOSE_SUFFIX
    rm -rf venv_temp &> $VERBOSE_SUFFIX
    sudo chown sixfab:sixfab $SIXFAB_PATH/.env.yaml &> $VERBOSE_SUFFIX
}


apply_dev_mode_if_necessary() {
    if [ "$IS_DEV" == True ]; then
    
        if [ $1 == "manager" ]; then
            print_info "Applying dev mode to manager..."
            cd $MANAGER_SOURCE_PATH/ &> $VERBOSE_SUFFIX
            sudo git reset --hard HEAD &> $VERBOSE_SUFFIX
            sudo git checkout dev &> $VERBOSE_SUFFIX
            restart_sixfab_services "manager"
        fi

        if [ $1 == "agent" ]; then
            print_info "Applying dev mode to agent..."
            cd $AGENT_SOURCE_PATH/ &> $VERBOSE_SUFFIX
            sudo git reset --hard HEAD &> $VERBOSE_SUFFIX
            sudo git checkout dev &> $VERBOSE_SUFFIX
            restart_sixfab_services "agent"
        fi
    fi
}


restart_sixfab_services(){
    if [ $deployment_type == "bulk" ]; then
        if [ $1 == "agent" ]; then
            sudo systemctl stop core_agent &> $VERBOSE_SUFFIX
        elif [ $1 == "manager" ]; then
            sudo systemctl stop core_manager &> $VERBOSE_SUFFIX
        fi
    else
        if [ $1 == "agent" ]; then
            sudo systemctl restart core_agent &> $VERBOSE_SUFFIX
        elif [ $1 == "manager" ]; then
            sudo systemctl restart core_manager &> $VERBOSE_SUFFIX
        fi
    fi
}


reboot_system() {
    GREEN='\033[0;32m'
    RED='\033[0;31m'
    YELLOW='\033[0;33m'
    NC='\033[0m'
    
    remove_conflicting_packages

    if [ $deployment_type == "bulk" ]; then
    printf "\n"
    printf -- "-----------------------------------------------------------------------\n"
    printf "Bulk deployment image is ready. You can use this image to deploy multiple CORE devices\n"
    printf "Follow instructions at the link below to copy os image and create multiple SD cards\n"
    printf "${YELLOW}Link:${NC} https://docs.sixfab.com/page/bulk-deployments#cloning-the-sd-card"
    printf "\n"
    printf "Press ${YELLOW}ENTER${NC} to power off your system as the first step.\n" 
    printf "Press ${YELLOW}Ctrl+C${NC} (^C) to finish installation without power off.\n" 
    printf -- "-----------------------------------------------------------------------\n"
    printf "\n"
    read -r -p "" foo
    sudo poweroff
    else
    printf "\n"
    printf -- "-----------------------------------------------------------------------\n"  
    printf "Press ${YELLOW}ENTER${NC} to reboot your system. (${GREEN}Recommended${NC})\n" 
    printf "Press ${YELLOW}Ctrl+C${NC} (^C) to finish installation without reboot.\n" 
    printf "\n"
    printf "${GREEN}Reminder${NC}: Plug the USB cable to Sixfab HAT!\n"
    printf "${RED}Warning${NC}: Network priority settings will be effective after reboot!\n"
    printf -- "-----------------------------------------------------------------------\n"
    printf "\n"
    read -r -p "" foo
    sudo reboot
    fi
}

### Progress Bar ###
# Revised by selengalp (yasinkaya.121@gmail.com) on 28/11/2021
# Main Source: https://github.com/pollev/bash_progress_bar

# Constants
CODE_SAVE_CURSOR="\033[s"
CODE_RESTORE_CURSOR="\033[u"
CODE_CURSOR_IN_SCROLL_AREA="\033[1A"
COLOR_FG="\e[32m"
COLOR_BG="\e[47m"
COLOR_BG_BLOCKED="\e[43m"
RESTORE_FG="\e[39m"
RESTORE_BG="\e[49m"

# Variables
PROGRESS_BLOCKED="false"
TRAPPING_ENABLED="false"
TRAP_SET="false"

CURRENT_NR_LINES=0

setup_scroll_area() {
    # If trapping is enabled, we will want to activate it whenever 
    # we setup the scroll area and remove it when we break the scroll area
    if [ "$TRAPPING_ENABLED" = "true" ]; then
        trap_on_interrupt
    fi

    lines=$(tput lines)
    CURRENT_NR_LINES=$lines
    let lines=$lines-1
    # Scroll down a bit to avoid visual glitch when the screen area shrinks by one row
    echo -en "\n"

    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"
    # Set scroll region (this will place the cursor in the top left)
    echo -en "\033[0;${lines}r"

    # Restore cursor but ensure its inside the scrolling area
    echo -en "$CODE_RESTORE_CURSOR"
    echo -en "$CODE_CURSOR_IN_SCROLL_AREA"

    # Start empty progress bar
    draw_progress_bar 0
}

destroy_scroll_area() {
    lines=$(tput lines)
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"
    # Set scroll region (this will place the cursor in the top left)
    echo -en "\033[0;${lines}r"

    # Restore cursor but ensure its inside the scrolling area
    echo -en "$CODE_RESTORE_CURSOR"
    echo -en "$CODE_CURSOR_IN_SCROLL_AREA"

    # We are done so clear the scroll bar
    clear_progress_bar

    # Scroll down a bit to avoid visual glitch when the screen area grows by one row
    echo -en "\n\n"

    # Once the scroll area is cleared, we want to remove any trap previously set. Otherwise, ctrl+c will exit our shell
    if [ "$TRAP_SET" = "true" ]; then
        trap - INT
    fi
}

draw_progress_bar() {
    percentage=$1
    lines=$(tput lines)
    let lines=$lines

    # Check if the window has been resized. If so, reset the scroll area
    if [ "$lines" -ne "$CURRENT_NR_LINES" ]; then
        setup_scroll_area
    fi

    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"

    # Move cursor position to last row
    echo -en "\033[${lines};0f"

    # Clear progress bar
    tput el

    # Draw progress bar
    PROGRESS_BLOCKED="false"
    print_bar_text $percentage

    # Restore cursor position
    echo -en "$CODE_RESTORE_CURSOR"
}

block_progress_bar() {
    percentage=$1
    lines=$(tput lines)
    let lines=$lines
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"

    # Move cursor position to last row
    echo -en "\033[${lines};0f"

    # Clear progress bar
    tput el

    # Draw progress bar
    PROGRESS_BLOCKED="true"
    print_bar_text $percentage

    # Restore cursor position
    echo -en "$CODE_RESTORE_CURSOR"
}

clear_progress_bar() {
    lines=$(tput lines)
    let lines=$lines
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"

    # Move cursor position to last row
    echo -en "\033[${lines};0f"

    # clear progress bar
    tput el

    # Restore cursor position
    echo -en "$CODE_RESTORE_CURSOR"
}

print_bar_text() {
    local percentage=$1
    local cols=$(tput cols)
    let bar_size=$cols-30

    local color="${COLOR_FG}${COLOR_BG}"
    if [ "$PROGRESS_BLOCKED" = "true" ]; then
        color="${COLOR_FG}${COLOR_BG_BLOCKED}"
    fi

    # Prepare progress bar
    let complete_size=($bar_size*$percentage)/100
    let remainder_size=$bar_size-$complete_size
    progress_bar=$(echo -ne ""; echo -en "${color}"; printf_new "█" $complete_size; echo -en "${RESTORE_FG}${RESTORE_BG}"; printf_new "" $remainder_size; echo -ne "");
    
    # Print progress bar
    echo -ne "Total Progress-> ${progress_bar} ${percentage}%"
}

enable_trapping() {
    TRAPPING_ENABLED="true"
}

trap_on_interrupt() {
    # If this function is called, we setup an interrupt handler to cleanup the progress bar
    TRAP_SET="true"
    trap cleanup_on_interrupt INT
}

cleanup_on_interrupt() {
    destroy_scroll_area
    exit
}

printf_new() {
    str=$1
    num=$2
    v=$(printf "%-${num}s" "$str")
    echo -ne "${v// /$str}"
}
### End of progress bar ###

main() {

    enable_trapping
    setup_scroll_area

    initialize_parameters "$@"
    check_is_root
    check_distro
    check_usb_is_connected
    draw_progress_bar 5

    check_user
    draw_progress_bar 7

    initialize_sudoers
    draw_progress_bar 10
    
    update_system
    draw_progress_bar 20

    install_system_dependencies
    draw_progress_bar 30

    check_network_layer_dependencies
    draw_progress_bar 40

    check_system_dependencies
    draw_progress_bar 50

    check_sixfab_folder
    initialize_environment_file
    draw_progress_bar 60

    install_agent
    draw_progress_bar 75

    install_manager
    draw_progress_bar 100

    print_done "Installation completed successfully."
    reboot_system
    destroy_scroll_area
}

main "$@"