#!/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.
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.
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" )

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}\""
}

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
}

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
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
}

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
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 sha512sum
  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=""
        sha512sum=""
        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 ']}'
        if [[ "$source" != "" ]] # safeguard against X.Y.0 patches when no patch sources exist
        then
          echo "SOURCE${counter}=\$source"
          echo "SOURCE${counter}_URL=\$source_url"
          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 [[ "$sha512sum" != "" ]]
          then
            echo "SOURCE${counter}_HASH=\"sha512:\$sha512sum\""
          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
        fi
        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
        (( 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"
          ;;
        5*|6*)
          . $KERNELS_DIRECTORY/${BASE_KVER}
          echo ". \${SPELL_DIRECTORY}/info/kernels/${BASE_KVER}"
          echo "SOURCE=\$source"
          echo "SOURCE_URL=\$source_url"
          echo "SOURCE_HASH=\$source_hash"
          ;;
        3*)
          . $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"
          if test -n "$source2"
          then
            echo "SOURCE2=\$source2"
            echo "SOURCE_GPG=kernel.gpg:\${SOURCE2}:ESTABLISHED_UPSTREAM_KEY"
            echo "SOURCE2_URL=\$source2_url"
            echo "SOURCE2_IGNORE=signature"
          fi
          if test -n "$source_hash"
          then
            echo "SOURCE_HASH=\$source_hash"
          fi
          ;;
      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
}

