#!/bin/bash
set -e

#####################################################################
## Variables

## Source code path
SRC=

## Output path
POTDIR=

## The component *.pot files are based roughly on CRM/Foo templates/CRM/Foo.
COMPONENT_POTS="Admin Badge Batch Campaign Case Contribute Event Extension Financial Grant Mailing Member PCP Pledge Project Queue Report"

## The adhoc *.pot files are based on clear file list (but may have some special/less predictable rules).
ADHOC_POTS="common-base countries drupal-civicrm install menu provinces"

## The magic *.pot files are derived from other *.pot files.
MAGIC_POTS="common-components"

## List of chosen *.pot files
POTS=

## Header file to prepend to any *.pot
HEADER_TMPL=bin/header
HEADER=

## Flags to control which actions are performed
DO_SCAN=
DO_DIGEST=
DO_CLEANUP=
FORCE=

#####################################################################
function usage() {
  cat <<EOT
create-pot-files.sh - builds .pot files for CiviCRM.

Usage:
  ./bin/create-pot-files.sh [options] [srcdir] [destdir] [pot1...]

Examples:
  ./bin/create-pot-files.sh ~/repository/civicrm/ ~/repository/l10n/po/pot/
  ./bin/create-pot-files.sh -sd ~/repository/civicrm/ ~/repository/l10n/po/pot/ common-base Admin Contribute

Options:
  -s    Scan targets for strings (Pass #1)
  -d    Digest/dedupe scanned strings (Pass #2)
  -c    Cleanup temp files
  -a    All (scan+digest+cleanup; default)
  -f    Force (Ignore cached results from previous scan)
  -h    Help

Targets:
  $COMPONENT_POTS
  $ADHOC_POTS $MAGIC_POTS

Although you should probably not call this directly. Use build-unified-pots.sh
if you are exporting the strings to Transifex.
http://wiki.civicrm.org/confluence/display/CRMDOC/Pushing+new+strings+to+Transifex

EOT

  exit 1;
}

#####################################################################
## Assert that CLI dependencies are met
function check_deps() {
  for cmd in sponge civistrings msgcomm msguniq tempfile tr grep cut date sed ; do
    if ! which $cmd > /dev/null ; then
      echo "Missing required command: $cmd"
      echo
      case "$cmd" in
        sponge)
          echo 'This program uses the "sponge" command which you can get by installing the'
          echo '"moreutils" package under Debian/Ubuntu or by visting'
          echo 'https://joeyh.name/code/moreutils/'
          ;;
        civistrings)
          echo 'This program uses the "civistrings" command which is bundled with buildkit.'
          echo 'You can download it separately from https://github.com/civicrm/civistrings'
      esac
      exit 1
    fi
  done
}

#####################################################################
## civistrings wrapper with some default options
function _civistrings() {
  civistrings --header="$HEADER" "$@"
}

#####################################################################
## usage: HEADER=$(build_header "$HEADER_TMPL")
function build_header() {
  local tmpl="$1"
  local out="$POTDIR/.header"
  local now=`date +'%F %R%z'`

  cat "$tmpl" \
    | sed "s/NOW/$now/" \
    > "$out"

  echo "$out"
}

#####################################################################
## Build an individual POT file
## usage: build_raw_pot <name>
## example: build_raw_pot Mailing
## example: build_raw_pot install
function build_raw_pot() {
  local name="$1"
  local filepath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot

  if [ -f "$filepath" -a -z "$FORCE" ]; then
    echo "[[ Found raw strings for ${name} from previous scan. ]]"
    return
  fi

  echo "[[ Building raw strings for ${name} ]]"

  case "$name" in

    ## Adhoc targets, sorted alphabetically

    common-base)
      _civistrings -o "$filepath" \
        {CRM,templates/CRM}/{ACL,Activity,Block,common,Contact,Core} \
        {CRM,templates/CRM}/{Custom,Dashlet,Dedupe,Export,Form,Friend} \
        {CRM,templates/CRM}/{Group,Import,Logging,Note,Price,Profile} \
        {CRM,templates/CRM}/{Relationship,SMS,Standalone,Tag,UF,Utils} \
        xml/templates/civicrm_acl.tpl \
        xml/templates/civicrm_data.tpl \
        xml/templates/languages.tpl \
        xml/templates/civicrm_msg_template.tpl \
        xml/templates/message_templates/friend_* \
        xml/templates/message_templates/uf_notify_* \
        ang/crm{App,Attachment,Example,Ui,Util}{,.js}  \
        js/Common.js \
        js/crm.ajax.js \
        js/crm.backbone.js \
        js/jquery/jquery.crmeditable.js \
        js/jquery/jquery.crmProfileSelector.js \
        js/jquery/jquery.crmRevisionLink.js \
        js/jquery/jquery.dashboard.js \
        js/model/crm.designer.js \
        js/model/crm.uf.js \
        js/view/crm.designer.js \
        packages/HTML/QuickForm

      ## The CRM/Upgrade folder includes *.tpl files which, for some reason,
      ## have been omitted from past pot's. Omitting these requires more
      ## precise file selection.
      find CRM/Upgrade -name '*.php' | _civistrings -ao "$filepath" -
      _civistrings -ao "$filepath" templates/CRM/Upgrade
      ;;

    common-components)
      ## Not yet; handled in the digest phase. That why it's in MAGIC_POTS
      return
      ;;

    countries)
      cat "$HEADER" > "$filepath"
      grep ^INSERT xml/templates/civicrm_country.tpl \
        | cut -d\" -f4 \
        | while read entry; do
          echo -e "msgctxt \"country\"\nmsgid \"$entry\"\nmsgstr \"\"\n"
        done \
        >> "$filepath"
      ## Hmm, if civicrm_country.tpl used {ts}, then we could just call "civistrings --msgctxt=country"
      ;;

    drupal-civicrm)
      _civistrings -o "$filepath" \
        drupal \
        CRM/Core/Permission.php
      ;;

    install)
      _civistrings -o "$filepath" \
        install/
      ;;

    menu)
      cat "$HEADER" > "$filepath"
      grep -h '<title>' CRM/*/xml/Menu/*.xml \
        | sed 's/^.*<title>\(.*\)<\/title>.*$/\1/' \
        | while read entry; do
          echo -e "msgctxt \"menu\"\nmsgid \"$entry\"\nmsgstr \"\"\n"
        done \
        >> "$filepath"
      _civistrings --msgctxt=menu xml/templates/civicrm_navigation.tpl -ao "$filepath"
      ;;

    provinces)
      cat "$HEADER" > "$filepath"
      grep '^(' xml/templates/civicrm_state_province.tpl \
        | cut -d\" -f4 \
        | while read entry; do
          echo -e "msgctxt \"province\"\nmsgid \"$entry\"\nmsgstr \"\"\n"
        done \
        >> "$filepath"
        ## Hmm, if civicrm_country.tpl used {ts}, then we could just call "civistrings --msgctxt=country"
      ;;

    ## Standard targets, sorted alphabetically

    Campaign)
      _civistrings -o "$filepath" \
        {CRM,templates/CRM}/$name \
        xml/templates/message_templates/petition_*
      ;;

    Case)
      _civistrings -o "$filepath" \
        {CRM,templates/CRM}/$name \
        ang/crmCaseType{,.js} \
        xml/templates/message_templates/case_*
      ;;

    Contribute)
      _civistrings -o "$filepath" \
        {CRM,templates/CRM}/$name \
        xml/templates/message_templates/contribution_* \
        xml/templates/message_templates/test_*
      ;;

    Event)
      _civistrings -o "$filepath" \
        {CRM,templates/CRM}/$name \
        xml/templates/message_templates/event_* \
        xml/templates/message_templates/participant_*
      ;;

    Mailing)
      _civistrings -o "$filepath" \
        {CRM,templates/CRM}/$name \
        ang/crm{Mailing,MailingAB}{,.js}
      ;;

    Member)
      _civistrings -o "$filepath" \
        {CRM,templates/CRM}/$name \
        xml/templates/message_templates/membership_*
      ;;

    PCP)
      _civistrings -o "$filepath" \
        {CRM,templates/CRM}/$name \
        xml/templates/message_templates/pcp_*
      ;;

    Pledge)
      _civistrings -o "$filepath" \
        {CRM,templates/CRM}/$name \
        xml/templates/message_templates/pledge_*
      ;;

    *)
      if echo " $COMPONENT_POTS " | grep -q " $name " > /dev/null ; then
        _civistrings -o "$filepath" {CRM,templates/CRM}/$name
      else
        echo "unrecognized pot: $name"
      fi
      ;;

  esac

  find "$filepath" ! -empty | while read f; do
    msguniq "$filepath" | sponge "$filepath"
  done
}

#####################################################################
## usage: make_stat <name>
## example: make_stat Mailing
function make_stat() {
  local name="$1"
  local filepath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot
  grep ^msgid "$filepath" | sort -u > "$filepath.msgid"
  grep '^#:' "$filepath" | sed 's/#://' | tr ' ' '\n' | sort -u > "$filepath.files"
}

#####################################################################
## Scan .raw-*.pot for common strings and put them in common-components.pot
## usage: build_common_components
function build_common_components() {
  echo "[[ Building common-components.pot ]]"
  local paths=""
  local has_multiple=0
  for comp in $COMPONENT_POTS ; do
    local rawfile=".raw-"$(echo $comp | tr '[:upper:]' '[:lower:]').pot
    if [ -f "$rawfile" ]; then
      paths="$paths $rawfile"
      has_multiple=1
    fi
  done
  if [ $has_multiple -eq 1 ]; then
    msgcomm $paths > .raw-common-components.pot
  else
    cat $HEADER > .raw-common-components.pot
  fi
}

#####################################################################
## example: build_final_pot Mailing
## example: build_final_pot install
function build_final_pot() {
  local name="$1"
  local rawpath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot
  local finalpath="$POTDIR/"$(echo $name | tr '[:upper:]' '[:lower:]').pot
  local tmpfile=`tempfile`

  echo "[[ Building final strings for ${name} ]]"

  cp -f "$rawpath" "$finalpath"

  if echo " $COMPONENT_POTS " | grep -q " $name " > /dev/null ; then
    msgcomm "$finalpath" .raw-common-components.pot > $tmpfile
    msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath"

    msgcomm "$finalpath" .raw-common-base.pot | sponge $tmpfile
    msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath"

  elif [ "$name" == "install" ]; then
    msgcomm "$finalpath" .raw-common-base.pot | sponge $tmpfile
    msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath"
  fi

  rm -f "$tmpfile"
}

#####################################################################
## Delete temp files
function do_cleanup() {
  echo "[[ Cleanup temp files ]]"
  rm .header .raw*pot -f
}

#####################################################################
## Main

[ "$1" == "--help" ] && usage
[ "$1" == "-h" ] && usage

check_deps

FOUND_ACTION=
while getopts "asfdc" opt; do
  case $opt in
    a)
      DO_SCAN=1
      DO_DIGEST=1
      DO_CLEANUP=1
      FOUND_ACTION=1
      ;;
    s)
      DO_SCAN=1
      FOUND_ACTION=1
      ;;
    d)
      DO_DIGEST=1
      FOUND_ACTION=1
      ;;
    c)
      DO_CLEANUP=1
      FOUND_ACTION=1
      ;;
    f)
      FORCE=1
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

if [ -z "$FOUND_ACTION" ]; then
  DO_SCAN=1
  DO_DIGEST=1
  DO_CLEANUP=1
fi

shift $((OPTIND-1))

[ "$1" == "" ] && echo 'source dir missing'     && usage
test ! -e "$1" && echo 'source does not exist'  && usage
test ! -d "$1" && echo 'source not a directory' && usage

[ "$2" == "" ] && echo 'target dir missing'     && usage
test ! -e "$2" && echo 'target does not exist'  && usage
test ! -d "$2" && echo 'target not a directory' && usage

# use absolute paths so that we can chdir/pushd
SRC=$(php -r 'echo realpath($argv[1]);' "$1")
POTDIR=$(php -r 'echo realpath($argv[1]);' "$2")
HEADER=$(build_header "$HEADER_TMPL")
## TODO: substitute "NOW" in HEADER
shift 2

if [ -z "$1" ]; then
  POTS="$COMPONENT_POTS $ADHOC_POTS $MAGIC_POTS"
else
  POTS="$@"
fi

if [ -n "$DO_SCAN" ]; then
  pushd "$SRC" >> /dev/null
    for POT in $POTS ; do
      build_raw_pot "$POT"
    done
  popd >> /dev/null
fi

if [ -n "$DO_DIGEST" ]; then
  pushd "$POTDIR" >> /dev/null
    echo "POTS : $POTS"
    build_common_components
    for POT in $POTS ; do
      echo "+++++++++ $POT"
      build_final_pot "$POT"
    done
  popd >> /dev/null
fi

if [ -n "$DO_CLEANUP" ]; then
  pushd "$POTDIR" >> /dev/null
    do_cleanup
  popd >> /dev/null
fi
