#!/bin/bash
#---------------------------------------------------------------------
##
## @scribe
## 
## @Synopsis scribe handles adding/updating/reindexing/etc. of grimoires
##
## @Copyright
## Original version Copywright 2002 by Ryan Abrams
## Additions/Corrections Copyright 2002-2004 by the Source Mage Team
## Released under the GPL
#---------------------------------------------------------------------


#---------------------------------------------------------------------
## help
##
## print horribly unhelpful helpscreen
##
#---------------------------------------------------------------------
function help() {
  cat  <<  EOF

Scribe is a utility for controlling the grimoires in your codex,
and the spells in your grimoires.

Invoke scribe with the desired command followed by the target.
Note that most options can be called with the beginning of the command.

Command		Arguments	Description
add		grimoire [from source]	Adds a new grimoire
				 (optionally from a different URL)
remove		grimoire	Removes a grimoire
update		[grimoire]	Updates a grimoire or all grimoires

fix		grimoire	Attempts to fix metadata for grimoire
reindex		[grimoire]	Recreates the list of spells
index				Prints list of grimoires

set		grim1 grim2	puts grim1 before grim2
swap		grim1 grim2 	Swaps two grimoires

localize	grimoire	Makes a grimoire local
				 (so scribe won't try to update it)
unlocalize	grimoire	Makes a grimoire nonlocal


Grimoire tarballs are located at $CODEX_TARBALL_URL
Grimoire rsync modules are located at $CODEX_RSYNC_URL

EOF

  exit  1
}


#---------------------------------------------------------------------
## Common error messag when downloading a grimoire fails.
#---------------------------------------------------------------------
function scribe_download_fail_error_msg() {
    message "Error downloading grimoire..."
    message "Grimoire tarballs are located at $CODEX_TARBALL_URL"
    message "Grimoire rsync modules are located at $CODEX_RSYNC_URL"
}

#---------------------------------------------------------------------
## Unpackage a grimoire tarball and make sure it worked
#---------------------------------------------------------------------
function unpackage_grimoire_tarball() {
  local tarball=$1
  local grimoire=$2
  local grim_name=$3
  #untar the new grimoire update and remove zip
  bzcat $tarball| tar -x  1>/dev/null 2>&1
  #check for a successful unzip
  if ! [ -d $grimoire ]; then
    #uh oh! bad unzip.
    message "ERROR: Grimoire tarball for $grim_name not" \
            "formated correctly!."
    return 1
  fi 
  return 0
}

#---------------------------------------------------------------------
## Given an rsync url, make sure it has a / on the end of it
## so downloading works.
#---------------------------------------------------------------------
function sanitize_rsync_url() {
  # add a / to the end, if one isnt there, if the user types
  # rsync://sourcemage.org::codex/games instead of
  # rsync://sourcemage.org::codex/games/ strange things happen
  if list_find "rsync" $prefix ; then
    echo $from|sed 's!\([^/]\)$!\1/!'
  else
    echo $from
  fi
}

#---------------------------------------------------------------------
## Validate a grimoire tree using a manifest
#---------------------------------------------------------------------
function scribe_validate_tree_common() {
  local scribe_target=$1
  local grim_name=$2
  local grimoire=$3
    if ! test -d "$scribe_target"; then
      message "Downloaded a tree but directory doesn't exist! file a bug if you see this"
      return 1
    fi
    if [[ "$scribe_target" != "$grim_name" ]] ; then
      mv -f "$scribe_target" "$grim_name"
    fi

    verify_grimoire_tree "$grim_name" "$grimoire"
    local rc=$?
    if [[ $rc != 0 ]] && [[ $rc != 253 ]] ; then
      pushd $grim_dir >/dev/null
      grimoire_tree_user_query "$grim_name" || return 1
      popd >/dev/null
    fi
}


#---------------------------------------------------------------------
## scribe_add
##
## add grimoires to codex, unless they already exist
## usage:
## scribe_add grimoire [ from location] [grimoire [ from location]] ...
##
#---------------------------------------------------------------------
function scribe_add() {
  $STD_DEBUG
  #For each item listed add it
  if ! [ -d $CODEX_ROOT ] ; then
    message "Main codex directory not present. Creating $CODEX_ROOT."
    mkdir -p $CODEX_ROOT
  fi

  local grimoire from
  while [ -n "$1" ] ; do
    grimoire="$1"
    if [ "$2" == "from" ] ; then
      from=$3
      shift 3
    else
      from=""
      shift 1
    fi

    if codex_find_grimoire $grimoire >/dev/null; then
      message "${PROBLEM_COLOR}There already exists a grimoire with" \
              "the name $grimoire! Refusing to add.${DEFAULT_COLOR}"
      continue
    fi

    # derive a full grimoire name
    grimoire=$(codex_canonicalize_grimoire_name $grimoire)
    message "Adding grimoire $grim_name to $grim_dir from $from"
    scribe_add_update_worker "$grimoire" "$from" add
  done
}
  
#---------------------------------------------------------------------
## scribe_add_worker
##
## downloads a grimoire, unpacks it and adds it to the codex listing
## if location is not given the default is used
##
## @param grimoire
## @param location to download from (optional), if empty use $CODEX_URL
##
#---------------------------------------------------------------------
function scribe_add_update_worker() {
  $STD_DEBUG
  local grimoire=$1
  local from=$2
  local add_or_update=$3

  if [ -z $grimoire ] ; then
    message "Empty grimoire name! Please contact the sorcery team if" \
            "you see this"
    return 1
  fi

  if [[ $add_or_update == update ]] ; then
    if ! test -d $grimoire ; then
      message "$grimoire is not a directory! refusing to update"
      return 1
    fi
  fi

  local grim_dir=$(dirname $grimoire)
  local grim_name=$(basename $grimoire)
  pushd $grim_dir &> /dev/null

  # get the url, if one was specified sanity check it
  if  [ -n "$from" ];  then
    if ! url_is_valid "$from"; then
      #if not valid, use default
      message  "Error: $from is not a recognized url. Using default."
      from=""
    else
      grim_target=$(basename $from)
    fi
  fi

  local prefix
  if  [ -z "$from" ];  then
    #from is empty - use the default
    prefix=$(url_get_prefix $CODEX_URL)
    if list_find "rsync svn cvs smgl_tla dir" $prefix ; then
      grim_target=$grim_name
    else
      grim_target="$grim_name.tar.bz2"
    fi
    from="$CODEX_URL/$grim_target"
  fi

  local prefix=$(url_get_prefix $from)
  from=$(sanitize_rsync_url $prefix $from)


  # do some cleanup before downloading
  if [[ $add_or_update == add ]] ; then
    if test -d $grimoire ; then
      message "Found an old grimoire directory, removing it..."
      rm -rf $grimoire
    fi
  fi
  test -f $grim_target && rm -f $grim_target

  # download it
  local scribe_target scribe_type
  url_download "$grim_target" "$from" "" scribe_target scribe_type

  #check the success of the download
  if [ $? != 0 ]; then
    scribe_download_fail_error_msg
    popd &>/dev/null
    return 1
  fi

  # do result specific actions
  local rc
  if [[ "$scribe_type" == file ]] ; then

    gpg_verify_grimoire $PWD/$scribe_target $(dirname $from)
    rc=$?
    gpg_user_query $rc $(basename $from) grimoire || return 1

    [[ $add_or_update == update ]] && mv $grim_name temp_$grim_name.old

    if unpackage_grimoire_tarball "$scribe_target" "$grimoire" "$grim_name"
    then
      rm $scribe_target
      if [[ $add_or_update == update ]] && [ -d temp_$grim_name.old ]; then
        rm -rf temp_$grim_name.old
      fi
    else
      # restore from backup
      [[ $add_or_update == update ]] && mv -f temp_$grim_name.old $grim_name
      popd &>/dev/null; return 1
    fi
  elif [[ "$scribe_type" == tree ]]; then
    if ! scribe_validate_tree_common "$scribe_target" "$grim_name" "$grimoire"
    then
      popd &>/dev/null; return 1
    fi
  else
    message "Unknown download type $scribe_type, file a bug if you see this"
    return 1
  fi
  popd &>/dev/null

  #success! create a GRIMOIRE file that stores where it was downloaded from
  echo "FROM_URL=$from" > $grimoire/GRIMOIRE && 
  chmod +x $grimoire/GRIMOIRE && 

  if [[ $add_or_update == add ]] ; then
    codex_add_grimoire $grimoire 0 &&
    scribe_reindex $grimoire
    message "Grimoire \"$grimoire\" successfully added to your codex."
  else
    scribe_reindex $grimoire
    message "$grim_name Updated!"
    grimoire_history $grimoire
  fi

  return 0
}
#---------------------------------------------------------------------
## scribe_fix
##
## frontend to metadata fixing
##
## @param grimoire names, if none all grimoires
##
#---------------------------------------------------------------------
function scribe_fix() {
  local grim grimoire grimoires
  if [[ $@ ]] ; then
    grimoires=$@
  else
    grimoires=`codex_get_all_grimoires`
  fi
  for grim in $grimoires; do

    if ! grimoire=$(codex_find_grimoire $grim) ; then
      message "Grimoire $grim not found"
      return 1
    fi

    if ! [ -e $grimoire/GRIMOIRE ]; then
      message "No Metadata found for Grimoire $grim"
      if scribe_fix_metadata $grimoire; then
        message "$grimoire fixed."
      else
        message "$grimoire not fixed"
      fi
    elif query "Metadata for $grimoire found. Fix anyway?" n; then
      if scribe_fix_metadata $grimoire 1; then
        message "$grimoire fixed."
      else
        message "$grimoire not fixed"
      fi
    else
      message "$grimoire ignored"
    fi
  done
}

#---------------------------------------------------------------------
## scribe_fix_metadata
##
## fixes the metadata on a grimoire based on user input
##
## @param grimoire name
## @param force, if one fix without prompting the user
##
#---------------------------------------------------------------------
function scribe_fix_metadata() {
  local URL grimoire_dir grimoire_name

  if ! grimoire_dir=$(codex_find_grimoire $1) ; then
    message "Grimoire $1 not found, this may be a sorcery bug..."
    return 1
  fi
  grimoire_name=$(basename $1)

  if query "Repair Grimoire:$1's Metadata?" n || [ "$2" == "1" ] ; then
    message "Enter url to pull grimoire from"
    read -p "[ $CODEX_URL/$grimoire_name.tar.bz2 ]: " URL
    URL=${URL:-$CODEX_URL/$grimoire_name.tar.bz2}
    message "Setting Metadata to: $URL"
    message "If this is incorrect, run \"scribe fix $1\" and correct"
    echo "FROM_URL=$URL" > $grimoire_dir/GRIMOIRE
    chmod +x $grimoire_dir/GRIMOIRE
    return 0
  fi
  return 1
}

#---------------------------------------------------------------------
## scribe_index
##
## Display installed grimoires
##
#---------------------------------------------------------------------
function scribe_index() {
  local idx grimoire
  
  echo ""
  echo "Codex Listing"
  echo "-------------"
  echo ""

  #for each grimoire
  let idx=0
  for grimoire in $(codex_get_all_grimoires); do
    echo " [$idx] : `basename "$grimoire"` : $grimoire"
    let idx+=1
  done
  echo ""
}

#---------------------------------------------------------------------
## scribe_localize
##
## Set grimoires "local" so scribe update ignores them
##
## @param Names of grimoires to localize
##
#---------------------------------------------------------------------
function scribe_localize() {
  local grimoire path
  for grimoire in $@; do
    scribe_localize_sub yes &&
    message "Made $path local"
  done
}

#---------------------------------------------------------------------
## scribe_localize_sub
##
## Adjust grimoires localization state
##
## @param Names of grimoires to (un)localize
## @param yes/no state of grimoire localization
##
#---------------------------------------------------------------------
function scribe_localize_sub() {
    path=$(codex_find_grimoire $grimoire)
    if [ -z "$path" ]; then
      message "No such grimoire: $grimoire"
      continue
    fi
    touch "$path/GRIMOIRE" &&
    modify_config "$path/GRIMOIRE" "CODEX_IS_LOCAL" "$1"
}

#---------------------------------------------------------------------
## scribe_reindex
##
## Update the spell index for grimoires
##
## @param grimoire names, if none all grimoires
##
#---------------------------------------------------------------------
function scribe_reindex() {
  local paths=""
  local grimoire grimoires grim
  if [[ "$@" ]] ; then
    grimoires="$@"
  else
    grimoires=`codex_get_all_grimoires`
  fi

  for grim in $grimoires; do
    if ! grimoire=$(codex_find_grimoire $grim) ; then
      message "Grimoire $grim not found"
      return 1
    fi
    paths="$paths $grimoire"
  done
  debug "scribe" "reindex: paths = \"$paths\""

  message -n "Reindexing spell list... "
  codex_create_cache $paths
  message "done."
}

#---------------------------------------------------------------------
## scribe_remove
##
## remove a grimoire from the codex
##
## @param grimoires to remove
##
#---------------------------------------------------------------------
function scribe_remove() {
  #remove all listed grimoires
  local grimoire grim
  for grim in $@; do
    # if the grimoire name starts with / they may have given a
    # full path, the [[ ]] actually doesnt expand /* to $(ls /)
    if ! grimoire=$(codex_find_grimoire $grim) ; then
      message "Grimoire $grim not found"
      return 1
    fi

    message "Deleting grimoire $grimoire directory"
    rm -rf $grimoire &&
    message "Removing grimoire $grimoire from codex" &&
    codex_remove_grimoire $grimoire ||
    { message "An error occured trying to remove $grimoire, I wonder why"
      return 1
    }
  done
}

#---------------------------------------------------------------------
## scribe_set
##
## Set grimoire1 before grimoire2, do this by removing grimoire1,
## then finding the position of grimoire2 and then use codex_add_grimoire
## on grimore1 with overwrite off
##
## @param grimoire1
## @param grimoire2
##
#---------------------------------------------------------------------
function scribe_set(){
  local grim1=$1 grimoire1
  local grim2=$2 grimoire2
  local idx1 idx2
  if [ $grim1 == $grim2 ] ; then
    message "$grim1 IS $grim2, can't set a grimoire to itself"
    return 1
  fi
  if ! codex_find_grimoire $grim1 grimoire1 idx1 ; then
    message "Grimoire $grim1 not found"
    return 1
  fi
  if ! codex_find_grimoire $grim2 grimoire2 idx2 ; then
    message "Grimoire $grim2 not found"
    return 1
  fi
  if [ $idx1 -eq $idx2 ] ; then
    message "This shouldn't happen, but if it does contact the sorcery team"
    return 1
  elif [ $idx1 -lt $idx2 ] ; then
    let idx2-- # grim1 is before grim2, by removing grim1, the position
               # decrease by 1
  fi
  
  message "Setting $grim1 before $grim2"
  codex_remove_grimoire $grimoire1
  codex_add_grimoire $grimoire1 $idx2
}

#---------------------------------------------------------------------
## scribe_swap
##
## switch grimoire1 and grimoire2 in the grimoire ordering
## do this by finding their positions, using the overwrite feature of
## codex_add_grimoire
##
## @param grimoire1
## @param grimoire2
##
#---------------------------------------------------------------------
function scribe_swap() {
  local grim1=$1 grimoire1
  local grim2=$2 grimoire2
  local idx1 idx2
  if ! codex_find_grimoire $grim1 grimoire1 idx1 ; then
    message "Grimoire $grim1 not found"
    return 1
  fi
  if ! codex_find_grimoire $grim2 grimoire2 idx2 ; then
    message "Grimoire $grim1 not found"
    return 1
  fi
  message -n "swapping "
  message "$grim1 with $grim2"
  codex_add_grimoire $grimoire1 $idx2 overwrite
  codex_add_grimoire $grimoire2 $idx1 overwrite
}

#---------------------------------------------------------------------
## scribe_update
##
## updates all installed grimoires, or just those passed in as params
##
## @param grimoire names, if none all grimoires
##
#---------------------------------------------------------------------
function scribe_update() {
  local grimoires grimoire grim from rc
  rc=0
  if [[ $@ ]] ; then
    while [ -n "$1" ] ; do
      grim="$1"
      if ! grimoire=$(codex_find_grimoire $grim) ; then
        message "Grimoire $grim not found"
        if [[ "$2" == "from" ]] ; then shift 3 ; else  shift 1 ; fi
        let rc++
        continue
      fi

      if ! [ -e $grimoire/GRIMOIRE ] && ! scribe_fix_metadata $grim; then
        message "$grim has invalid metadata. Not Updating"
        if [[ "$2" == "from" ]] ; then shift 3 ; else  shift 1 ; fi
        let rc++
        continue
      fi

      if codex_is_local $grimoire; then
        message "${SPELL_COLOR}$grim${MESSAGE_COLOR} is being marked" \
                "as local. Won't update.${DEFAULT_COLOR}"
        if [[ "$2" == "from" ]] ; then shift 3 ; else  shift 1 ; fi
        continue
      fi

      . $grimoire/GRIMOIRE
      # get a url if one exists
      if [ "$2" == "from" ] ; then
        from=$3
        shift 3
      else
        from=$FROM_URL
        shift 1
      fi

      message "Updating grimoire $grim from $from"
      scribe_add_update_worker "$grimoire" "$from" update ||
      let rc++
    done
  else
    #for each grimoire
    for grimoire in $(codex_get_all_grimoires); do
      grim=$(basename $grimoire)
      if ! [ -e $grimoire/GRIMOIRE ] && ! scribe_fix_metadata $grimoire; then
        #no metadata found. dont auto-update
        message "$grim has invalid metadata. Not Updating"
        continue
      fi

      if codex_is_local $grimoire; then
        message "${SPELL_COLOR}$grim${MESSAGE_COLOR} is being marked as local. Won't update.${DEFAULT_COLOR}"
        continue
      fi

      #include metadata
      . $grimoire/GRIMOIRE
      message "Updating grimoire $grim from $from"
      scribe_add_update_worker "$grimoire" "$FROM_URL" update ||
      let rc++
    done
  fi
  tablet_import_repair_files $TABLET_PATH
  return $rc
}

#---------------------------------------------------------------------
## scribe_localize
##
## Removes a grimoire's "local" state so scribe update will update them
##
## @param Names of grimoires to unlocalize
##
#---------------------------------------------------------------------
function scribe_unlocalize() {
  local grimoire path
  for grimoire in $@; do
    scribe_localize_sub $grimiore no &&
    message "Made $path unlocal"
  done
}

#---------------------------------------------------------------------
## find_function
##
## take the command line and figure out what the user wants to do
##
## @param command line args
##
#---------------------------------------------------------------------
function find_function()  {
  case  $1  in
                      a|ad|add) FUNCTION="add" ;;
                      f|fi|fix) FUNCTION="fix" ;;
           i|in|ind|inde|index) FUNCTION="index";;
          l|loc|local|localize) FUNCTION="localize";;
      rei|reind|reinde|reindex) FUNCTION="reindex";;
      rm|rem|remo|remov|remove) FUNCTION="remove" ;;
                        se|set) FUNCTION="set";;
                   sw|swa|swap) FUNCTION="swap";;
   un|unloc|unlocal|unlocalize) FUNCTION="unlocalize";;
    u|up|upd|upda|updat|update) FUNCTION="update" ;;
    *) help    ;;
  esac
}

#---------------------------------------------------------------------
## main
##
## start the ride.
#---------------------------------------------------------------------
function main() {
  #move to the tmp folder in case we create junk
  cd  /tmp

  #check out what it is we are doing
  local FUNCTION
  find_function $*
  shift 1

  #now that we have a function, do it.
  case $FUNCTION in
           add) scribe_add $@ ;;
           fix) scribe_fix $@ ;;
         index) scribe_index $@ ;;
      localize) scribe_localize $@ ;;
       reindex) scribe_reindex $@ ;;
        remove) scribe_remove $@ ;;
           set) scribe_set $@ ;;
          swap) scribe_swap $@ ;;
    unlocalize) scribe_unlocalize $@ ;;
        update) scribe_update $@ ;;
             *) help ;;
  esac
}

#---------------------------------------------------------------------
. /etc/sorcery/config

#check if root. If not, become root
if    [  "$UID"  ==  0  ] ; then
  if  [[  $NICE != "0"  ]] ; then
    renice $NICE -p $$  >/dev/null
  fi
  mk_tmp_dirs scribe
  main  $@
  rc=$?
  cleanup_tmp_dir $TMP_DIR
  exit $rc
elif  [[  $1 == -h  ]]  ||  [[  $1 == --help  ]] ; then
  help
elif [[ $1 == index ]] ; then
  scribe_index # this requires no access privaledges so special case it
else  
  echo  "Enter the root password, please."  1>&2
  PARAMS=$(consolidate_params "$@")
  su -c "$0 $PARAMS" root
fi

#---------------------------------------------------------------------
##=back
##
##=head1 LICENSE
##
## 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
##
#---------------------------------------------------------------------
