#!/usr/local/bin/cbsd
#v12.1.9
MYARG=""
MYOPTARG="mode"
CBSDMODULE="bhyve"
MYDESC="Ncurses based bhyve guest creation wizard"
EXTHELP="wf_bcreate"
ADDHELP="
${H3_COLOR}Description${N0_COLOR}:

The CBSD supports a large number of options for creating environments
and the TUI (text-user interface) dialog variant is probably the easiest.
This is an interactive dialog that generates a configuration file for the
'cbsd bcreate' command. You can see this configuration file if you answer
negatively to the 'Do you want to create jail immediately?' question at
the end of the dialogue.

If you create the environment often and in large quantities, the CBSD 
recommends using the CBSDfile (Vagrant style) instead of bcreate/bconstruct-tui.

${H3_COLOR}Options${N0_COLOR}:

${N2_COLOR}mode=${N0_COLOR} - set 'full' to unhide all atributes.

${H3_COLOR}Examples${N0_COLOR}:

 # cbsd bconstruct-tui

${H3_COLOR}See also${N0_COLOR}:

  cbsd bcreate --help
  cbsd bconstruct --help
  cbsd up --help

"

. ${subrdir}/nc.subr
. ${cbsdinit}

. ${subrdir}/bhyve.subr
# preliminary reading of some pre-defined constants for tui messages
readconf bhyve-default-default.conf

. ${subrdir}/settings-tui.subr
. ${subrdir}/settings-tui-virtual.subr
. ${subrdir}/settings-tui-bhyve.subr
. ${dialog}

extra_default_item=

gen_newjail_conf()
{
	local _i _required _mytest _ret

	_required="jname host_hostname ip4_addr ver arch imgsize"

	if [ ${is_cloud} -eq 1 ]; then
		_required="${_required} cloud_init_options"
	fi

	msg_ok="ok"

	for _i in ${_required}; do
		eval _mytest=\$$_i
		if [ -z "${_mytest}" ]; then
			f_dialog_msgbox "${_i} must be filled" "Error"
			extra_default_item="${_i}"
			return 0
		fi
	done

	unset extra_default_item

	# adjust default sectorize
	if [ -z "${sectorsize}" ]; then
		sectorsize="${default_sectorsize}"
		[ -z "${sectorsize}" ] && sectorsize="4096"
	fi

	# adjust/inherit hdd_boot_firmware from cd_boot_firmware when not set:
	[ "${hdd_boot_firmware}" = "0" ] && hdd_boot_firmware="${cd_boot_firmware}"

	if [ "${ip4_addr}" != "REALDHCP" ]; then
		#check for ip if oninterface
		if [ "${interface}" != "0" -a -n "${ip4_addr}" ]; then
			checkip ip=${ip4_addr} check=1 2>/dev/null
			case $? in
				2)
					msg_yes="ok"
					msg_no="not ok"
					f_dialog_noyes "It seems like ${ip4_addr} address already used on several devices on the LAN\nYou can found MAC address by \"arp -an\" command.\n If you believe that it's ok, choose 'ok' to continue or 'not ok' for another IP address" "WARNING"
					case $? in
						0)
							;;
						*)
							return 0
							;;
					esac
			esac
		fi
	fi

	gen_jconf
	ERR=$?

	[ ${ERR} -eq 0 ] || err 1 "${N1_COLOR}bad conf${N0_COLOR}"
	err 0 ${A}
}

dialog_menu_main()
{
	local title=" ${product} v${myversion} "
	local btitle="${DIALOG_BACKTITLE}"
	local prompt="Use menu to construct VM and create config file"

	defaultitem= # Calculated below

	local hline=
	local mark
	local i _mytest

	f_dialog_default_fetch defaultitem

	# checkbox mark
	for i in astart xhci tablet fbuf jailed chrooted; do
		eval _mytest=\$$i
		if [ "${_mytest}" = "1" ]; then
			export ${i}_mark="X"
		else
			export ${i}_mark=" "
		fi
	done

	item_let="A"
	item_num="0"
	local menu_list=""

	menu_list="${menu_list} '${item_let} vm_os_type'	'$(curval vm_os_type)'		'Guest OS type'"
	inc_menu_index item_let
	menu_list="${menu_list} '${item_let} vm_os_profile'	'$(curval vm_os_profile)'	'Guest OS profile'"
	inc_menu_index item_let

	if [ "${from_jail}" = "1" -a "${bhyve_profile}" != "FreeBSD-bsdinstall-jail" ]; then
		menu_list="${menu_list}	'${item_let} jprofile'	'$(curval jprofile)'	'Select jail profile for jcreate'"
	fi

	local vm_package_num=$( cbsdsqlro local SELECT COUNT\(name\) FROM vmpackages )

	if ! is_number ${vm_package_num}; then
		if [ ${vm_package_num} -gt 0 ]; then
			menu_list="${menu_list} '${item_let} vm_package'	'$(curval vm_package)'		'Package group name'"
			inc_menu_index item_let
		fi
	fi

	menu_list="${menu_list} '${item_let} jname'		'$(curval jname)'		'A short jail name'"
	inc_menu_index item_let
	menu_list="${menu_list} '${item_let} host_hostname'	'$(curval host_hostname)'	'Full (FQDN) jail hostname'"
	inc_menu_index item_let
	menu_list="${menu_list} '${item_let} imgsize'		'$(curval imgsize)'		'reserved (expand) X size for free space'"
	inc_menu_index item_let

	if [ ${is_cloud} -eq 0 ]; then
		# moved to cloud-init helper for cloud host
		menu_list="${menu_list} '${item_let} ip4_addr'		'$(curval ip4_addr)'		'${ip4_addr_msg}'"
		inc_menu_index item_let
	fi

	menu_list="${menu_list} '${item_let} vm_ram'		'$(curval vm_ram)'		'RAM'"
	inc_menu_index item_let
	menu_list="${menu_list} '${item_let} vm_cpus'		'$(curval vm_cpus)'		'CPUs number'"
	inc_menu_index item_let

	if [ ${is_cloud} -eq 0 ]; then
		# not for cloud
		menu_list="${menu_list} '${item_let} vm_iso_path'	'$(curval vm_iso_path)'		'ISO Path for CD-ROM'"
		inc_menu_index item_let
	fi

	menu_list="${menu_list} '${item_let} vm_hostbridge'	'$(curval vm_hostbridge)'	'VMs hostbridge'"
	inc_menu_index item_let
	menu_list="${menu_list} '${item_let} astart'		'[${astart_mark}]'		'Autostart with system'"
	inc_menu_index item_let
	menu_list="${menu_list} '${item_let} xhci'		'[${xhci_mark}]'		'eXtensible Host Controller Interface (xHCI) USB controller'"

	if [ "${xhci}" = "1" ]; then
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} tablet'		'[${tablet_mark}]'		'A USB tablet device (via xHCI)'"
	fi

	inc_menu_index item_let
	menu_list="${menu_list} '${item_let} fbuf'		'[${fbuf_mark}]'		'emulate fbuf device (vnc)'"

	if [ ${freebsdhostversion} -gt 1200085 -a ${is_cloud} -eq 0 ]; then
		# jailed available in FreeBSD 12.0-RELEASE+
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} jailed'		'[${jailed_mark}]'		'Run bhyve in jail. Highly experimental!'"
	fi
#	if [ ${is_cloud} -eq 0 ]; then
#		# chrooted bhyve
#		inc_menu_index item_let
#		menu_list="${menu_list} '${item_let} chrooted'		'[${chrooted_mark}]'		'Run bhyve in chroot. Highly experimental!'"
#	fi

	inc_menu_index item_let
	menu_list="${menu_list} '${item_let} interface'		'$(curval interface)'		'Interface selection and aliasing mode'"

	if [ ${freebsdhostversion} -gt 1100119 ]; then
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} vm_vnc_port'	'$(curval vm_vnc_port)'		'VNC port'"
	else
		# Disable for FreeBSD 11 < 1100120
		vm_vnc_port=1
	fi

	inc_menu_index item_let
	menu_list="${menu_list} '${item_let} vm_efi'		'$(curval vm_efi)'		'UEFI Boot mode'"

#	disable for a while, bhyveload/grub-bhyve is too unreliable method
#	inc_menu_index item_let
#	menu_list="${menu_list} '${item_let} vm_console'	'$(curval vm_console)'		'Output console'"

	menu_list="${menu_list} '${item_let} debug_engine'		'$(curval debug_engine)'	'Run in debugger?'"
	inc_menu_index item_let

	if [ ${is_cloud} -eq 1 ]; then
		menu_list="${menu_list} '${item_let} cloud_init_options'	'$(curval cloud_init_options)'	'cloud-init settings'"
		inc_menu_index item_let
	fi

	if [ "${zfsfeat}" = "1" ]; then
		menu_list="${menu_list} '-'		'-'	''"
		inc_menu_index item_num
#		menu_list="${menu_list} '${item_num} zfsfeature'	'ZFS-features >>'	'Additional ZFS-based features'"
		menu_list="${menu_list} '${item_num} imgtype'		'$(curval imgtype)'	'ZVOL or MD disk type'"

		if [ ${is_cloud} -eq 0 ]; then
			# not for cloud
			inc_menu_index item_num
			menu_list="${menu_list} '${item_num} zfs_snapsrc'	'$(curval zfs_snapsrc)'	'Create bhyve from ZFS snapshot'"
		fi
	fi

	menu_list="${menu_list} '${item_num} vm_cpu_topology'	'$(curval vm_cpu_topology)'	'Select VM CPU topology'"
	inc_menu_index item_num

	menu_list="${menu_list} '${item_num} LPC'			'LPC devices >>'	'cbsd blpc'"
	inc_menu_index item_num

	# FreeBSD-13+ only
	if [ ${freebsdhostversion} -gt 1300033 ]; then
		inc_menu_index item_num
		menu_list="${menu_list} '${item_num} bhyve_audio_options'	'Audio options >>'		'Audio opt: ${soundhw}'"
	fi

	menu_list="${menu_list} '${item_num} bhyve_options'	'Bhyve options >>'	'Customize bhyve options: acpi:${bhyve_generate_acpi}, wire:${bhyve_wire_memory} rts_utc:${bhyve_rts_keeps_utc} msi:${bhyve_force_msi_irq} x2apic:${bhyve_x2apic_mode} mptable:${bhyve_mptable_gen} ign_acc:${bhyve_ignore_msr_acc}'"
	inc_menu_index item_num

	if [ "${vm_efi}" != "none" ]; then
		if [ "${fbuf}" = "1" ]; then
			inc_menu_index item_num
			menu_list="${menu_list} '${item_num} bhyve_vnc_options'	'VNC options >>'	'VNC opt: wait:${cd_vnc_wait} res:${bhyve_vnc_resolution} bind:${bhyve_vnc_tcp_bind} vnc_password:XXX'"
		fi
	fi

	menu_list="${menu_list} '${item_num} bhyve_flags'	'Additional bhyve_flags'	'Customize additional bhyve_flags. Current: ${bhyve_flags}'"
	inc_menu_index item_num

	menu_list="${menu_list} '${item_num} cd_boot_firmware'	'Select CD boot firmware'	'Change CD boot firmware. Current: ${cd_boot_firmware}'"
	inc_menu_index item_num
	menu_list="${menu_list} '${item_num} hdd_boot_firmware'	'Select HDD boot firmware'	'Change HDD boot firmware. Current: ${hdd_boot_firmware}'"
	inc_menu_index item_num

	menu_list="${menu_list} '${item_num} exit_behavior'	'Exit behavior settings'	'Exit behavior settings: poweroff:${on_poweroff},reboot:${on_reboot},crash:${on_crash}'"
	inc_menu_index item_num

	if [ "${from_jail}" = "1" -a "${bhyve_profile}" != "FreeBSD-bsdinstall-jail" ]; then
		menu_list="${menu_list} '-'	'JAIL SUB:'	'fromjail submenu'"
		[ -z "${swapsize}" ] && swapsize="4g"
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} vm_guestfs'	'$(curval vm_guestfs)'	'Choose filesystem for boot image'"
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} swapsize'	'$(curval swapsize)'	'Allocate and Configure for swap partition'"
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} pkglist'	'$(curval pkgnum)'	'mark pkg for install from repo'"
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} gw4'		'$(curval gw4)'	'Default router for bhyve or vimage'"
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} ver'		'$(curval ver)'	'choose code base version'"
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} applytpl'	'$(curval applytpl)'	'Apply cbsd templates'"
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} floatresolv'	'$(curval floatresolv)'	'Auto correct for jail resolv.conf'"
		inc_menu_index item_let
		menu_list="${menu_list} '${item_let} arch'		'$(curval arch)'	'target arch'"
		inc_menu_index item_num
		menu_list="${menu_list} '${item_num} user_pw_root'	'Root Password'	'Change jail root password'"
		inc_menu_index item_num
		menu_list="${menu_list} '${item_num} add_user'	'$(curval user_add)'	'Create additional account within VM'"
		inc_menu_index item_num
		menu_list="${menu_list} '${item_num} services'		'Services >>'	'Toggle Startup Services'"
		inc_menu_index item_num
		menu_list="${menu_list} '${item_num} pkglist'	'$(curval pkgnum)'	'mark pkg for install from repo'"
	fi

	menu_list="${menu_list} '-'	'-'	'fromjail submenu'"
	inc_menu_index item_num
	menu_list="${menu_list} '${item_num} GO'	'PROCEED!'	'PROCEED!'"

	if [ -n "${extra_default_item}" ]; then
		# scan for extra default item in menu list
		OIFS="${IFS}"
		IFS="'"
		for i in ${menu_list}; do
			IFS="${OIFS}"
			case "${i}" in
				[0-9]*${extra_default_item}|[aA-zZ]*${extra_default_item})
					defaultitem="${i}"
					break
					;;
			esac
		done
		IFS="${OIFS}"
	fi
	cbsd_menubox_with_help
	retval=$?

	f_dialog_data_sanitize menu_choice
	f_dialog_menutag_store "$menu_choice"
	f_dialog_default_store "$menu_choice"

	return $retval
}

#### [ MAIN AREA ] ####
Makefile="${CBSD_PWD}/CBSDfile"
if [ -r "${Makefile}" ]; then
	${ECHO} "${N1_COLOR}found CBSDfile: ${N2_COLOR}${Makefile}${N0_COLOR}"
	err 1 "${N1_COLOR}${CBSD_APP} doesn't not support for CBSDfile, please change current directory${N0_COLOR}"
fi
[ ! -f ${localcbsdconf} ] && err 1 "${N1_COLOR}no such conf file${N0_COLOR}"
. ${localcbsdconf}
. ${inventory}

zero_profile="bhyve-default-default.conf"
default_profile="bhyve-default-default.conf"

#defaults
. ${subrdir}/build.subr

# read for skip_bhyve_init_warning
readconf ${default_profile}
init_bhyve
readconf vnc.conf

# re-read profile for init global_profile_file variable
get_construct_profile ${default_profile}
[ -z "${profile}" -a -n "${jail_profile}" ] && profile="${jail_profile}"

[ -z "${bhyve_vnc_resolution}" ] && bhyve_vnc_resolution="${default_vnc_width}x${default_vnc_height}"
[ -z "${bhyve_vnc_tcp_bind}" ] && bhyve_vnc_tcp_bind="${default_vnc_tcp_bind}"

# IPv6 only host?
case "${node_ip6_active}" in
	1)
		if [ "${node_ip4_active}" = "0" ]; then
			bhyve_vnc_tcp_bind="[::1]"
		fi
		;;
esac

[ -z "${bhyve_vnc_vgaconf}" ] && bhyve_vnc_vgaconf="${default_vnc_vgaconf}"
[ -z "${bhyve_vnc_vgaconf}" ] && bhyve_vnc_vgaconf="${default_bhyve_vnc_vgaconf}"
[ -z "${vnc_password}" ] && vnc_password="${default_vnc_password}"
[ -z "${imgtype}" ] && imgtype="zvol"
[ -z "${uuid}" ] && uuid="0"
[ -z "${bhyve_vnc_kbdlayout}" ] && bhyve_vnc_kbdlayout="${default_bhyve_vnc_kbdlayout}"

baserw=1
ip4_addr="DHCP"
jprofile="default"
cloud_init_options=

f_dialog_title "$msg_system_console_configuration"
f_dialog_backtitle "${ipgm:+bsdconfig }$pgm"
f_mustberoot_init

# init first bconstruct settings
if [ ! -r ${tmpdir}/bconstruct.conf ]; then
	last_cache_crc=0
	${CAT_CMD} > ${tmpdir}/bconstruct.conf <<EOF
last_vm_os_type="freebsd"
last_vm_os_profile="FreeBSD-x64-14.3"
last_cache_crc="0"
EOF
else
	. ${tmpdir}/bconstruct.conf
fi

# todo: src=bhyve
cache_crc=$( get-profiles src=iso cache_sum=1 )
if [ "${cache_crc}" != "${last_cache_crc}" ]; then
	${RM_CMD} -f "${tmpdir}"/bconstruct.conf
	/usr/local/cbsd/misc/cbsdsysrc -qf "${tmpdir}"/bconstruct.conf last_cache_crc="${cache_crc}"
fi

if [ "${default_profile}" = "default" -a -r ${tmpdir}/bconstruct.conf ]; then
	[ -r ${tmpdir}/get_construct_vm_os_profile.menu ] && ${RM_CMD} -f ${tmpdir}/get_construct_vm_os_profile.menu
	if [ -n "${last_vm_os_type}" ]; then
		vm_os_type="${last_vm_os_type}"
		get_construct_vm_os_type ${vm_os_type}
	fi
	if [ -n "${last_vm_os_profile}" ]; then
		vm_os_profile="${last_vm_os_profile}"
		get_construct_vm_os_profile ${vm_os_profile}
	fi
else
	vm_iso_path="${register_iso_as}"
	apply_vm_package
fi

lpc_temp_sql=

while [ 1 ]; do
	pkgnum=0
	[ -n "${pkglist}" -a "${pkglist}" != "NO" ] && pkgnum=$( ${WC_CMD} -w ${pkglist} | ${AWK_CMD} '{printf $1}' )

	dialog_menu_main
	retval=$?

	if [ $retval -eq $DIALOG_HELP ]; then
		get_help
		continue
	elif [ $retval -ne $DIALOG_OK ]; then
		f_die
	fi

	index=${mtag%% *}
	mychoice=${mtag##* }

	case "${mychoice}" in
		"EXIT")
			exit 0
			;;
		"GO")
			# store last choices
			/usr/local/cbsd/misc/cbsdsysrc -qf ${tmpdir}/bconstruct.conf last_vm_os_type="${vm_os_type}" \
				last_vm_os_profile="${vm_os_profile}" > /dev/null 2>&1
			gen_newjail_conf
			;;
		"-")
			continue
			;;
		interface)
			get_construct_interface -d 1 -s "tap vboxnet lo xnb wlan" -b 1 -v 1 -n 1
			;;
		astart|bhyve_generate_acpi|bhyve_wire_memory|bhyve_rts_keeps_utc|bhyve_force_msi_irq|bhyve_x2apic_mode|bhyve_mptable_gen|bhyve_ignore_msr_acc|xhci|tablet|fbuf)
			invert_checkbox ${mychoice}
			continue
			;;
		jailed)
			eval _mytest=\$chrooted
			if [ "${_mytest}" = "1" ]; then
				invert_checkbox chrooted
			fi
			invert_checkbox ${mychoice}
			continue
			;;
		chrooted)
			eval _mytest=\$jailed
			if [ "${_mytest}" = "1" ]; then
				invert_checkbox jailed
			fi
			invert_checkbox ${mychoice}
			continue
			;;
		LPC)
			if [ -z "${lpc_temp_sql}" ]; then
				lpc_temp_sql=$( ${MKTEMP_CMD} )
				# keep in sync with bcreate
				/usr/local/bin/cbsd ${miscdir}/updatesql ${lpc_temp_sql} ${distdir}/share/local-bhyve-lpc.schema lpc

				[ -n "${otpm}" ] && tpm="${tpm}"
				[ -n "${obootrom}" ] && bootrom="${obootrom}"
				[ -n "${ocom1}" ] && com1="${ocom1}"
				[ -n "${ocom2}" ] && com2="${ocom2}"
				[ -n "${ocom3}" ] && com3="${ocom3}"
				[ -n "${ocom4}" ] && com4="${ocom4}"

				[ -z "${tpm}" ] && tpm="0"
				[ -z "${bootrom}" ] && bootrom="${distdir}/upgrade/patch/efi.fd"
				[ -z "${com1}" ] && com1="stdio"
				[ -z "${com2}" ] && com2="0"
				[ -z "${com3}" ] && com3="0"
				[ -z "${com4}" ] && com4="0"

				${miscdir}/sqlcli ${lpc_temp_sql} "INSERT INTO 'lpc' ( 'lpcslot_name', 'lpcslot_desc', 'lpcslot_value' ) VALUES ( 'bootrom', 'bootrom device', '${bootrom}' ), ( 'com1', 'com1 device', '${com1}' ), ( 'com2', 'com2 device', '${com2}' ), ( 'com3', 'com3 device', '${com3}' ), ( 'com4', 'com4 device', '${com4}' ), ( 'tpm', 'TPM device', '${tpm}' );"
				trap "${RM_CMD} -f ${lpc_temp_sql} ${lpc_temp_sql}-shm ${lpc_temp_sql}-wal" HUP INT ABRT BUS TERM EXIT
			fi
			blpc-tui out="${lpc_temp_sql}"
			trap "${RM_CMD} -f ${lpc_temp_sql}-shm ${lpc_temp_sql}-wal" HUP INT ABRT BUS TERM EXIT
			;;
		*)
			get_construct_${mychoice}
			;;
	esac
done

exit 0
