#!/bin/bash
#
# preziplayer - prezi offline presentation player for linux
# by Tom Freudenberg, 4commerce technologies AG, Hamburg, Germany
# http://www.4commerce.de
#
# created: october 2011
# updated: september 2012
# github:  https://github.com/TomFreudenberg/preziplayer

# This program 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 3 of the License, or
# (at your option) any later version.

# This program 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.

# For a copy of the GNU General Public License please see
# <http://www.gnu.org/licenses/>.

# Dieses Programm ist Freie Software: Sie können es unter den Bedingungen
# der GNU General Public License, wie von der Free Software Foundation,
# Version 3 der Lizenz oder (nach Ihrer Option) jeder späteren
# veröffentlichten Version, weiterverbreiten und/oder modifizieren.

# Dieses Programm wird in der Hoffnung, dass es nützlich sein wird, aber
# OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
# Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
# Siehe die GNU General Public License für weitere Details.

# Eine Kopie der GNU General Public License erhalten Sie unter
# <http://www.gnu.org/licenses/>.



###
### Global constants
###
# port to use for tiny web server
WEB_PORT=12345
# name of index to start presentation
WEB_INDEX="presentation.html"
# name of index to start presentation
MOVIE_INDEX="presentation.swf"
# suffix to append to shrinked zip files
SHRINKED_SUF="shrinked.zip"
# suffix to append to webspace zip files
WEBSPACE_SUF="webspace.zip"
# base to installation share
PREZI_SHARE="/usr/share/preziplayer"
# name of favicon
FAVICON="favicon.ico"

###
### Global vars
###
# current selected presentation zip
PREZI_ZIP=""
# name of base directoy contained in presentation zip
PREZI_DIR=""
# random created temp directory
PREZI_TMP=""
# active PID for forked tiny web server process
WEB_PID=-1


###
### just a message
###
__info() {
  # $1 = Message
  zenity --info --title="prezi Player" --text="${1}"
}


###
### show error message and exit if code was given
###
__error() {
  # $1 = Errorcode (optional)
  # $2 = Message

  # show info
  zenity --error --title="$(eval_gettext 'prezi Player - Error')" --text="${2:-${1}}"

  # check if we need to exit (two params were given)
  if [[ "${2}" != "" ]]; then
    exit ${1}
  fi
}


###
### cleanup procedure used by trap
###
__cleanup() {
  # deactivate trap
  trap - 0 1 2 3 6 15 EXIT

  # check running weborf
  if [[ ${WEB_PID} -gt 0 ]]; then
    kill -15 ${WEB_PID}
  fi;

  # remove temporary environment
  if [[ ! -z "${PREZI_TMP}" ]] && [[ -d "${PREZI_TMP}" ]]; then
    rm -rf "${PREZI_TMP}"
  fi
}


###
### check preliminaries and setup environment
###
__initialize() {
  # check some tools we need

  # enabled internationalization GNU gettext
  export TEXTDOMAIN="$(basename ${0})"
  
  # check for GNU Text
  if [[ ! $(which gettext.sh) ]]; then
    echo -e "Oops. sorry! To run a prezzi presentation we need some internationalization tools.\nPlease install [gettext], you can use: apt-get install gettext"
    exit 1
  else
  # source gettext
    . gettext.sh >/dev/null 2>&1
  fi

  # first of all our gui output :-)
  if [[ ! $(which zenity) ]]; then
    echo -e "$(eval_gettext 'Oops, sorry! We are some End-User-Tool and for this we like to use a GUI output with zenity.\nPlease install [zenity], you can use: apt-get install zenity')"
    exit 1
  fi

  # tiny webserver
  if [[ ! $(which weborf) ]]; then
    __error 1 "$(eval_gettext 'Oops, sorry! To run a prezzi presentation we need some tiny webservice.\n\nPlease install [weborf], you can use:\n\napt-get install weborf')"
  fi

  # zip util
  if [[ ! $(which zip) ]]; then
    __error 1 "$(eval_gettext 'Oops, sorry! To run a prezzi presentation we need some tools.\n\nPlease install [zip], you can use:\n\napt-get install zip')"
  fi

  # unzip util
  if [[ ! $(which unzip) ]]; then
    __error 1 "$(eval_gettext 'Oops, sorry! To run a prezzi presentation we need some tools.\n\nPlease install [unzip], you can use:\n\napt-get install unzip')"
  fi

  # from now we we remove finally all created temp
  trap __cleanup 0 1 2 3 6 15 EXIT
}


###
### create temporary working area
###
__mktemp() { 
  # generate a random temp directory
  PREZI_TMP=$(mktemp -q -d)
  if [[ ! "$?" -eq 0 ]]; then
    __error 1 "$(eval_gettext 'Sorry, could not create TMP directory.')"
  fi;
  
  # in case that option --suffix to mktemp is not available overall
  # we do an informal renaming by ourself
  TMP_RENAME=$(mv "${PREZI_TMP}" "${PREZI_TMP}.prezi")

  # save the new tmp location after successful renaming
  if [[ "$?" -eq 0 ]]; then
    PREZI_TMP="${PREZI_TMP}.prezi"
  fi
}


###
### simple html template to create a content
###
__mkhtmlcontainer() {
  # $1 = name of movie file

  # cat content to temp as html index file
  cat <<!EOF
<html>
  <head>
    <title>prezi player - offline presentation</title>
    <link rel="shortcut icon" href="/favicon.ico" />
    <style type="text/css">
      * {margin: 0; padding: 0; }
      body {font: 13px/18px sans-serif, Verdana, Helvetica, Arial, Tahoma; width: 100%; background: #EAEAE2; }
      a, a:link, a:hover, a:active, a:visited { color: #666; outline: none; text-decoration: none; }
      p { margin: 0 0 18px; }
      img { border: none; }
      input { vertical-align: middle; }

      #wrapper {
        background: #fff;
        width: 900px;
        margin: 40px auto 20px;
        -webkit-border-radius: 15px;
        border-radius: 15px;
      }

      #content-top, #content-bottom { height: 20px; }
      #content { height: 500px; }

      #footer { margin-bottom: 30px; }
      #footer p { text-align: center; }
    </style>
  </head>
  <body>
    <div id="wrapper">
      <div id="content-top"></div>
      <div id="content">
        <div id="player">
          <object id="prezi" width="100%" height="100%" style="visibility: visible; width: 100%; height: 100%; left: auto; top: auto; margin: 0 auto;" >
            <embed id="preziobj" width="100%" height="100%" flashvars="aspect-ratio-guide" bgcolor="#ffffff" 
                   allowscriptaccess="always" allowfullscreeninteractive="true" allowfullscreen="true" 
                   type="application/x-shockwave-flash" src="${1}" />
          </object>
        </div>
      </div>
      <div id="content-bottom"></div>
    </div>

    <div id="footer">
      <p><a href="https://github.com/TomFreudenberg/preziplayer/wiki"><strong>prezi player</strong> - written by Tom Freudenberg - @github.com</a></p>
    </div>
  </body>
</html>
!EOF
}


###
### simple welcome
###
__welcome() {
  # a warm welcome ;-)
  __info "$(eval_gettext 'Welcome to prezzi offline player.\n\nPlease start with your downloaded zip file.\nPresentation will start as local mini website.\n\nHave fun.')"
  if [[ ! "$?" -eq 0 ]]; then
    exit 1
  fi
}


###
### show file selection box and try to get presentation zip filename
###
__select_prezi() {
  # try to get a filename
  PREZI_ZIP=$(zenity --title="$(eval_gettext 'Select your prezi presentation')" --file-selection --file-filter="prezi zip (*.zip)|*.zip" --file-filter="All files (*.*)|*")
  # check dialog result
  if [[ ! "$?" -eq 0 ]]; then
     __error 1 "$(eval_gettext 'Abort! No presentation was selected.')"
  fi

  if [[ ! -f "${PREZI_ZIP}" ]] || [[ ! -r "${PREZI_ZIP}" ]]; then
    __error 1 "$(printf "$(eval_gettext 'Sorry, could not access or open: [%s]')" ${PREZI_ZIP})"
  fi;
}


###
### unzip presentation zip file and prepare presentation environment
###
__build_prezi_env() {

  __mktemp

  # Unzip prezi content to TMP
  unzip -q -d "${PREZI_TMP}" "${PREZI_ZIP}"
  if [[ ! "$?" -eq 0 ]]; then
    __error 1 "$(printf "$(eval_gettext 'Sorry, could not extract content from zip: [%s]')" ${PREZI_ZIP})"
  fi;

  # get prezi container
  PREZI_DIR=$(find "${PREZI_TMP}" -mindepth 1 -maxdepth 1 -type d)
  if [[ ! -d "${PREZI_DIR}" ]]; then
    __error 1 "$(printf "$(eval_gettext 'Sorry, unknown content in zip: [%s]')" ${PREZI_ZIP})"
  fi;

  # link movie.swf to base directory and index file
  find "${PREZI_DIR}" -type f -name 'movie.swf' -exec ln -s "{}" "${PREZI_DIR}/${MOVIE_INDEX}" \;
  if [[ ! -L "${PREZI_DIR}/${MOVIE_INDEX}" ]]; then
    __error 1 "$(printf "$(eval_gettext 'Sorry, could not locate flash file movie.swf from: [%s]')" ${PREZI_ZIP})"
  fi;

  # create html container
  __mkhtmlcontainer ${MOVIE_INDEX} > "${PREZI_DIR}/${WEB_INDEX}"
  if [[ ! -f "${PREZI_DIR}/${WEB_INDEX}" ]]; then
    __error 1 "$(printf "$(eval_gettext 'Sorry, could not create [%s] index file at: [%s]')" ${WEB_INDEX} ${PREZI_DIR})"
  fi;

  # copy prezi.favicon.ico to base directory
  if [[ -d "${PREZI_SHARE}" ]]; then
    find "${PREZI_SHARE}" -type f -name '*.ico' -exec cp "{}" "${PREZI_DIR}/${FAVICON}" \;
  fi;
  if [[ ! -f "${PREZI_DIR}/${FAVICON}" ]]; then
    __error "$(printf "$(eval_gettext 'Warning, favicon is missing at: [%s]')" ${PREZI_SHARE})"
  fi;

}


###
### start tiny web server and save forked PID
###
__run_webserver() {
  # $1 = Port to use

  # Run Webserver as new process
  weborf -p ${1} -b "${PREZI_DIR}" -I "${WEB_INDEX}" >/dev/null &

  # get pid to inspect and cleanup
  WEB_PID=$!
}


###
### utility to copy and shrink presentation zip file by removing non linux stuff
###
__shrink_zip() {
  # info message
  __info "$(eval_gettext 'A copy of your presentation will now be shrinked.\nPlease click OK and be patient for a moment.')"
  if [[ ! "$?" -eq 0 ]]; then
    return
  fi

  # create a copy for this
  cp "${PREZI_ZIP}" "${PREZI_ZIP}.${SHRINKED_SUF}"

  # we will shrink the presentation by removing unused content like windows or mac player
  zip -q -d "${PREZI_ZIP}.${SHRINKED_SUF}" "*/prezi.exe" "*/prezi.app/*/Info.plist" "*/prezi.app/*/PkgInfo" "*/MacOS/*" "*/prezi.app/*/Resources/*.lproj/*" "*/prezi.app/*/Resources/*.icns" "*/prezi.app/*/Resources/*.rsrc"

  if [[ ! "$?" -eq 0 ]]; then
    __error "$(printf "$(eval_gettext 'Sorry, could not shrink zip at location:\n\n [%s]')" "${PREZI_ZIP}.${SHRINKED_SUF}")"
  else
    __info "$(printf "$(eval_gettext 'A copy of your presentation has been stored at location:\n\n [%s]')" "${PREZI_ZIP}.${SHRINKED_SUF}")"
  fi
}


###
### utility to create a webspace archive to publish prezi presentations on private webspace
###
__webspace_zip() {
  # info message
  __info "$(eval_gettext 'An archive of your presentation will now be prepared for your webspace.\nPlease click OK and be patient for a moment.')"
  if [[ ! "$?" -eq 0 ]]; then
    return
  fi

  # first we will prepare the presentation by removing unused content like windows or mac player
  find "${PREZI_TMP}" -iregex '.*/\(prezi\.exe\|prezi\.app/.*/\(Info\.plist\|PkgInfo\|Resources/.*\.\(lproj\|icns\|rsrc\)\)\|MacOS\|_CodeSignature\)' -prune -exec rm -rf "{}" \;

  if [[ ! "$?" -eq 0 ]]; then
    __error "$(printf "$(eval_gettext 'Sorry, could not prepare zip content at location:\n\n [%s]')" "${PREZI_ZIP}.${WEBSPACE_SUF}")"
  fi

  # now repack for private webspace
  cd "${PREZI_DIR}"
  rm -rf "${PREZI_ZIP}.${WEBSPACE_SUF}"
  zip -pr "${PREZI_ZIP}.${WEBSPACE_SUF}" "."

  if [[ ! "$?" -eq 0 ]]; then
    __error "$(printf "$(eval_gettext 'Sorry, could not prepare zip content at location:\n\n [%s]')" "${PREZI_ZIP}.${WEBSPACE_SUF}")"
  else
    __info "$(printf "$(eval_gettext 'A copy of your presentation has been stored at location:\n\n [%s]')" "${PREZI_ZIP}.${WEBSPACE_SUF}")"
  fi
}


###
### use default browser to show presentation (must handle Flash)
###
__open_browser() {
  # build URI
  URI="http://localhost:${WEB_PORT}"

  # info message
  __info "$(printf "$(eval_gettext 'Your browser will now be directed to your offline presentation.\nLaunching [%s].\n\nPlease click OK and look at your browser.')" ${URI})"

  # check method to launch browser
  CMD_OPTION=$(which x-www-browser)

  if [[ "$?" -eq 0 ]]; then
    # on Debian / alternative based systems call x-www-browser
    x-www-browser "${URI}"
  else
    # start defaut web browser on freedesktop systems
    xdg-open "${URI}"
  fi
}


###
### menu loop until end option is used
###
__menu() {

  while [[ 1 ]]; do
    OPTION=$(zenity --title="prezi Player" \
                    --text="$(eval_gettext 'Please select your action and choose OK.\n\nPresentation is available on localhost.\nCancel will always loop until you select an action.\n')" \
                    --list --radiolist \
                    --height="300" \
                    --column="" --column="Id" --column="$(eval_gettext 'Select action')" --print-column=2 --hide-column=2 \
                    FALSE open_browser "$(eval_gettext 'Open browser')" \
                    FALSE shrink_zip "$(eval_gettext 'Shrink zip file')" \
                    FALSE webspace_zip "$(eval_gettext 'Create zip for webspace')" \
                    FALSE end_prezi_player "$(eval_gettext 'End prezi player')" \
    ) # do not remove 

    # check that an option was selected, followed by pressing OK
    # CANCEL will left an empty option
    if [[ -n "${OPTION}" ]]; then
      case "${OPTION}" in
        "open_browser")
           __open_browser
           ;;
        "shrink_zip")
           __shrink_zip
           ;;
        "webspace_zip")
           __webspace_zip
           ;;
        "end_prezi_player")
           exit 0;
           ;;
      esac
    fi
  done
}


###
### main
###

__initialize

__welcome

__select_prezi

__build_prezi_env

__run_webserver ${WEB_PORT}

__menu

