#!/bin/bash

. ${SPELL_DIRECTORY}/liblinux

# Create the default KERNEL_ARCH variable
function 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
}

# convert the old variable names to new variable names
# $1 pass a file name
function old_var_compat()
{
	local old_var
	old_var=( kernel_arch mode reconf kver patches selected_patches )
	local new_var
	new_var=( KERNEL_ARCH KMODE KLINUX_RECONFIGURE BASE_KVER CANONICAL_PATCHES SELECTED_PATCHES )
	if [[ -f $1 ]] 
	then
		for(( i=0 ; $i < ${#old_var[*]} ; i++))
		do
			sedit "s/^${old_var[$i]}/${new_var[$i]}/g" $1 &&
			persistent_remove ${old_var[$i]}
		done
	fi
}

# create the defaults file from scratch
# do this with every command
function 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

	# 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
		old_var_compat ${BUILD_DIRECTORY}/linux-$VERSION/.spell-config.p &&
		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
		old_var_compat ${BUILD_DIRECTORY}/linux-$(installed_version $SPELL)/.spell-config.p &&
		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_2_6"
		[ -z "$SELECTED_PATCHES" ] && SELECTED_PATCHES="LATEST_maintenance_patches"
	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
		persistent_remove MAKEMODE &&
		persistent_remove UPDATE_BL &&
		persistent_remove 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...
function 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
function 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?
function main_menu()
{
	while true
	do
		command=$( "${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" "Kernel Arch, Build Dir, Cleaning" )
		retval="$?"
		if [ "$retval" == "0" ]
		then
			case "$command" in
				"newktree") 
					KMODE=$command
					new_tree_menu 
					;;
				"oldktree")
					KMODE=$command
					old_src_menu
					;;
				"expert")
					KMODE=$command
					custom_kernel
					;;
				"Options")
					options_menu
					;;
				*) break ;;
			esac
		else
			break
		fi
	done
}

# clean a source directory ($1)
function clean_src_menu()
{
	while true
	do
		clean_options=$( "${dialog[@]}" --title "Clean Menu" \
			--item-help --menu \
			"How to Clean" 0 0 0 \
			"mrproper" "Clean $1 with mrproper" "Clean Source" \
			"clean" "Clean $1 with clean" "Clean Source" )
		retval="$?"
		if [ "$retval" == "0" ]
		then
			tmpdir="`pwd`"
			cd $1
			make ${clean_options}
			cd ${tmpdir}
		else
			break
		fi
	done
}
# select which kernel source to clean
# pass that kernel source to clean_src_menu
function clean_menu()
{
        while true
        do
                eval 'menu_option=$( "${dialog[@]}" --title "Clean Menu" \
			--item-help --menu \
			"Which Kernel" 0 0 0 '`list_usr_src`' )'
		retval="$?"
		if [[ "$retval" == "0" && "$menu_option" != "No" ]]
		then
			clean_src_menu ${menu_option}
		else
			break
		fi
	done
}

# making other package types for other Linux distros
function other_pkg_src_menu()
{
	while true
	do
		menu_option=$( "${dialog[@]}" --title "Options Menu" \
			--item-help --menu \
			"Other Package Creation\nThese only work if you have the respective applications installed" 0 0 0 \
			"rpm" "Make an RPM package" "Requires RPM" \
			"rpm-pkg" "Make another RPM package" "Requires RPM" \
			"binrpm-pkg" "Okay more RPM packages" "Requires RPM" \
			"deb-pkg" "Make a Debian Package" "Requires Debian Package Utilities" 
			)
		retval="$?"
		if [ "$retval" == "0" ]
		then
			tmpdir="`pwd`"
			cd $1
			make ${menu_option}
			cd $tmpdir
		else
			break
		fi
	done
}

# list the linux sources to make other package types
function other_package_menu()
{
	while true
	do
		eval 'menu_option=$( "${dialog[@]}" --title "Options Menu" \
			--item-help --menu \
			"Select a Kernel Source" 0 0 0 '`list_usr_src`' )'
		retval="$?"
		if [[ "$retval" == "0" && "$menu_option" != "No" ]]
		then
			other_pkg_src_menu ${menu_option}
		else
			break
		fi
	done
}

# select other arch to compile for
function arch_menu() 
{
	arch_default
	local DEFAULT=${KERNEL_ARCH}
	local COMMAND
	while true
	do
		COMMAND=$( "${dialog[@]}" --title "Select Kernel Architecture"       \
		--item-help --default-item "$DEFAULT" --menu      \
		"Select a kernel architecture to use" 0 0 0            \
		"CUSTOM"             "Manually enter some other Architecture"           "You can enter any valid architecture" \
		"DEFAULT"            "Default Kernel Architecture"                      "The detected architecture for your platform, $DEFAULT" \
		"alpha"              "DEC/Compaq 64-bit Alpha"                          "DEC Alpha" \
		"arm"                "Embedded Processors using 32-bit ARM processors"  "ARM 32-bit" \
		"cris"               "Embedded Axis ETRAX CRIS processors 32-bit"       "Axis ETRAX CRIS" \
		"ia64"               "Intel Architecture 64-bit Itanium"                "Intel IA64 64-bit" \
		"i386"               "Intel Architecture 32-bit 386 through Pentium 4"  "Intel x86 32-bit" \
		"m68k"               "Motorola 68000 series 32-bit (older Macintosh)"   "m68k 32-bit" \
		"mips"               "MIPS Technologies 32-bit compatible processors"   "MIPS32 platforms" \
		"mips64"             "MIPS Technologies 64-bit compatible processors"   "MIPS64 platforms" \
		"parisc"             "HP PA-RISC 32 and 64-bit processors"              "PA-RISC 32-bit mode" \
		"ppc"                "PowerPC 32-bit (IBM and Motorola) G3/G4 Mac"      "PPC32 G3/G4" \
		"ppc64"              "PowerPC 64-bit (IBM) G5 Mac"                      "PPC64 G5" \
		"s390"               "IBM s390  31-bit systems"                         "IBM s390 31-bit" \
		"s390x"              "IBM s390x 64-bit systems"                         "IBM s390 64-bit" \
		"sh"                 "SuperH processors sh4 is 32-bit, sh5 is 64-bit"   "SH4 and SH5 32-bit mode" \
		"sparc"              "Sun SPARC 32-bit processors"                      "Sun SPARC 32-bit" \
		"sparc64"            "Sun SPARC 64-bit processors"                      "Sun SPARC 64-bit" \
		"x86_64"             "AMD x86-64 64-bit Hammer/Opteron/Athlon64 chips"  "AMD x86-64 Hammer" \
		"um"                 "User Mode Linux arch used for creating UML kernels" "User Mode Linux" \
		"xen"                "Xen kernel arch used for creating Xen kernels" "Xen Kernel"
		)
		retval=$?
		if [[ "$retval" == "0" ]]
		then
			if [[ $COMMAND == "CUSTOM" ]]
			then
				COMMAND=$("${dialog[@]}" --nocancel --inputbox "Which Architecture do you want to build?\nYou can enter any valid architecture.\nExamples:\n$i386\nppc\nx86_64\nsparc\nsparc64" 0 0 ${KERNEL_ARCH})
			elif [[ $COMMAND == "DEFAULT" ]]
			then
				COMMAND="$DEFAULT"
			fi
			KERNEL_ARCH=$COMMAND
			break
		else
			break
		fi
	done
}

# change the O option for the kernel
# doesn't work with some patched kernels
function build_dir_menu()
{
	output_dir=$( "${dialog[@]}" --nocancel --inputbox \
	"Would you like to select a kernel output dir.\n\
	Leave it blank for no output dir\n" 0 0 ${output_dir} )
	retval="$?"
	echo $retval
	if [[ "$retval" == "0" && "$output_dir" != "" ]]
	then
		OPTIONAL_BUILD_DIR=$output_dir
		persistent_add OPTIONAL_BUILD_DIR
		echo "first ifstatment"
	elif [[ "$retval" == "0" && "$output_dir" == "" ]]
	then
		persistent_remove OPTIONAL_BUILD_DIR
		echo "second ifstatment"
	else
		echo "Do nothing"
	fi
}

# restore a config file from a different location
function restore_config()
{
	. ${SPELL_DIRECTORY}/DETAILS
	while true
	do
		CONFIG_RESTORE=$( "${dialog[@]}" --title "Config Restore" \
			--item-help --menu \
			"Restore Config From:" 0 0 0 \
			"/proc/config.gz" "Restore" "Save /proc/config.gz your kernel tree" \
			"/boot/config-$VERSION" "Restore" "Save /boot/config-$VERSION to your kernel tree" \
			"Custom" "Restore" "Save custom config file to your kernel tree" \
			)
		retval="$?"
		if [ "$retval" == "0" ]
		then
			persistent_add CONFIG_RESTORE
			case "${CONFIG_RESTORE}" in
				"Custom")
					CONFIG_RESTORE=$( "${dialog[@]}" --title "Config Restore" \
						--nocancel --inputbox \
						"Enter Custom File to Save to /usr/src/linux/.config" \
						0 0 ${CONFIG_RESTORE} )
					;;
					*)
					;;
			esac
			break
		else
			break
		fi
	done
}

# other misc options menu
function options_menu()
{
	while true
	do
		menu_option=$( "${dialog[@]}" --title "Options Menu" \
			--item-help --menu \
			"Options Menu Yeah Fun" 0 0 0 \
			"Clean" "Options" "Clean the Source Tree" \
			"Arch" "Selection" "Select Arch Default is $ARCH" \
			"Config" "Restoration" "Optional config restoration" \
			"Other" "Packages" "Make an RPM or Debian Package" \
			"Make" "Options" "Options passed to make for compiling" 
			)
		retval="`echo $?`"
		if [[ "$retval" == "0" ]]
		then
			case "${menu_option}" in
				"Clean")
					clean_menu
					;;
				"Arch")
					arch_menu
					;;
				"Other")
					other_package_menu
					;;
				"Config")
					restore_config
					;;
				"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 "")
					persistent_add EXTRA_MAKE_OPTIONS
					;;
				*) break ;;
			esac
		else
			break
		fi
	done
}

function custom_kernel()
{
	command=$("${dialog[@]}" --inputbox "Enter the kernel version for the source you want to build?\nExamples:\n2.4.31\n2.6.13.4-selinux1\n${LATEST_2_6}" 0 0 "")
	retval=$?
	echo $command
	if [[ "$?" == "0" ]] 
	then
		BASE_KVER=$command
		old_src_tree="/usr/src/linux-${BASE_KVER}"
		persistent_add old_src_tree
		KLINUX_RECONFIGURE="true"
		persistent_remove MAKEMODE
		persistent_remove UPDATE_BL
		persistent_remove 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?
function new_tree_menu() 
{
	while true
	do
		menu=""
		versions="`ls -r1 $KERNELS_DIRECTORY`"
		for version in $versions
		do
			menu="${menu}${version} Linux_Kernel Select_one "
		done
		menu="$( awk -F= '$1 ~ /LATEST_2/ {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
				2.[0-9].[0-9]*) 
					BASE_KVER=$command
					patch_menu $BASE_KVER
					;;
				LATEST*)
					BASE_KVER=$command
					patch_menu $BASE_KVER
					;;
				CUSTOM*)
					custom_kernel
					;;
				*) 
					break 
					;;
			esac
		else
			break
		fi
	done
}

# find all patches list the directories and LATEST patches for the kernel version $1
function 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 -1r $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
function 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 -1r $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
function 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 "Commit" \
			--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
}

function 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 seleted 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
function 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
				old_var_compat ${old_src_tree}/.spell-config.p
				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
}

function load_old_src_tree()
{
	local tmp
	old_var_compat ${old_src_tree}/.spell-config.p
	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
	if [ "$BASE_KVER" == "" ]
	then
		echo "You really need to tell the spell what linux kernel you are dealing with."
		sleep 5
	fi
	while [ "$BASE_KVER" == "" ]
	do
		main_menu
		if [ "$BASE_KVER" == "" ]
		then
			echo "You really need to tell the spell what linux kernel you are dealing with."
			sleep 5
		fi	
	done
else
	echo "No Reconfigure, using ${SPELL_CONFIG}.p for defaults"
fi &&
run_conflicts &&
create_details
