#!/bin/bash

. ${SPELL_DIRECTORY}/liblinux

# Create the default KERNEL_ARCH variable
arch_default()
{
	if [ -z "$KERNEL_ARCH" ]
	then
		case  `uname -m`  in
			alpha) KERNEL_ARCH=alpha   ;;
			arm) KERNEL_ARCH=arm     ;;
			cris) KERNEL_ARCH=cris    ;;
			ia64) KERNEL_ARCH=ia64    ;;
			i*86) KERNEL_ARCH=i386    ;;
			m68k) KERNEL_ARCH=m68k    ;;
			mips) KERNEL_ARCH=mips    ;;
			mips64) KERNEL_ARCH=mips64  ;;
			parisc) KERNEL_ARCH=parisc  ;;
			powerpc) KERNEL_ARCH=ppc     ;;
			ppc) KERNEL_ARCH=ppc     ;;
			powerpc64) KERNEL_ARCH=ppc64   ;;
			s390) KERNEL_ARCH=s390    ;;
			s390x) KERNEL_ARCH=s390x   ;;
			sh*) KERNEL_ARCH=sh      ;;
			sparc) KERNEL_ARCH=sparc   ;;
			sparc64) KERNEL_ARCH=sparc64 ;;
			x86_64) KERNEL_ARCH=x86_64  ;;
		esac		
	fi
}

# create the defaults file from scratch
# do this with every command
create_defaults()
{
	arch_default
	# add variables that I need
	
	# what arch the kernel is being made for
	persistent_add KERNEL_ARCH
	
	# what are we doing, 
	# making a new kernel tree: newktree
	# recompiling an old kernel tree: oldktree
	# using a custom existing kernel tree: expert
	persistent_add KMODE

	# internal semi-copy of RECONFIGURE so that the spell can call
	# menuconfig in PRE_BUILD
	persistent_add KLINUX_RECONFIGURE

	# kernel version for the base kernel source, or in case of expert the
	# full kernel version of the source tree
	persistent_add BASE_KVER

	# list of final patches after depends resolution
	persistent_add CANONICAL_PATCHES

	# list of patches that the user selected
	persistent_add SELECTED_PATCHES

	# extra make options that get directly passed to the make command line
	persistent_add EXTRA_MAKE_OPTIONS

	# restore the config file from a particular location in this variable
	persistent_add CONFIG_RESTORE

	# old source tree location for oldktree mode
	persistent_add old_src_tree

	# make sure that the state of the spell is good when we compile
	# since users can put the spell into a state where it will always fail
	# do some sanity checks to get a working installed configuration

	# check if the grimoire version was completed successfully and load
	# it's set of persistent vars and change the KMODE to oldktree if the
	# KMODE that made the kernel wasn't expert
	VERSION="`gaze version $SPELL | awk 'NR == 3 {print $4}'`"
	if [ -f ${BUILD_DIRECTORY}/linux-${VERSION}/.spell-config.p ]
	then
		cp ${BUILD_DIRECTORY}/linux-$VERSION/.spell-config.p \
		   ${SPELL_CONFIG}.p
		persistent_load
		if [[ "$KMODE" != "expert" ]]
		then
			KMODE="oldktree"
		fi
	
	# upgrade path: if the installed version of the spell was installed
	# successfully then use it's persistent vars and switch to newktree
	# so the update can be performed.
	elif [ -f ${BUILD_DIRECTORY}/linux-$(installed_version $SPELL)/.spell-config.p ]
	then
		cp ${BUILD_DIRECTORY}/linux-$(installed_version $SPELL)/.spell-config.p ${SPELL_CONFIG}.p
		persistent_load
		KMODE="newktree"
		
	# we don't have any configuration and we need to set some sane defaults
	# so that things won't break.
	# these defaults are the vanilla kernel with the latest maintenance
	# patches
	else
		[ -z "$KMODE" ] && KMODE="newktree"
		[ -z "$BASE_KVER" ] && BASE_KVER="LATEST_6"
		[ -z "$SELECTED_PATCHES" ] && SELECTED_PATCHES="LATEST_maintenance_patches_6"
	fi

	# check RECONFIGURE for cast -r linux and set KLINUX_RECONFIGURE appropriately
	# since the questions afterwards are saved with the persistent vars we
	# need to remove them from the persistent var table so that the 
	# questions will be asked again.
	if [[ $RECONFIGURE ]]
	then
		MAKEMODE=""
		UPDATE_BL=""
		CONFIG_KERNEL=""
		KLINUX_RECONFIGURE="true"
	else
		KLINUX_RECONFIGURE="false"
	fi

	# do this just to make sure
	persistent_save
	persistent_load
}

# dialog command works even if you add --nocancel as part of the arguments
dialog=( "dialog" "--stdout" )

# used for checking the current version against the updated version in the
# spell
# output is the kernel version that is the updated version of the spell...
# might need to be modified...
get_usr_src_kver()
{
	local spell_config=$1
	local newkver
	local ret
	. $spell_config
	calculate_depends > /dev/null
	if [[ ! -z $CANONICAL_PATCHES ]]
	then
		for patch in $CANONICAL_PATCHES
		do
			source_patch ${patch}
			if [[ ! -z $patchversion ]]
			then
				newkver=$patchversion
			fi
		done
		echo $newkver
	else
		case $BASE_KVER in
			LATEST*)
				echo ${!BASE_KVER}
				;;
			*)
				echo $BASE_KVER
				;;
		esac
	fi
}

# simply list ${BUILD_DIRECTORY} linux sources 
# these source directories are only spells that have been successfully installed
# this is accomplished by puting the persistent config file when that kernel was
# installed into the source directory
# also the check is done to determine the version of each linux source to see if
# the kernel source needs updating
list_usr_src()
{
	local tmpkver
	local linuxsrcs
	local linuxsrc
	if find ${BUILD_DIRECTORY}/linux*/.spell-config.p >> /dev/null 2>&1 
	then
		linuxsrcs="`ls -1d ${BUILD_DIRECTORY}/linux*`"
		for linuxsrc in $linuxsrcs
		do
			if [ -d $linuxsrc ] && [ -f $linuxsrc/.spell-config.p ]
			then
				echo "$linuxsrc"
				echo "\"Source Tree\""
				echo "\"Use ${linuxsrc} as the source tree\""
			fi
		done
	else
		echo "No"
		echo "\"Available Source trees\""
		echo "\"Maybe you need to compile a new one?\""
	fi
}
																			
# main menu uhhh, what else is there to say?
main_menu()
{
	local first_iter=0
	while [[ $KMODE == "Options" || $first_iter == 0 ]]
	do
		first_iter=1
		KMODE=$( "${dialog[@]}" --title "New or Used Kernel Tree" \
			--item-help --default-item "$KMODE" \
			--cancel-label "Exit" --menu \
			"Would you like to use a new kernel tree or an existing one?\nThe Current kernel is $BASE_KVER\nThe current selected patches: ${SELECTED_PATCHES}" 0 0 0 \
			"newktree" "New Kernel Tree" "I want to use a new kernel tree"  \
			"oldktree" "Old Kernel Tree" "I want to use an old kernel tree"  \
			"expert"   "User Generated Kernel Tree" "I want to use a kernel tree that I made" \
			"Options"  "Menu" "Make and config options" )
		retval=$?
		if [[ $retval == 0 ]]
		then
			if [[ "$KMODE" == "Options" ]]
			then
				options_menu
			else
				return $retval
			fi
		else
			return $retval
		fi
	done
}

options_menu()
{
	while true
	do
		menu_option=$( "${dialog[@]}" --title "Options Menu" \
			--item-help --cancel-label "Exit" --menu \
			"Options Menu Yeah Fun" 0 0 0 \
			"Config" "Restoration" "Optional config restoration" \
			"Make" "Options" "Options passed to make for compiling" 
			)
		retval="`echo $?`"
		if [[ "$retval" == "0" ]]
		then
			case "${menu_option}" in
				Make)
				EXTRA_MAKE_OPTIONS=$("${dialog[@]}" --inputbox "These Options are passed to make when compiling your kernel\nFor Example\nV=1 will change the 2.6 output to look more like the 2.4 output\nEXTRA_CFLAGS=$CFLAGS will add sorcery cflags to your kernel" 0 0 "$EXTRA_MAKE_OPTIONS")
				;;
				Config)
				CONFIG_RESTORE=$("${dialog[@]}" --inputbox "This is the location of the config you would like used for the kernel to be built with.\nExamples:\n/proc/config.gz\n/etc/sorcery/local/kernel.config" 0 0 "$CONFIG_RESTORE")
				;;
				*) break ;;
			esac
		else
			break
		fi
	done
}

custom_kernel()
{
	command=$("${dialog[@]}" --inputbox "Enter the kernel version for the source 
you want to build?\nExamples:\n2.6.13.4-selinux1\n${LATEST_4}" 0 0 "")
	retval=$?
	echo $command
	if [[ "$?" == "0" ]] 
	then
		BASE_KVER=$command
		old_src_tree="/usr/src/linux-${BASE_KVER}"
		KLINUX_RECONFIGURE="true"
		MAKEMODE=""
		UPDATE_BL=""
		CONFIG_KERNEL=""
		SELECTED_PATCHES=""
		patches=""
	fi
}

# create a new linux source tree
# I use awk to parse the latest.defaults file
# that should be changed but how?
new_tree_menu()
{
	menu=""
	versions="`ls -1vr $KERNELS_DIRECTORY/`"
	for version in $versions
	do
		menu="${menu}${version} Linux_Kernel Select_one "
	done
	menu="$( awk -F= '$1 ~ /LATEST_3|LATEST_4|LATEST_5|LATEST_6/ {print $1 " Linux_Kernel " $2 }' $LATEST_KDEFAULTS ) ${menu}"
	command=$( "${dialog[@]}" --title "Select Kernel Version" \
	--item-help --cancel-label "Exit" --default-item "$BASE_KVER" --menu \
	"Select a kernel version to use" 0 0 0 \
	$menu )
	retval="$?"
	if [ "$retval" == "0" ]
	then
		case "$command" in
			6*|5*|4*|3*) 
				BASE_KVER=$command
				;;
			LATEST*)
				BASE_KVER=$command
				;;
		esac &&
		return $retval
	else
		return $retval
	fi
}

# find all patches list the directories and LATEST patches for the kernel version $1
patch_list()
{
	local tmp
	local found
	local patchvers
	local patchver
	local patches
	local patch
	patches="`ls -1 $KPATCHES_DIRECTORY/`"
	for patch in $patches
	do
		patchvers="`ls -1vr $KPATCHES_DIRECTORY/${patch}`"
		found="false"
		for patchver in $patchvers
		do
			source_patch $patchver
			for applied in $appliedkernels
			do
				if [[ $applied == $1 || $applied == ${!1} ]]
				then
					found="true"
				fi
			done
		done
		if [ "$found" == "true" ]
		then
			echo $patch
			echo '"Patch Sub Menu"'
		fi
	done
}

# list all patches for kernel version $1 and for patch $2
list_specific_patch_dir()
{
  local tmp="LATEST_$2"

	if [[ ${!tmp} ]]
	then
	  echo $tmp
		echo Toggle
		if [[ ${3/$tmp/} == ${3} ]]
		then
		  echo off
		else
		  echo on
		fi
		echo $2
	fi
	patchvers="`ls -1vr $KPATCHES_DIRECTORY/$2`"
	for patchver in $patchvers
	do
		source_patch $patchver
		for applied in $appliedkernels
		do
			if [[ $applied == $1 || $applied == ${!1} ]]
			then
				echo $patchver
				echo Toggle
				if [[ ${3/$patchver /} == ${3} ]]
				then
				  echo off
				else
				  echo on
				fi
				echo $2
			fi
		done
	done
}

# main patch menu called from main_menu
# list directories and valid latest patches
# that apply to the kernel version $1
patch_menu()
{
	local new_patch_list=""
	while true
	do
		menu=$(patch_list $1)
		if [ "$menu" == "" ]
		then
			menu='"No Patches Available Sorry"'
		fi
		eval 'command=$( "${dialog[@]}" --title "Select Patches" \
			--help-button --cancel-label "Exit" \
			--ok-label "Select" --menu \
			"Currently selected kernel: $1\nCurrently selected patches: $new_patch_list" 0 0 0 '${menu}' )'
		retval="$?"
		if [[ "$retval" == "0" && "$command" != "No_Patches" ]]
		then
			dialog_specific_patchdir $1 $command "$new_patch_list" new_patch_list
		elif [[ "$retval" == "2" && "${command}" != "No_Patches" ]]
		then
			if [[ "LATEST_${command/HELP LATEST_/}" == "${command/HELP /}" ]]
			then
				tmp=${command/HELP /}
				source_info ${tmp}
				"${dialog[@]}" --msgbox "Selecting this will keep the kernel tree updated.\n$help" 0 0
			else
				tmp=${command/HELP /}
				. $KPATCHES_DIRECTORY/${command/HELP /}/.info
				"${dialog[@]}" --msgbox "$help" 0 0
			fi
		else
			SELECTED_PATCHES=$new_patch_list
			break
		fi
	done
}

dialog_specific_patchdir()
{
  local patchdir=$2
	local retval patch
	local npatch_list=$3
	local upvar=$4
	while true
	do
		. $KPATCHES_DIRECTORY/$patchdir/.info	
		command=$( "${dialog[@]}" --title "Select Patches" \
			--item-help --cancel-label "Commit" \
			--ok-label "Toggle" --checklist \
			"$help\n\nCurrently selected kernel: $1\nCurrently selected patches: $npatch_list" 0 0 0 \
			$(list_specific_patch_dir $1 $patchdir "$npatch_list") )
		retval=$?
		if [[ $retval == 0 ]]
		then
			for patch in $npatch_list
			do
			  if [[ -f "$KPATCHES_DIRECTORY/$patchdir/$patch" || -f "$KPATCHES_DIRECTORY/$patchdir/${!patch}" ]] 
				then
				  npatch_list=${npatch_list/$patch /}
				fi
			done
			for patch in ${command}
			do
				npatch_list="${npatch_list}${patch//\"/} "
			done
		else
		  eval "$upvar=\${npatch_list}"
			break
		fi
	done
}

# list the old source menu to reconfigure an old source tree
old_src_menu()
{
	while true
	do
		eval 'old_src_tree=$( "${dialog[@]}" --title "Select Old Source Tree" \
				--item-help \
				--default-item "$BUILD_DIRECTORY/$old_src_tree"\
				--menu \
				"Select Old Source Tree\nSome source trees may \
need updating" \
				0 0 0 '`list_usr_src`' )'
		retval="$?"
		if [[ "$retval" == "0" && "$old_src_tree" != "No" ]]
		then
			if [[ "${old_src_tree}" == "${BUILD_DIRECTORY}/linux" && -L "${old_src_tree}" ]]
			then
				old_src_tree="`readlink -f ${old_src_tree}`"
			fi
			if [[ "${old_src_tree}" != "${BUILD_DIRECTORY}/linux-`get_usr_src_kver ${old_src_tree}/.spell-config.p`" ]]
			then
				if dialog --yesno "${old_src_tree} will be updated to `get_usr_src_kver ${old_src_tree}/.spell-config.p`.\nDo you want to continue?" 0 0
				then
					load_old_src_tree

					break
				fi
			else
				load_old_src_tree

				break
			fi
		else
			break
		fi
	done
}

load_old_src_tree()
{
	local tmp
	cp ${old_src_tree}/.spell-config.p ${SPELL_CONFIG}.p
	tmp=$old_src_tree
	persistent_load
	persistent_add old_src_tree
	if [[ $KMODE != "expert" ]]
	then
		KMODE="oldktree"
	fi
	old_src_tree=$tmp
	KLINUX_RECONFIGURE="true"
	persistent_remove MAKEMODE
	persistent_remove UPDATE_BL
	persistent_remove CONFIG_KERNEL
	persistent_save
	persistent_load
}

# Start of the execution

# if it's not there create it
. $LATEST_KDEFAULTS
create_defaults

if [[ "$KLINUX_RECONFIGURE" == "true" || "$BASE_KVER" == "" ]]
then
	main_menu &&
	case "$KMODE" in
		"newktree")
		new_tree_menu &&
		patch_menu $BASE_KVER
		;;
		"oldktree")
		old_src_menu
		;;
		"expert")
		custom_kernel
		;;
	esac &&
	persistent_remove MAKEMODE &&
	persistent_remove UPDATE_BL 
else
	echo "No Reconfigure, using ${SPELL_CONFIG}.p for defaults"
fi &&
run_conflicts &&
create_details
