#!/bin/bash

############################################################
#keithhedger
#Tue 16 Apr 13:49:54 BST 2024
#Built using: makeswitch -c ViCp.conf
############################################################

if [ -e vicp.include ];then
	. vicp.include
fi

OPTCOUNT=0

vicpargs ()
{

APPNAME="vicp"
VERSION="0.0.5"

NOGUI=0
RECURSIVE=0
RECURSIVE=0

while [ $# -gt 0 ]
do
	case "$1" in

		-X|--nogui)
			NOGUI=1
			;;
		-r|--recursive)
			RECURSIVE=1
			;;
		-R|--Recursive)
			RECURSIVE=1
			;;
		'-?'|--help|-h)
			echo $APPNAME - Version $VERSION
			echo "Useage: $APPNAME [option] ..."
			echo "-X|--nogui : No Gui Display"
			echo "-r|--recursive : I do recurse"
			echo "-R|--Recursive : I do recurse"
			echo -e "Calling script as 'vimv' will move files instead of copying"
			exit 0
			;;
		-v|--version)
			echo $APPNAME - Version $VERSION
			exit 0
			;;
		*)
			OPTS[$OPTCOUNT]="$1"
			(( OPTCOUNT++ ))
			;;
	esac
	shift
done

}
###CUT###

trap "leavehere" EXIT HUP INT TERM KILL QUIT

libinit ()
{
	BLACK=0
	RED=1
	GREEN=2
	YELLOW=3
	BLUE=4
	MAJ=5
	CYAN=6
	WHITE=7

	NORMCHARSET="\e(B"

	CURRENTFORE=0
	CURRENTBACK=7
	CURRENTSET=$NORMCHARSET
	CURRENTBOLD=0
	CURRENTBB=0

	UPL=┌
	UPR=┐
	LL=└
	LR=┘
	VBAR=│
	HBAR=─

	CURRENTX=-1
	CURRENTY=-1

	CURSORXSTACK=""
	CURSORYSTACK=""
	STACKCNT=-1

	LINES=$(tput lines)
	COLS=$(tput cols)

	HIDECURSOR="\e[?25l"
	SHOWCURSOR="\e[?25h"
}

libinit

NUMARGS=0
FILECNT=0
BOXHITE=13
BOXWID=$((COLS-12))
MIDX=$((COLS/2))
MIDY=$((LINES/2))
BARWID=$((BOXWID-8))
STARTX=$((6+4))
DESTISDIR=0
ISMOVE=0
DIALOGNAME="ViCp"
DIALOGMESSAGE="Copying"
DIALOGSUBMESSAGE="Copied"

declare -A FILELIST

leavehere ()
{
	trap - EXIT HUP INT TERM KILL QUIT
#	killall vicp &>/dev/null
#make sure all the lights are off
#	kill -9 $CPPID &>/dev/null
#cos somtimes dialog screws terminal
	stty sane 2>/dev/null
	setterm -cursor on 2>/dev/null
	putcursor 0 $LINES

	exit 0
}

putcursor ()
{
	echo -en "\e[$((${2}));$((${1}))H"
}

setglobalattr ()
{
	while [ "${#@}" -gt 0 ]
		do	
			case $1 in
			
				fore)
					CURRENTFORE=$2
					shift
					;;
				back)
					CURRENTBACK=$2
					shift
					;;
				bold)
					CURRENTBOLD=$2
					shift
					;;
				bright)
					if [ $2 -eq 0 ];then
						CURRENTBB=0
					else
						CURRENTBB=60
					fi
					shift
					;;
				charset)
					CURRENTSET=$NORMCHARSET
					shift
					;;
			esac
			
			shift
		done
	resetattrs
}

clearLine ()
{
	local i

	for i in $(seq $1)
		do
			echo -ne " "
		done
}

brightOn ()
{
	CURRENTBB=60
	echo -en "\e[$CURRENTBOLD;$((30+$CURRENTFORE));$((40+$CURRENTBACK+$CURRENTBB))m$CURRENTSET"
}

boldOn ()
{
	CURRENTBOLD=1
	echo -en "\e[$CURRENTBOLD;$((30+$CURRENTFORE));$((40+$CURRENTBACK+$CURRENTBB))m$CURRENTSET"
}

getcursor ()
{
	local tmpcursor

	stty -echo
	echo -n $'\e[6n'
	read -d R tmpcursor
	stty echo
	tmpcursor=${tmpcursor#??}
	CURRENTX=$((${tmpcursor##*;}))
	CURRENTY=$((${tmpcursor%%;*}))
}

resetcursorx ()
{
	echo -en "\e[${CURRENTX}G"
}

movecursor ()
{
#$1=move x $2=move y
	if (( ${1}!=0 ));then
		if (( $1<0 ));then
			echo -en "\e[${1#-}D"
		else
			echo -en "\e[${1}C"
		fi
	fi

	if (( ${2}!=0 ));then
		if (( $2<0 ));then
			echo -en "\e[${2#-}A"
		else
			echo -en "\e[${2}B"
		fi
	fi
}

drawbox ()
{
	local width=$1 height=$2 boxtype=$3 fill=$4
	local topcolour bottomcolour
#	local fill=0
	local i
	local j

	((width=width-2))
	((height=height-2))

	getcursor
	case $boxtype in
		unfilled)
			topcolour=$( createcol bold 1 back $CYAN fore $CYAN bright 0 charset 1 )
			bottomcolour=$( createcol bold 0 fore $BLACK charset 1 back $CYAN bright 0 )
			fill=0
			;;
		dialog)
			topcolour=$( createcol bold 1 back $CYAN fore $CYAN bright 0 charset 1 )
			bottomcolour=$( createcol bold 0 fore $BLACK charset 1 back $CYAN bright 0 )
			fill=1
			;;			
		dialoginput)
			topcolour=$( createcol bold 0 fore $BLACK charset 1 back $CYAN bright 0 )
			bottomcolour=$( createcol bold 1 back $CYAN fore $CYAN bright 0 charset 1 )
			fill=1
			;;			
		box)
			topcolour=$(createcol fore $4 charset 1 )
			bottomcolour=$(createcol fore $5 charset 1)
			;;
		fill)
			topcolour=$(createcol fore $4 charset 1 back $6 )
			bottomcolour=$(createcol fore $5 charset 1 back $6)
			fill=1
			;;
	esac
#top
	echo -ne "$topcolour$UPL"
	for i in $(seq $width)
		do
			echo -ne "$HBAR"
		done
		echo -e "$bottomcolour$UPR"
#middle

	for j in $(seq $height)
		do
			resetcursorx
			echo -ne "$topcolour$VBAR"
			if ((fill==0));then
				movecursor $width 0
			else
				for i in $(seq $width)
					do
						echo -n " "
					done
			fi
			echo -e "$bottomcolour$VBAR"
		done
#bottom

	echo -ne "\e[${CURRENTX}G$topcolour$LL"
	for i in $(seq $width)
		do
			echo -ne "$bottomcolour$HBAR"
		done
	echo -ne $bottomcolour$LR
}

pushcursor ()
{
	getcursor
	((STACKCNT=STACKCNT+1))
	CURSORXSTACK[$STACKCNT]=$CURRENTX
	CURSORYSTACK[$STACKCNT]=$CURRENTY
}

popcursor ()
{
	putcursor ${CURSORXSTACK[$STACKCNT]} ${CURSORYSTACK[$STACKCNT]}
	CURRENTX=${CURSORXSTACK[$STACKCNT]}
	CURRENTY=${CURSORYSTACK[$STACKCNT]}
	((STACKCNT=$STACKCNT-1))
}

resetattrs ()
{
	echo -en "\e[$CURRENTBOLD;$((30+$CURRENTFORE));$((40+$CURRENTBACK+$CURRENTBB))m$CURRENTSET"
}

saveattr ()
{
	SAVEBOLD=$CURRENTBOLD
	SAVEFORE=$CURRENTFORE
	SAVEBACK=$CURRENTBACK
	SAVEBB=$CURRENTBB
	SAVESET=$CURRENTSET
}

restoreattr ()
{
	CURRENTBOLD=$SAVEBOLD
	CURRENTFORE=$SAVEFORE
	CURRENTBACK=$SAVEBACK
	CURRENTBB=$SAVEBB
	CURRENTSET=$SAVESET
	resetattrs
}

createcol ()
{
	local arg colour
	local fore=$CURRENTFORE
	local back=$CURRENTBACK
	local bold=$CURRENTBOLD
	local charset=$CURRENTSET
	local brightback=$CURRENTBB

	while [ "${#@}" -gt 0 ]
		do	
			case $1 in
			
				fore)
					fore=$2
					shift
					;;
				back)
					back=$2
					shift
					;;
				bold)
					bold=$2
					shift
					;;
				bright)
					if [ $2 -eq 0 ];then
						brightback="0"
					else
						brightback="60"
					fi
					shift
					;;
				charset)
					charset=$NORMCHARSET
					shift
					;;
			esac
			
			shift
		done

	colour="\e[$bold;$((30+$fore));$((40+${back}+${brightback}))m$charset"
	echo $colour
}

setForeColour ()
{
	CURRENTFORE=$1
	echo -en "\e[$CURRENTBOLD;$((30+$CURRENTFORE));$((40+$CURRENTBACK+$CURRENTBB))m$CURRENTSET"
}

setBackColour ()
{
	CURRENTBACK=$1
	echo -en "\e[$CURRENTBOLD;$((30+$CURRENTFORE));$((40+$CURRENTBACK+$CURRENTBB))m$CURRENTSET"
}

drawbar ()
{
#$1=len $2=perc $3 barcolour
	local slices fill
	local barsize=$1 perc=$2
	local cnt
	local middle=$((barsize/2))
	local bar
	local percoffset

	percoffset=${#perc}
	
	slices=$(echo "scale=2 ;($barsize/100)"|bc -l 2>/dev/null)
	fill=$(echo "scale=2 ;$slices*$perc+0.5"|bc -l 2>/dev/null )
	fill=${fill%.[[:digit:]]*}

	pushcursor
		drawbox $((barsize+2)) 3 unfilled
	popcursor
	movecursor 1 1
	for cnt in $(seq $barsize)
		do
			bar="${bar} "
		done
	solidcolour=$(createcol back $3 fore $CYAN bold 0)
	barcolour=$(createcol back $CYAN fore $3 bold 1 bright 0)
	bar="${bar:0:middle-percoffset}${perc}%${bar:middle-percoffset:middle-1}"
	echo -e "${solidcolour}${bar:0:fill}${barcolour}${bar:fill:barsize-fill}"
	resetattrs
}

#drawhline startx starty wid attr
drawHLine ()
{
	local i

	saveattr
	putcursor $1 $2
	for i in $(seq $3)
		do
			echo -ne "${4}$HBAR"
		done
	restoreattr
}

#drawhline startx starty hite attr
drawVLine ()
{
	local i

	saveattr
	putcursor $1 $2
	for i in $(seq $3)
		do
			putcursor $1 $((i+${2}))
			echo -ne "${4}$VBAR"
		done
	restoreattr
}

#inwid inhite title
drawDialogBox ()
{
	local innerwidth=$1 innerhite=$2
	local boxx boxy

	boxx=$(((COLS/2)-(innerwidth/2)+1))
	boxy=$(((LINES/2)-(innerhite/2)))

	tput setb 1
	setterm -back $BLUE 
	clear
	putcursor 0 2
	drawHLine 0 2 $COLS $(createcol fore $WHITE back $BLUE charset 1)
	putcursor 0 0
	echo -en "$(createcol fore $WHITE back $BLUE)$3"

	putcursor $boxx $boxy
	drawbox $innerwidth $innerhite "dialog" 0 0 0
	drawHLine $((boxx+2)) $((boxy+innerhite)) $innerwidth $(createcol fore $BLACK back $BLACK charset 1)
	drawVLine $((boxx+innerwidth)) $((boxy)) $innerhite $(createcol fore $BLACK back $BLACK charset 1)
	drawVLine $((boxx+innerwidth+1)) $((boxy)) $innerhite $(createcol fore $BLACK back $BLACK charset 1)
}

doDialogFilenames()
{
	local boxwid=$((COLS-12))
	local boxhite=$((13))
	local startx=$((6+4))
	local voffset=$((MIDY-(boxhite/2)+1))
	local clearfrom=$((boxwid-9))
	local clearto=$((boxwid-10))

	putcursor $((startx+5)) $((5+voffset))
	setglobalattr back $CYAN bright 0
	clearLine $clearfrom
	putcursor $((startx+6)) $((6+voffset))
	clearLine $clearto

	putcursor $startx $((5+voffset))
	echo -en "${VICPYELLOW}From:${VICPBLACK}$1"

	putcursor $startx $((6+voffset))
	echo -en "${VICPYELLOW}To:${VICPBLACK}$2"

	putcursor $startx $((1+voffset))
	echo -en "${VICPYELLOW}Overall Progress"
	brightOn
}

doDisplayNoGUI ()
{
	local filenum=$1
	local tosize=$2
	local fromsize=$3
	local fileperc=$4
	local overallperc=$5
	local filename="$6"

	if [ $FILECNT -eq 1 ];then
		putcursor 0 $((LINES-1))
		echo -en "\r${DIALOGSUBMESSAGE} ${tosize}B of ${fromsize}B (${fileperc%.?*}%) to ${filename}\e[K"
	else
		putcursor 0 $((LINES-1))
		echo -e "${DIALOGMESSAGE} $filenum Of $FILECNT (${overallperc%.?*}%)"
		echo -en "\r${DIALOGSUBMESSAGE} ${tosize}B of ${fromsize}B (${fileperc%.?*}%) to ${filename}\e[K"
	fi
}

doDisplay ()
{
	local tosize=$2
	local fromsize=$3
	local filenum=$1
	local voffset
	local fileperc=$4
	local overallperc=$5

	voffset=$((MIDY-(BOXHITE/2)+1))

	putcursor $STARTX $((voffset))
	echo -en "${VICPYELLOW}File ${VICPBLACK}$filenum ${VICPYELLOW}Of ${VICPBLACK}$FILECNT"

	putcursor $STARTX $((7+voffset))
	echo -en "${VICPYELLOW}${DIALOGSUBMESSAGE} ${VICPBLACK}$tosize${VICPYELLOW} Of ${VICPBLACK}$fromsize"

	putcursor $STARTX $((8+voffset))
	drawbar $BARWID $fileperc $YELLOW

	putcursor $STARTX $((2+voffset))
	drawbar $BARWID $overallperc $YELLOW
}

resetTerminal ()
{
	clear
	libinit
	BOXHITE=13
	BOXWID=$((COLS-12))
	MIDX=$((COLS/2))
	MIDY=$((LINES/2))
	BARWID=$((BOXWID-8))
	STARTX=$((6+4))
	VICPYELLOW=$(createcol fore $YELLOW bold 1 back $CYAN)
	VICPBLACK=$(createcol fore $BLACK bold 0 back $CYAN bright 0)

	if [ $NOGUI -eq 0 ];then
		drawDialogBox $BOXWID $BOXHITE $DIALOGNAME
		putcursor $((MIDX-8)) $((MIDY-(BOXHITE/2)))
		setForeColour $YELLOW
		setBackColour $CYAN
		boldOn
		echo -en "${NORMCHARSET}${DIALOGMESSAGE} Files..."
	fi
}

copyFile ()
{
	local fromsize tosize pct
	local percent

	fromsize=$(stat -c %s "$1" 2>/dev/null)
	tosize=0
	percent=$(echo "scale=2 ;100/($FILECNT/$3)"|bc -l 2>/dev/null )
	percent=${percent%.?*}
	percent=${percent:-0}

	if [[ $ISMOVE -eq 0 ]];then
		cp $CPFLAGS "$1" "$2" &>/dev/null &
	else
		mv "$1" "$2" &>/dev/null &
	fi
	CPPID=$!
	if ((fromsize==0));then
		if [ $NOGUI -eq 1 ];then
			doDisplayNoGUI "$3" $tosize $fromsize 100 $percent "$2"
		else
			doDialogFilenames "$1" "$2"
			doDisplay "$3" $tosize $fromsize 100 $percent
		fi
		return
	fi

#this for speed
	if [ $NOGUI -eq 1 ];then
		while (( fromsize>tosize )) && ps $CPPID &>/dev/null
			do
				if [ $(tput lines) -ne $LINES ] || [ $(tput cols) -ne $COLS ];then
					resetTerminal
				fi
				tosize=$(stat -c %s "$2" 2>/dev/null)
				pct=$(( 100*(tosize)/(fromsize) ))
				doDisplayNoGUI "$3" $tosize $fromsize $pct $percent "$2"
			done
		tosize=$(stat -c %s "$2" 2>/dev/null)
		doDisplayNoGUI "$3" $tosize $fromsize 100 $percent "$2"
		echo
		echo
	else
		doDialogFilenames "$1" "$2"
		while (( fromsize>tosize )) && ps $CPPID &>/dev/null
			do
				if [ $(tput lines) -ne $LINES ] || [ $(tput cols) -ne $COLS ];then
					resetTerminal
					doDialogFilenames "$1" "$2"
					doDisplay "$3" $tosize $fromsize 100 $percent
				fi
				tosize=$(stat -c %s "$2" 2>/dev/null)
				pct=$(( 100*(tosize)/(fromsize) ))
				doDisplay "$3" $tosize $fromsize $pct $percent
			done
		tosize=$(stat -c %s "$2" 2>/dev/null)
		doDisplay "$3" $tosize $fromsize 100 $percent
	fi
}

FOLDERLIST=""

buildFileList ()
{
	local d1
	local d2

	d1=1
	d2=1
	FILELIST[0,0]=0
	FILECNT=0

	for ((i=0;i<${#OPTS[@]}-1;i++))
		do
			d2=1
#skip folder if no recurse.
			if [ $RECURSIVE -eq 0 ] && [ -d "${OPTS[i]}" ];then
				echo "vicp: omitting directory '"${OPTS[i]}"'"
				continue
			fi
			while read
				do
					if [[ ! -L "$REPLY" ]] && [[ -d "$REPLY" ]];then
						FOLDERLIST="${REPLY}\n${FOLDERLIST}"
					fi
					FILELIST[$d1,$d2]="$REPLY"
					(( d2++ ))
					if [ -f "$REPLY" ];then
						(( FILECNT++ ))
					fi
				done < <(find "${OPTS[i]}")
#number of sub strings
				FILELIST[$d1,0]=$d2
				(( d1++ ))
		done
#number of 2d arrays
	FILELIST[0,0]=$d1
}

getDestination ()
{
	if ((NUMARGS==2)) && [ -d "${OPTS[1]}" ];then
		echo "${OPTS[1]}/$(basename "${OPTS[0]}")"
		return
	fi

	if ((NUMARGS==2));then
		echo "${OPTS[1]}"
		return
	fi

	if ((NUMARGS>2)) && [ -d "${OPTS[NUMARGS-1]}" ];then
		echo "${OPTS[NUMARGS-1]}"
		return
	fi
}

getLastArg ()
{
	echo "${OPTS[NUMARGS-1]}"
}

optionCheck ()
{
	local loop

	DESTISDIR=0
	if [ $NUMARGS -lt 2 ];then
		echo "cp: missing destination file operand after ${OPTS[0]}"
		exit 1
	fi

	if [ $NUMARGS -gt 1 ];then
		for((loop=0;loop<NUMARGS-1;loop++))
			do
				if ! stat -c %s "${OPTS[loop]}" &>/dev/null;then
					echo "cp: cannot 'stat ${OPTS[0]}': No such file or directory XXX"
					exit 1
				fi
			done
	fi

	if [ $NUMARGS -eq 2 ] && [ -d "${OPTS[0]}" ] && [ -f "${OPTS[1]}" ];then
		echo "cp: cannot overwrite non-directory '${OPTS[1]}' with directory '${OPTS[0]}'"
		exit 1
	fi

	if [ $NUMARGS -gt 2 ] && [ ! -d  ${OPTS[NUMARGS-1]} ];then
		echo "cp: target '${OPTS[NUMARGS-1]}' is not a directory"
		exit 1
	fi

	if [ $NUMARGS -eq 2 ] && [ -d "${OPTS[0]}" ];then
		if [ ! -e "${OPTS[1]}" ];then
			DESTISDIR=1
		else
			DESTISDIR=2
		fi
	fi

	if [ $NUMARGS -eq 2 ] && [ ! -d "${OPTS[0]}" ];then
		DESTISDIR=3
	fi

	if [ $NUMARGS -eq 2 ] && [ -d "${OPTS[1]}" ];then
		DESTISDIR=4
	fi
}

printfiles ()
{
	local j
	local k
	local tfold
	local destdir
	local lastarg="$(getLastArg)"
	local domove=0
	local stripfolder=""
	local cnt=0
	local uselastarg=0

	if [ $NOGUI -eq 0 ];then
		drawDialogBox $BOXWID $BOXHITE $DIALOGNAME
		putcursor $((MIDX-8)) $((MIDY-(BOXHITE/2)))
		setForeColour $YELLOW
		setBackColour $CYAN
		boldOn
		echo -en "${NORMCHARSET}${DIALOGMESSAGE} Files..."
	else
		echo
	fi

	if [ ! -e "$lastarg" ];then
		lastarg=$(dirname "${lastarg}")/tmpfold$$
		domove=1
	fi

	if [ $DESTISDIR -eq 3 ];then
		lastarg="$(getLastArg)"	
		stripfolder=""
		uselastarg=1
		domove=0
	fi

	if [ $DESTISDIR -eq 2 ];then
		lastarg="$(getLastArg)"	
		stripfolder=""
		uselastarg=1
		domove=0
	fi

	for (( j=1;j<${FILELIST[0,0]};j++ ))
		do
			stripfolder="$(dirname "${FILELIST[$j,1]}")"
			for (( k=1;k<${FILELIST[$j,0]};k++ ))
				do
					if [ "$stripfolder" != "." ];then
						newfold="${FILELIST[$j,$k]#"${stripfolder}/"}"
					else
						newfold="${FILELIST[$j,$k]}"
					fi

					if [[ ! -L "${FILELIST[$j,$k]}" ]] && [[ -d "${FILELIST[$j,$k]}" ]];then
						mkdir -p "${lastarg}/$newfold"
					else
						(( cnt++ ))
						if [ $uselastarg -eq 1 ];then
							#echo "copyFile "${FILELIST[$j,$k]}" "${lastarg}" $cnt"
							copyFile "${FILELIST[$j,$k]}" "${lastarg}" $cnt
						else
							copyFile "${FILELIST[$j,$k]}" "${lastarg}/$newfold" $cnt
							#echo "copyFile "${FILELIST[$j,$k]}" "${lastarg}/$newfold" $cnt"
						fi
						#copyFile "${FILELIST[$j,$k]}" "${lastarg}/$newfold" $cnt
					fi
				done
		done

	if [ $domove -eq 1 ];then
		mv "${lastarg}/$(basename "${FILELIST[1,1]}")" "$(getLastArg)"
		rmdir "${lastarg}"
	fi
#	exit 0
}

main ()
{
	if [[ $(basename "$0") = "vimv" ]];then
		ISMOVE=1
		RECURSIVE=1
		DIALOGNAME="ViMv"
		DIALOGMESSAGE="Moving"
		DIALOGSUBMESSAGE="Moved"
	fi

	NUMARGS=${#OPTS[@]}

	optionCheck
	VICPYELLOW=$(createcol fore $YELLOW bold 1 back $CYAN)
	VICPBLACK=$(createcol fore $BLACK bold 0 back $CYAN bright 0)

	buildFileList

	printf $HIDECURSOR
	printfiles
	printf $SHOWCURSOR

#echo ${FILELIST[0,0]}
#clear

#for ((j=0;j<FILELIST[0,0];j++))
#do
##echo ${FILELIST[$j]}
#echo ${FILELIST[0,$j]}
#done
}

setCpSwitches ()
{
	local cnt
	local arrary
	local newcnt=0

	CPFLAGS="--no-dereference "

	for ((cnt=0;cnt<${#OPTS[@]};cnt++))
		do
			if [ "${OPTS[cnt]:0:1}" = "-" ];then
				CPFLAGS="${CPFLAGS} ${OPTS[cnt]}"
			else
				arrary[newcnt]="${OPTS[cnt]}"
				(( newcnt++ ))
			fi
		done
	OPTS=${arrary}
}

setterm -cursor off

vicpargs "$@"
setCpSwitches

main
putcursor 0 $LINES
stty sane
setterm -cursor on
printf $SHOWCURSOR
printf "\e[0m"

if [[ $ISMOVE -eq 1 ]];then
	while read
		do
			if [[ ! "X$REPLY" = "X" ]];then
				rmdir "$REPLY" ||true
			fi
		done < <(echo -e $FOLDERLIST)
fi