#!/bin/bash
#---
## @Synopsis Spell creator script for SourceMage GNU/Linux
## @Copyright Copyright 2006-2007 SourceMage GNU/Linux
## @License GPL v2 or higher
##
## @ToDo Add option to use upstream gpg signature as included or
## @ToDo downloadable .sig file
## @ToDo Put a multiversion option
## @ToDo Interface to add CONFIGURE options
## @ToDo Make quill the be-all for spells
##
## Asks the user in simple questions about generating a spell.
## @Globals BUILDISON, INSTALLISON, PREBUILDISON, TRIGGERSISON
## @Globals SPELL_HISTORY_NAME, SPELL_HISTORY_EMAIL, SPELL_NAME,
## @Globals SPELL_SRC_URL, SPELL_LICENSE, SPELL_URL, SPELL_SHORT_DESCRIPTION,
## @Globals SPELL_DESCRIPTION, SPELL_DEPENDENCIES, SPELL_OPTIONAL_DEPENDENCIES,
## @Globals SPELL_CONFLICTS, HISOTRY_DATE, SPELL_SRC_FILE,
## @Globals SPELL_SANITIZED_FILE_NAME, SPELL_MD5_UNPACKED, SPELL_VERSION,
## @Globals i, j, n, BUILD_DTFILE, DTFILE_MENUENTRY, DTFILE_EXEC
## @Globals DTFILE_MENUPATH, DTFILE_ICON, DTFILE_TERM, SPELL_SRC_DIR
##
## @Thanks BearPerson, dufflebunk, afrayedknot
## @Thanks To all testers
## @Contribution abouter, lynxlynxlynx, iuso
##
#---
QUILL_VERSION="$(< /var/lib/quill/version)"
#---
## really basic stuff that should be loaded and set
. /var/lib/sorcery/modules/libmedia
LOCAL_MEDIA_CONFIG=/etc/sorcery/local/media source /etc/sorcery/media
function message() { echo -e "$@"; }
. /var/lib/quill/modules/libcore
## end of skeletal prerequisites - we get the rest later

QUILL_MODE="apprentice"
HISTORY_DATE="$(date +%Y-%m-%d)"

QUILL_HOME_DIR=~/.sourcemage/quill
QUILL_QUILLRC=${QUILL_HOME_DIR}/quillrc
QUILL_OLD_QUILLRC=~/.quillrc
QUILL_SPELL_DIR=~/.sourcemage/spells
QUILL_TMP_DIR=~/.sourcemage/tmp
QUILL_CONFIG_VERSION=1
QUILL_OLD_CONFIG_VERSION=$QUILL_CONFIG_VERSION

#---
## Not really a function it's supposed to parse the command line parameters
#---
while [[ -n $1 ]]; do
  if grep -q "^ -" <<< " $1"; then
    case  $1  in
      --fmxml|-f)
                  check_parameter $2
                  QUILL_FETCH_MODE="fmxml"
                  shift ;;
   --perlcpan|-c)
                  check_parameter $2
                  QUILL_FETCH_MODE="cpan"
                  shift ;;
    --rubyraa|-y)
                  check_parameter $2
                  QUILL_FETCH_MODE="raa"
                  shift ;;
     --update|-u)
                  check_parameter $2
                  QUILL_UPDATE="on"
                  shift ;;
         -uf|-fu)
                  check_parameter $2
                  QUILL_UPDATE="on"
                  QUILL_FETCH_MODE="fmxml"
                  shift ;;
         -uc|-cu)
                  check_parameter $2
                  QUILL_UPDATE="on"
                  QUILL_FETCH_MODE="cpan"
                  shift ;;
         -yu|-uy)
                  check_parameter $2
                  QUILL_UPDATE="on"
                  QUILL_FETCH_MODE="raa"
                  shift ;;
      --purge|-p) QUILL_MODE="purge"; quill_purge $2 ;;
 --apprentice|-a) QUILL_MODE="apprentice" ;;
       --mage|-m) QUILL_MODE="mage" ;;
     --wizard|-w) QUILL_MODE="wizard" ;;
--reconfigure|-r) QUILL_MODE="reconfigure" ;;
       --help|-h) quill_help ;;
    --version|-v) quill_version ;;
               *) error_msg "Invalid parameter $1"
                  quill_help 102 ;;
    esac
  fi
  shift
done

unset -f message
. /etc/sorcery/config
EDITOR=${EDITOR:-nano}
for i in /var/lib/quill/modules/lib*
do
  . $i
done

quill_rc
if [[ ${QUILL_CONFIG_VERSION/0.2.2/1} != $QUILL_OLD_CONFIG_VERSION ]]; then
  QUILL_MODE="reconfigure"
  error_msg "Your quill settings are too old, forcing a reconfiguration!"
  quill_rc
fi

# we should do some quillrc sanity checking someday
[[ ! -d $QUILL_SPELL_DIR ]] &&
  error_msg "Bad QUILL_SPELL_DIR ($QUILL_SPELL_DIR)!" &&
  echo "Try running quill -r" &&
  exit 7

PROMPT_DELAY="31557807"
BUILD_API=2
load_build_api
mkdir -p $QUILL_TMP_DIR
SPELL_UPDATED=n

# devel sorcery function
if ! declare -f codex_get_spell_paths &> /dev/null; then
  function codex_get_spell_paths() {
    local SPELL_DIRECTORY=$1
    SECTION_DIRECTORY=${SPELL_DIRECTORY%/*}
    SECTION=${SECTION_DIRECTORY##*/}
    GRIMOIRE=${SECTION_DIRECTORY%/*}
    GRIMOIRE_NAME=$(basename $GRIMOIRE)
  }
fi

## Down to here... from here it's question and answer time
#---

#---
## Questions and stuff

quill_welcome
if [[ ! -z $QUILL_FETCH_MODE ]]
then
  # fetch various spell variables
  quill_${QUILL_FETCH_MODE}_core $QUILL_TARGET

  # creates SPELL_VERSION and SPELL_SRC_FILE (to be SOURCE)
  parse_spell_source_file_info
fi

if [[ "$QUILL_UPDATE" == "on" ]]
then
  SPELL_NAME=$QUILL_TARGET
  codex_does_spell_exist $SPELL_NAME || exit 6
  ask_and_copy_over || exit 7
  cd $QUILL_SPELL_DIR/$SPELL_NAME

  while true; do
    actions=('Update the spell to a newer version' \
             'Add arbitrary HISTORY entries' \
             'Increment/add PATCHLEVEL or SECURITY_PATCH' \
             'Copy it here from the grimoire or QUILL_GIT_DIR' \
             'Copy it under QUILL_GIT_DIR' \
             'Copy it back to the grimoire' \
             'Try it out' \
             'Quit' ) #'Manage patches' 'Add a another version' 'Convert to upstream signing'
    echo
    query_list "What do you want to do?" ANSWER "Quit" "${actions[@]}"

    [[ $ANSWER == "Quit" ]] && exit 0

    if [[ $ANSWER == ${actions[0]} || $ANSWER == ${actions[1]} || $ANSWER == ${actions[2]} ]]
    then
      echo
      [[ -d $QUILL_SPELL_DIR/$SPELL_NAME ]] || ask_and_copy_over || continue
      [[ -e DETAILS.orig ]] || cp DETAILS DETAILS.orig
      echo
    fi
    case "$ANSWER" in
      "${actions[0]}") #"Update the spell to a newer version"

        #fetch current versions, ignoring SCM ones
        versions=( $(sed -n 's/\s*\<VERSION=//p' DETAILS |
                   grep -Eiv '\$[(]date *+|cvs|svn|tla|git|scm' | sort | uniq) )
        [[ -z ${versions[@]} ]] && error_msg "No versions to update!" && continue
        message "Current relevant version(s): ${versions[@]}"

        update_counter="${#versions[@]}"
        #suggest the new one from fm and/or ask for input
        for version in "${versions[@]}"
        do
          if query "Do you want to update $version?" y
          then
            query_spell_version
            [[ -z $SPELL_VERSION ]] && error_msg "Empty VERSION!" && continue 2

            #save the chosen version
            sed -i "s/\sVERSION=['\"]*${version}['\"]*\s*$/ VERSION=$SPELL_VERSION/" DETAILS
            SPELL_UPDATED=y

            #check for multiversion spells
            multiversion_check

            # reparse the (usually new) source urls
            get_sources_and_urls

            # check that (usually new) source urls are ok
            check_source_urls

            # reparse the possibly new source urls and company
            get_sources_and_urls
            echo

            message "Summoning ..."
            #download; we are not a grimoire so some hacking is needed
            # also workaround sorcery bug #13412
            to_summon="$sources_and_urls"
            for source in $sources
            do
              [[ -e $SOURCE_CACHE/$source ]] &&
              to_summon="$(sed "/^$source /d" <<< "$to_summon")"
            done
            if [[ -z $to_summon ]]
            then
              message "Nothing new to summon, all sources found."
            else
              if ! summon -r <<< "$to_summon"
              then
                error_msg "Summoning failed, aborting update!"
                query "Do you want to edit DETAILS? (you can retry the update)" n && quill_edit DETAILS
                continue 2
              fi
            fi
            echo

            #update HISTORY - already here, so that the patchlevels come later
            add_history_entries "DETAILS: updated ${if_var:-spell} to $SPELL_VERSION"
            echo

            # ask for SECURITY_PATCHing
            update_patchlevel SECURITY_PATCH

            # restore patchlevel
            update_patchlevel PATCHLEVEL skip remove

            # remove UPDATED if any
            sed -i "/^\s*UPDATED=/d" DETAILS

            #get hashes and save them, removing any previous hash/gpg
            i=
            for source in $sources
            do
              hash=$(quill_hash_get $SOURCE_CACHE/$source)
              # use the original DETAILS, as we already changed the VERSION
              current_check=$(eval ${if_var:-:}; . DETAILS.orig &> /dev/null; eval echo \$SOURCE${i}_HASH\$SOURCE${i}_GPG\$SOURCE${i}_IGNORE\$\{MD5[${i:-0}]\})

              if [[ -z $current_check ]]
              then
                error_msg "Missing SOURCE${i}_HASH/SOURCE${i}_GPG/SOURCE${i}_IGNORE"
                message "Add the appropriate one to the spell!"
                [[ ! $i ]] && i=1
                let i+=1
                continue
              fi

              #skip SOURCE_IGNORE
              if grep -Eq "volatile|signature|unversioned" <<< "$current_check"
              then
                [[ ! $i ]] && i=1
                let i+=1
                continue
              fi

              # MD5? These contain no :
              if ! grep -q ":" <<< "$current_check"
              then
                new_value=$(count_spaces SOURCE${i}_HASH)
                sed -i "s/^.*MD5\[${i:-0}\]=['\"]*${current_check}['\"]*\s*$/${new_value}SOURCE${i}_HASH=sha512:$hash/" DETAILS
                remove_old_sigs "$version"

                [[ ! $i ]] && i=1
                let i+=1
                continue
              fi

              #SOURCE_GPG?
              if grep -Eq "(^(sha(512|256|1)|md5):.*|UPSTREAM_KEY)" <<< "$current_check"
              then
                # UPSTREAM_HASH is rare and probably not fetched - ignoring
                if grep -q "UPSTREAM_KEY" <<< "$current_check"
                then
                  [[ ! $i ]] && i=1
                  let i+=1
                  continue
                fi
                # otherwise just do nothing, SOURCE_HASH handling comes next
              else
                #it is a non-upstream gpg
                echo "SOURCE${i}_GPG=$current_check"
                if ! grep -q "gurus.gpg" <<< "$current_check"
                then
                  error_msg "This looks like an upstream keyring and should be marked as such."
                  error_msg "Probably with :UPSTREAM_KEY at the end of the $current_check."
                  error_msg "Do not replace it!"
                fi
                if ! query "Do you want to replace SOURCE${i}_GPG with a hash?" n
                then
                  # sign it and continue
                  gpg --default-key $GURU_GPGKEY -v --digest-algo SHA512 -o $QUILL_SPELL_DIR/$SPELL_NAME/$source.sig --detach-sign $SOURCE_CACHE/$source
                  echo
                  remove_old_sigs "$version"

                  [[ ! $i ]] && i=1
                  let i+=1
                  continue
                fi
              fi

              # it is either a SOURCE_HASH or the user wants to replace SOURCE_GPG
              # SOURCE_GPG usually has unexpanded variables, while we have the expanded
              unexpand_source_gpg || {
              [[ ! $i ]] && i=1
              let i+=1
              continue; }


              #it is either a SOURCE_HASH or the user wants to replace SOURCE_GPG
              new_value=$(count_spaces SOURCE${i}_HASH)
              sed -i "s/^.*SOURCE${i}_[^=]*=['\"]*${current_check}.*['\"]*\s*$/${new_value}SOURCE${i}_HASH=sha512:$hash/" DETAILS

              # remove sigs, trying not to touch any that are still needed
              remove_old_sigs "$version"

              [[ ! $i ]] && i=1
              let i+=1
            done

            echo
          else
            let update_counter-=1
          fi
        done

        if [[ $update_counter == 0 ]];
        then # there were no updates
          continue
        fi

        # check if WEB_SITE is reachable
        WEB_SITE=$(. DETAILS &> /dev/null; echo $WEB_SITE)
        if wget -S --spider "$WEB_SITE" 2>&1 | grep -Eq "ERROR 404|service not known"
        then
          error_msg "The website is not reachable: $WEB_SITE"
          message "Fix it."
          sleep 3
          quill_edit DETAILS
          add_history_entry "DETAILS: fixed WEB_SITE"
          echo
        fi

        rm DETAILS.orig
        if query "Do you want to review the update or add custom modifications?" y
        then
          quill_edit
          echo
        fi

        echo
        message "Done with updating the spell!"
        echo

      ;;
      "${actions[1]}") #"Add an arbitrary HISTORY entry"
        add_user_history_entries
      ;;
      "${actions[2]}") #"Increment/add PATCHLEVEL or SECURITY_PATCH"
        #fetch current versions, ignoring SCM ones
        versions=( $(sed -n 's/\s*\<VERSION=//p' DETAILS |
                   grep -Eiv '\$[(]date *+|cvs|svn|tla|git|scm' | sort | uniq) )
        [[ -z ${versions[@]} ]] && error_msg "No versions to update!" && continue

        message "Current relevant version(s): ${versions[@]}"

        for version in "${versions[@]}"
        do
          if query "Do you want to do it for $version?" y
          then
            SPELL_VERSION="$version"
            update_patchlevel PATCHLEVEL
            update_patchlevel SECURITY_PATCH
            message "Done."
            SPELL_UPDATED=y
          fi
        done
      ;;
#      "${actions[2]}") #"Add another version"
#SCM or branch?
#update patchlevel?
#update HISTORY
#copy the spell back to the grimoire?
      "${actions[3]}") #"Copy it here from the grimoire or QUILL_GIT_DIR"
        ask_and_copy_over || continue
      ;;
      "${actions[4]}") #"Copy it under $QUILL_GIT_DIR"
        copy_to_git || continue
      ;;
      "${actions[5]}") #"Copy it back to the grimoire"
        codex_get_spell_paths $(codex_find_spell_by_name $SPELL_NAME)
        if [[ ! -e $QUILL_SPELL_DIR/$SPELL_NAME ]]
        then
          error_msg "There is nothing to copy back!"
          continue
        fi
        if [[ ! -e $GRIMOIRE/$SECTION/$SPELL_NAME ]]
        then
          error_msg "Origin not found! Very severe!"
          continue
        fi

        quill_final_put_in_grimoire $GRIMOIRE $SECTION no
      ;;
      "${actions[6]}") #"Try it out"
        codex_get_spell_paths $(codex_find_spell_by_name $SPELL_NAME)
        if [[ ! -e $GRIMOIRE/$SECTION/$SPELL_NAME ]]
        then
          error_msg "Origin not found! Very severe!"
          continue
        fi

        # three possible states:
        # 1. the files are the same, no update was made
        # 2. the files are the same, an update was made and copied over
        # 3. the files differ
        # in the case of 1 and 3 we want to confirm
        if [[ $SPELL_UPDATED == n ]]
        then
          # 1. there is no difference; there is no change
          query "Are you sure you want to cast the unchanged spell from the grimoire?" y ||
          continue
        fi
        if ! diff -q $QUILL_SPELL_DIR/$SPELL_NAME $GRIMOIRE/$SECTION/$SPELL_NAME -x DETAILS.orig > /dev/null
        then
          # 3. the spell was changed, but not copied over yet
          query "Are you sure you want to cast the unchanged spell from the grimoire?" y ||
          continue
        fi

        if query "Do you want to cast -r? (possibly needed for multiversion spells)" n; then
          cast -r $SPELL_NAME
        else
          cast -c $SPELL_NAME
        fi
      ;;
    esac

  done
else
  query_spell_name
  query_spell_source_url
  query_spell_license
  query_spell_url
  query_spell_short_description
  query_spell_description
  query_spell_dependencies
  query_spell_optional_dependencies
  query_spell_build
  query_spell_install
  query_spell_desktop_file

  if [[ "${QUILL_MODE}" == "mage" ]] || [[ "${QUILL_MODE}" == "wizard" ]]
  then
    query_spell_pre_build
    query_spell_prepare
    query_spell_conflicts
    #-query_spell_configure
    query_spell_provides
    #-query_spell_final
    #-query_spell_triggers
    #-query_spell_solo
    #-query_spell_configs
    #-query_spell_volatiles
    if [[ "${QUILL_MODE}" == "wizard" ]]
    then
      #-  query_spell_pre_install
      #-  query_spell_post_install
      #-  query_spell_pre_remove
      #-  query_spell_post_remove
      #-  query_spell_pre_resurrect
      #-  query_spell_post_resurrect
      true
    fi
  fi


  create_spell_base

  if [[ "${SPELL_DEPENDENCIES}" != "" ]]
  then
    add_dependencies
  fi

  if [[ "${SPELL_OPTIONAL_DEPENDENCIES}" != "" ]]
  then
    add_optional_dependencies
  fi

  # add && where appropriate in DEPENDS
  if [[ ${SPELL_OPTIONAL_DEPENDENCIES} != "" ]] || [[ ${SPELL_DEPENDENCIES} != "" ]]
  then
    sed -e 's/^ *$//' -e 's/[^\\]$/& \&\&/' DEPENDS | tac |
      awk '{ if (removed!=1){ if (sub("&&$","") > 0){ removed=1 }}; print }' |
      tac > temporary-DEPENDS
    mv temporary-DEPENDS DEPENDS
  fi

  if [[ "${SPELL_CONFLICTS}" != "" ]]
  then
    add_conflicts
  fi

  if [[ "${SPELL_PROVIDES}" != "" ]]
  then
    add_provides
  fi

  # redo it for fmxml - so it works if the xml didn't contain a valid source url
  # first time for other modes
  parse_spell_source_file_info
  show_spell_source_file_info
  echo

  get_spell_source

  hunt_src_dir

  add_details

  if [[ "${PREPAREISON}" == "PREPARE, " ]]
  then
    add_prepare
  fi

  #-if [[ "${CONFIGUREISON}" == "CONFIGURE, " ]]
  #-then
  #-  add_configure
  #-fi

  if [[ "${PREBUILDISON}" == "PRE_BUILD, " ]]
  then
    add_pre_build
  fi

  if [[ "${BUILDISON}" == "BUILD, " ]]
  then
    add_build
  fi

  if [[ "${INSTALLISON}" == "INSTALL, " ]]
  then
    add_install
  fi

  #-if [[ "${POSTINSTALLISON}" == "POST_INSTALL, " ]]
  #-then
  #-  add_post_install
  #-fi

  #-if [[ "${TRIGGERSISON}" == "TRIGGERS, " ]]
  #-then
  #-  add_triggers
  #-fi

  if [[ "${BUILD_DTFILE}" != "" ]]
  then
    add_desktop_file
  fi

  add_history
  quill_set_executable_bit
  if query "Do you want to edit all the spell files?" n
  then
    quill_edit
  fi
  if query "Do you want to tarball the spell?" n
  then
    quill_final_tarball
  fi
  message ""
  message "The spell has been generated and put into: $QUILL_SPELL_DIR/$SPELL_NAME"
  if query "Do you want to put the spell into one of the avialable grimoires?" n
  then
    quill_final_put_in_grimoire &&
    if query "Do you want to *try* to cast the spell?" n
    then
      cast $SPELL_NAME
    fi
  fi
  if query "Do you want to put the spell into QUILL_GIT_DIR?" n
  then
    if [[ -z $QUILL_GIT_DIR ]]
    then
      error_msg "\$QUILL_GIT_DIR is empty, run quill -r to set it!"
      echo
    else
      find "$QUILL_GIT_DIR" -maxdepth 1 -type d -printf "%P "
      echo
      query_string GRIMOIRE "Which of the above is the target git grimoire? "
      dir_check "$QUILL_GIT_DIR/$GRIMOIRE" fatal
      echo
      # maybe we have QUILL_SECT_NAME from the previous query, so default to it
      find "$QUILL_GIT_DIR/$GRIMOIRE" -maxdepth 1 -type d -printf "%P\n" | column
      query_string SECTION "Which section do you want to put it in? " $QUILL_SECT_NAME
      dir_check "$QUILL_GIT_DIR/$GRIMOIRE/$SECTION" fatal
      if [[ -w $QUILL_GIT_DIR/$GRIMOIRE/$SECTION ]]
      then
        mkdir "$QUILL_GIT_DIR/$GRIMOIRE/$SECTION/$SPELL_NAME"
      else
        ${QUILL_SUDO:-su} -c "mkdir '$QUILL_GIT_DIR/$GRIMOIRE/$SECTION/$SPELL_NAME'"
      fi
      copy_to_git
    fi
  fi
fi
message "That's all folks!"

#---
##
## This software is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This software is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this software; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
##
#---
