#!/bin/bash
# kernel information directory
local KERNELS_DIRECTORY="${SPELL_DIRECTORY}/info/kernels"

# patches information directory
local KPATCHES_DIRECTORY="${SPELL_DIRECTORY}/info/patches"
# sourced from DETAILS to create SOURCE_URL SOURCE
# KERNEL_VERSION environmental vars
local USER_KDETAILS="${CONFIG_CACHE}/${SPELL}.details.defaults"
local DEPENDS_MAKEFILE="${CONFIG_CACHE}/${SPELL}.makefile.patches"
# Latest versions of your favorite kernel sources and patches
local LATEST_KDEFAULTS="${SPELL_DIRECTORY}/latest.defaults"

# Source patch info for the patch
# this is used for getting specific information about the source name source url
# verification checking what the dependancies are, things like that.
function source_patch()
{	
	local tmploc
	case $1 in
		LATEST*)
			tmploc=$(find $KPATCHES_DIRECTORY/*/${!1} | head -n 1)
			;;
		*)
			tmploc=$(find $KPATCHES_DIRECTORY/*/${1} | head -n 1)
	esac &&
	[ -f $tmploc ] && . $tmploc
}

# Source info for patch
# this is used for general info about the patch, what the patch is, contains,
# made by, things like that.
function source_info()
{
	local tmploc
	case $1 in
		LATEST*)
			tmploc=$(find $KPATCHES_DIRECTORY/*/${!1} | head -n 1)
			;;
		*)
			tmploc=$(find $KPATCHES_DIRECTORY/*/$1 | head -n 1)
			;;
	esac &&
	[ -f `dirname $tmp`/.info ] && . `dirname $tmploc`/.info
}

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

function linux_sort_list()
{
	local upvar=$1
  local list="$2"
	local new_list=""
	local elem
	for elem in $list
	do
		if [[ ${elem/LATEST_/} == ${elem} ]]
		then
			new_list="${new_list}${elem} "
		else
			new_list="${elem} ${new_list}"
		fi
	done
	eval "$upvar=\"${new_list}\""
}

function fix_conflicts_dialog()
{
	local list="$@"
	local new_list
	local menu_list=""
	local elem
	local retval
	for elem in $list
	do
		menu_list="${menu_list}$elem patch off "
	done
	while true
	do
	  new_list=$( "${dialog[@]}" --title "Fix Conflicting Patches" \
	              --cancel-label "Cancel" --ok-label "Okay" --checklist \
		  					"You have conflicting patches.\nSelected patches will be removed." \
		  					0 0 0 ${menu_list} )
		retval=$?
		if [[ $retval == 0 ]]
		then
		  echo "${new_list//\"}"
			for elem in ${new_list//\"}
			do
				SELECTED_PATCHES="${SELECTED_PATCHES//$elem }"
				SELECTED_PATCHES="${SELECTED_PATCHES%%$elem}"
			done
			break
		else
			break
		fi
	done
	return $retval
}

function run_conflicts()
{
  local patch
	local conflicting_patch
	local conflicting_list=""
	message "${MESSAGE_COLOR}Generating Conflicts From:${DEFAULT_COLOR}"
	echo $SELECTED_PATCHES
	for patch in $SELECTED_PATCHES
	do
		source_patch ${patch}
		for conflicting_patch in $SELECTED_PATCHES
		do
# duplicate patches conflict
# just remove it...
			if [[ ${patch} == ${conflicting_patch} ]]
			then
				SELECTED_PATCHES="${SELECTED_PATCHES//$patch } ${patch}"
			fi

# built in conflicts between LATEST_<patches>
			if [[ ${patch/LATEST_} != ${patch} && ${conflicting_patch/LATEST_} != ${conflicting_patch} && ${patch} != ${conflicting_patch} ]]
			then
				conflicting_list="${conflicting_list//$patch }${patch} "
			fi

# same directory patches also conflict
			if [[ -f $KPATCHES_DIRECTORY/${patch/LATEST_}/${conflicting_patch} ||
			      -f $KPATCHES_DIRECTORY/${conflicting_patch/LATEST_}/${patch} ]]
			then
				conflicting_list="${conflicting_list//$conflicting_patch }${conflicting_patch} "
				conflicting_list="${conflicting_list//$patch }${patch} "
			fi
		done
	done
	linux_sort_list conflicting_list "${conflicting_list}"
	if [[ ${conflicting_list} ]]
	then
		message "${MESSAGE_COLOR}Conflicting Patches${DEFAULT_COLOR}"
		message "${PROBLEM_COLOR}${conflicting_list}${DEFAULT_COLOR}"
		fix_conflicts_dialog ${conflicting_list} &&
		run_conflicts
	else
		message "${MESSAGE_COLOR}No Conflicting Patches${DEFAULT_COLOR}"
	fi
}

# if any of the patches needs any other patches applied before it is
# for example the mm patches apply against the pre patched kernel
function calculate_depends()
{
	# create Makefile
	dependslist=""
	new_patches_list=""
	all_patches_list=""
	if [[ "$SELECTED_PATCHES" != "" ]]
	then
		# check for post patches and add those to the new_patches_list
		for patch in $SELECTED_PATCHES
		do
			source_patch ${patch}
			if [ "$post_patch" != "" ]
			then
				new_patches_list="${new_patches_list}${patch} ${post_patch} "
				case "${patch}" in
					LATEST*)
						all_patches_list="${all_patches_list}${!patch} ${post_patch} "
					;;
					*)
						all_patches_list="${all_patches_list}${patch} ${post_patch} "
					;;
				esac
			else
				new_patches_list="${new_patches_list}${patch} "
				case "${patch}" in
					LATEST*)
						all_patches_list="${all_patches_list}${!patch} "
					;;
					*)
						all_patches_list="${all_patches_list}${patch} "
					;;
				esac
			fi
		done
		rm -f $DEPENDS_MAKEFILE
		(
		makefile_targets="all "
		echo "all: $all_patches_list"
		echo ""
		for i in $new_patches_list
		do
			rec_dep_make $i
		done
		echo ".PHONY : ${makefile_targets}"
		) > $DEPENDS_MAKEFILE
		# this doesn't work with persistent vars don't know why?
		CANONICAL_PATCHES=$(make -sf $DEPENDS_MAKEFILE all 2>/dev/null)
		echo $CANONICAL_PATCHES
	fi
}

function rec_dep_make()
{
	depends=""
	case "$1" in
		LATEST*)
			source_patch ${1}
			if [[ "${depends}" == "" ]] 
			then
				makefile_targets="${makefile_targets} ${!1} "
				echo "${!1}:"
				echo "	@echo -n \"${1} \""
				echo ""
			else
				makefile_targets="${makefile_targets} ${!1} ${depends} "
				echo "${!1}: ${depends}"
				echo "	@echo -n \"${1} \""
				echo ""
				rec_dep_make ${depends}
			fi	
		;;
		*)
			source_patch ${1}
			if [[ "${depends}" == "" ]] 
			then
				makefile_targets="${makefile_targets} ${1} "
				echo "${1}:"
				echo "	@echo -n \"${1} \""
				echo ""
			else
				makefile_targets="${makefile_targets} ${1} ${depends} "
				echo "${1}: ${depends}"
				echo "	@echo -n \"${1} \""
				echo ""
				rec_dep_make ${depends}
			fi
		;;
	esac
}

# created details.defaults file to be sourced by DETAILS file for VERSION SOURCE_URL SOURCE
# stuff like that
function create_details()
{
	local counter="3"
	local patches 
	local basekver 
	local source_gpg
	local source_gpg_list 
	local source_gpg_list_url
	local gpg_source_url
	local version_append_list
	local depends
	local patch
	local md5sum
	local sha1sum
	local patch_det_version="false"
	case "$KMODE" in
		*ktree)
			CANONICAL_PATCHES=""
			basekver=""
			source_gpg_list=""
			source_gpg_list_url=""
			
			calculate_depends
			(
			depends=""
			echo 'TXTMESSAGE=""'
			
			# deal with the basic sources for the patches
			# involes the main source for each patch and figuring
			# out the base kernel version to use
			for patch in $CANONICAL_PATCHES
			do
				source=""
				source_url=""
				source_gpg=""
				gpg_source_url=""
				md5sum=""
				sha1sum=""
				appliedkernels=""
				patchversion=""
				appendversion=""
				source_patch $patch
				case "$patch" in
					LATEST*)
						echo "PATCH[${counter}]=\$${patch}"
						;;
					*)
						echo "PATCH[${counter}]=${patch}"
						;;
				esac
				echo -n '. ${SPELL_DIRECTORY}/info/patches/*/${PATCH['
				echo -n $counter
				echo ']}'
				echo "SOURCE${counter}=\$source"
				echo "SOURCE${counter}_URL=\$source_url"
				echo 'TXTMESSAGE="${TXTMESSAGE}${txtmessage} "'
				echo 'SHORT="${SHORT}${short} "'
				if [[ "" != "$patchversion" ]]
				then
					basekver="$appliedkernels"
					echo 'KERNEL_VERSION=${patchversion}'
					patchversion=""
					patch_det_version="true"
				fi
				if [[ "$appendversion" != "" ]]
				then
					version_append_list="${version_append_list}${patch}:${counter} "
					patch_det_version="true"
				fi
				if [[ "$source_gpg" != "" ]] 
				then
					echo "SOURCE${counter}_GPG=\$source_gpg"
					if [[ "$gpg_source_url" != "" ]]
					then
						source_gpg_list_url="${source_gpg_list_url}${patch}:${counter} "
					fi
				elif [[ "$sha1sum" != "" ]]
				then
					echo "SOURCE${counter}_HASH=\"sha1:\$sha1sum\""
				elif [[ "$md5sum" != "" ]]
				then
					echo "SOURCE${counter}_HASH=\"md5:\$md5sum\""
				else
					echo "SOURCE${counter}_IGNORE=UNVERIFIED"
				fi
				(( counter++ ))
			done
			# deal with the version appending
			for patch in $version_append_list
			do
				echo -n '. ${SPELL_DIRECTORY}/info/patches/*/${PATCH['
				echo -n ${patch/*:}
				echo ']}'
				echo 'KERNEL_VERSION=${KERNEL_VERSION}${appendversion}'
			done
			# deal with the signatures for each patch if there is
			# any
			for patch in $source_gpg_list_url 
			do
				source_gpg=""
				gpg_source_url=""
				source_patch ${patch/:*}
				case "${patch/:*}" in
					LATEST*)
						tmp=${patch/:*}
						;;
					*)
						;;
				esac &&
				echo -n '. ${SPELL_DIRECTORY}/info/patches/*/${PATCH['
				echo -n ${patch/*:}
				echo ']}'
				echo "SOURCE${counter}=\$(echo \$source_gpg | cut -d: -f2)"
				echo "SOURCE${counter}_URL=\$gpg_source_url"
				echo "SOURCE${counter}_IGNORE=signature"
				(( counter++ ))
			done
			if [[ "$basekver" != "" ]]
			then
				BASE_KVER="$basekver"
			fi
                        case "$BASE_KVER" in
				4*|3.[0-9]*)
					. $KERNELS_DIRECTORY/${BASE_KVER}
					echo ". \${SPELL_DIRECTORY}/info/kernels/${BASE_KVER}"
					echo "SOURCE=\$source"
					echo "SOURCE_URL=\$source_url"
					echo "SOURCE2=\$source2"
					echo "SOURCE_GPG=kernel.gpg:\${SOURCE2}:ESTABLISHED_UPSTREAM_KEY"
					echo "SOURCE2_URL=\$source2_url"
					echo "SOURCE2_IGNORE=signature"
					;;
				3*|2.[0-9]*)
					. $KERNELS_DIRECTORY/${BASE_KVER}
					echo ". \${SPELL_DIRECTORY}/info/kernels/${BASE_KVER}"
					echo "SOURCE=\$source"
					echo "SOURCE_URL=\$source_url"
					echo "SOURCE2=\${source}.sign"
					echo "SOURCE_GPG=kernel.gpg:\${SOURCE2}:ESTABLISHED_UPSTREAM_KEY"
					echo "SOURCE2_URL=\${source_url}.sign"
					echo "SOURCE2_IGNORE=signature"
					;;
				*)
					. $KERNELS_DIRECTORY/${!BASE_KVER}
					echo ". \${SPELL_DIRECTORY}/info/kernels/\$$BASE_KVER"
					echo "SOURCE=\$source"
					echo "SOURCE_URL=\$source_url"
					echo "SOURCE2=\${source}.sign"
					echo "SOURCE_GPG=kernel.gpg:\${SOURCE2}:ESTABLISHED_UPSTREAM_KEY"
					echo "SOURCE2_URL=\${source_url}.sign"
					echo "SOURCE2_IGNORE=signature"
					;;
			esac
			# if there are no patches then KERNEL_VERSION isn't put in the details.defaults file so
			# you have to check to make sure KERNEL_VERSION is in the file and used
			if [[ "$patch_det_version" == "false" ]]
			then
				echo 'KERNEL_VERSION=${version}'
			fi
			) > $USER_KDETAILS
			;;
		expert)
			# shouldn't have any source information when expert
			# is being used
			echo "KERNEL_VERSION=$BASE_KVER" > $USER_KDETAILS
			;;
		*)
			echo "I don't know what the hell is $KMODE"
			;;
	esac
}

