#!/bin/bash

[ ! -f /usr/local/bins/common ] && echo "/usr/local/bin/common ist nicht vorhanden" && exit 1

. /usr/local/bins/common

appendScans () { #{{{{
  #-------------------------------------------------------------------------------------------------------------------------------
  # Verbindet je 2 aufeinanderfolgende Seiten horizontal
  #-------------------------------------------------------------------------------------------------------------------------------
  [ -z "$appendFromPageOne" -a -z "$appendFromPageTwo" ] && die "Interner Fehler: Keine Angabe fuer Append-Steuerung"
  local startPageNum=1
  [ -n "$appendFromPageTwo" ] && startPageNum=2
  local leftPageNum=""
  local leftPage=""
  local rightPageNum=""
  local rightPage=""
  local numScans=$(ls -1 [1-9]*_scan 2>/dev/null | wc -l)
  checkReturn "appendScans: Konnte Anzahl Scans nicht ermitteln";
  case $numScans in
    [^0-9]*) die "Keine Zahl (Anzahl Scans): $numScans" ;;
  esac
  echo "Anzahl Scans: $numScans"
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  for (( leftPageNum=$startPageNum; leftPageNum <= $numScans; leftPageNum+=2 )); do
    leftPage=${leftPageNum}_scan 
    rightPageNum=$(( leftPageNum+1 ))
    rightPage=${rightPageNum}_scan
    [ ! -f "$leftPage" ] && die "appendScans: Linke Seite $leftPage existiert nicht"
    if [ -f "$rightPage" ]; then
      echo "Appending $leftPage and $rightPage"
      convert $leftPage $rightPage +append dummy
      checkReturn "appendScans: Konnte $leftPage und $rightPage nicht verbinden"
      mv dummy "$leftPage"
      checkReturn "appendScans: mv-Fehler $leftPage"
      rm $rightPage
      checkReturn "rm-Fehler $rightPage"
    else
      echo "Belasse $leftPage alleine"
    fi
  done
} #}}}}
cleanup () { #{{{{
  out "Raeume auf ..."
  local files=$(find . -maxdepth 1 -name '[1-9]*_scan*')
  if [ -n "$files" ]; then
    rm [1-9]*_scan* 
    checkReturn "cleanup: Konnte temporaere Dateien nicht loeschen"
  fi
} #}}}}
convertScans2Pdf () { #{{{{
  for page in `ls [1-9]*_scan 2>/dev/null`
  do
    echo "Konvertiere $page -> ${page}.pdf"
    convert ${page} -compress jpeg -quality ${quality}% ${page}.pdf
    checkReturn "convertScans2Pdf: Konnte $page nicht nach ${page}.pdf konvertieren"
  done
} #}}}}
concatenate () { #{{{{
  local numScans=`ls [1-9]*_scan.pdf 2>/dev/null | wc -w`
  if [ "$numScans" -eq 0 ]; then
    out "Kein Scan vorhanden => Ende"
    cleanup
    exit 0
  else
    if [ "$numScans" -eq 1 ]; then
      out "Erstelle 1 Seite"
      [ ! -f 1_scan.pdf ] && die "Interner Fehler: Dokument 1_scan.pdf existiert nicht"
      mv 1_scan.pdf "$namePdf"
      checkReturn "Konnte 1_scan.pdf nicht in $namePdf umbenennen"
    else
      out "Verkette $numScans Seiten"
      gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -SOutputFile=$namePdf `ls [1-9]*_scan.pdf | sort -n`
      checkReturn "concatenate: Konnte Einzelpdf-Dateien nicht zu $namePdf verketten"
    fi
  fi
} #}}}}
cropScans () { #{{{{
  #-------------------------------------------------------------------------------------------------------------------------------
  # Beschneidet Bilder anhand der crop-Option aus Imagemagick und behaelt Bildformat bei (bei Graustufen implizit PGM, cf. Manpage
  # von scanimage).
  #-------------------------------------------------------------------------------------------------------------------------------
  local outFile=""
  for page in `ls [1-9]*_scan 2>/dev/null`
  do
    outFile=${page}_cut
    echo "Beschneide $page -> $outFile"
    convert -crop "$cropOpts" $page $outFile
    checkReturn "cropScans: Konnte $page nicht beschneiden"
    mv $outFile $page
    checkReturn "cropScans: Konnte $outFile nicht in $page umbenennen"
  done
} #}}}}
keepScan () { #{{{{
  local answer

  [ ! -f "$namePdf" ]&& die "keepScan: $namePdf existiert nicht"

  if [ "$gui" == "yes" ]; then
    dialog --yesno "Scan behalten ?" 5 20
    if [ $? -eq 1 ]; then
      clear
      dialog --msgbox "Loesche $namePdf" 5 40
      clear
      rm -f "$namePdf"
      checkReturn "Konnte $namePdf nicht loeschen"
    else
      clear
      dialog --msgbox "=> ${scanTarget}/$namePdf" 5 120 
      clear
    fi
  else
    read -p "Scan behalten (j/n) [j] ? " -n1 answer
    printf "\n"
    if [ -z "$answer" -o "$answer" == "j" ]; then
      out "Fertig: ${scanTarget}/$namePdf"
    else
      out "Loesche $namePdf"
      rm -f "$namePdf"
      checkReturn "Konnte $namePdf nicht loeschen"
    fi
  fi
} #}}}}
makeGrid () { #{{{{
  #-----------------------------------------------------------------------------------------------
  # Zeichnet in ersten Testscan ein Gitternetz als Hilfe zur Bestimmung der crop-Parameter
  # fuer Folgeaufruf
  #-----------------------------------------------------------------------------------------------
  local inFile=1_scan
  local outFile=grid.jpg
  local fontPath="/usr/share/fonts/truetype/msttcorefonts"
  local fontPath="/usr/share/fonts/truetype/ubuntu-font-family"
  local fontSize="40"
  local font="UbuntuMono-RI.ttf"
  local fontColor=black
  #-----------------------------------------------------------------------------------------------
  [ ! -f "$inFile" ] && die "makeGrid: $infile existiert nicht"
  out "Zeichne Grid ... => $outFile"
  #-----------------------------------------------------------------------------------------------
  # Linien
  #-----------------------------------------------------------------------------------------------
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # 100er
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  befehl="convert $inFile -fill none -stroke black -strokewidth 5 "
  for i in {100..2500..100}; do
    befehl="$befehl -draw \"line ${i},0 ${i},2500\""
    befehl="$befehl -draw \"line 0,${i} 2000,${i}\""
  done
  befehl="$befehl $outFile"
  eval $befehl
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # 50er
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  befehl="mogrify -fill none -stroke red -strokewidth 3 "
  for i in {50..2500..100}; do
    befehl="$befehl -draw \"line ${i},0 ${i},2500\""
    befehl="$befehl -draw \"line 0,${i} 2000,${i}\""
  done
  befehl="$befehl $outFile"
  eval $befehl
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # 25er und 75er
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  befehl="mogrify -fill none -stroke green -strokewidth 1 "
  for i in {25..2500..50}; do
    befehl="$befehl -draw \"line ${i},0 ${i},2500\""
    befehl="$befehl -draw \"line 0,${i} 2000,${i}\""
  done
  befehl="$befehl $outFile"
  eval $befehl
  #-----------------------------------------------------------------------------------------------
  # Beschriftung
  #-----------------------------------------------------------------------------------------------
  befehl="mogrify -font ${fontPath}/$font -pointsize $fontSize -fill $fontColor "
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Horizontal
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  for i in {100..2000..100}; do
    befehl="$befehl -draw \"text ${i},40 '$i'\""
  done
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Vertikal
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  for i in {100..2500..100}; do
    befehl="$befehl -draw \"text 10,${i} '${i}'\""
  done
  befehl="$befehl $outFile"
  eval $befehl
} #}}}}
out () { #{{{{
  cat <<EOF

$line
$@
$line

EOF
} #}}}}
rotateScans () { #{{{{
  #-------------------------------------------------------------------------------------------------------------------------------
  # etwaige Rotation
  #-------------------------------------------------------------------------------------------------------------------------------
  local angle=""
  local index=""
  while read scan; do
    case "$rotation" in
      l) angle="-90" ;;
      r) angle="90" ;;
      L) index=${scan%_*}
        if ! isInteger "$index"; then
          die "Konnte index nicht aus $scan extrahieren"
        fi
        if (( $index % 2 == 0 )); then
          angle="-90"
        else
          angle="90"
        fi ;;
      R) index=${scan%_*}
        if ! isInteger "$index"; then
          die "Konnte index nicht aus $scan extrahieren"
        fi
        if (( $index % 2 == 0 )); then
          angle="90"
        else
          angle="-90"
        fi ;;
    esac
    printf "%20.20s => Winkel: %s\n" "$scan" "$angle"
    mogrify -rotate $angle -auto-orient "$scan"
    checkReturn "mogrify-Fehler $scan with $angle"
  done < <(ls [0-9]*scan)
} #}}}}
preview () { #{{{{
  # xpdf -z page $namePdf
  # (mupdf|zathura|apvlv|gv) $namePdf
  out "Starte Ansicht ..."
  case "${host,,}" in
    *rasp*) xpdf -z page "$namePdf" 2>/dev/null ;;
         *) mupdf "$namePdf" ;;
  esac
} #}}}}
scan () { #{{{{
  #----------------------------------------------------------------------------------------------------------------------------------------------------------------------
  # Lokaler Scanner oder saned-Device
  #
  # Cave: Sowohl scanimage als auch scanadf sind lokal absolut stabil !
  #       Via saned sind scanadf durchgehend und scanimage manchmal instabil !
  #       scanadf ohne Option "--batch=%d_scan", dafuer mit "-o %d_scan"
  #       Bei scanimage ist Returnwert nach Stapelende immer != 0   => folgende Returnwertpruefung daher nur bei scanadf moeglich
  #         checkReturn "scan: Scan-Fehler" 
  #
  # Alte Fassung:
  #   scanadf -l $ulx -t $uly -x $lrx -y $lry --source "$source" --resolution $resolution --mode $mode --page-width $pagewidth --page-height $pageheight -o %d_scan
  #
  # scanimage -L (ggf. Scanner schliessen und neu oeffnen)
  #   device `fujitsu:ScanSnap S510M:748' is a FUJITSU ScanSnap S510M scanner
  #----------------------------------------------------------------------------------------------------------------------------------------------------------------------
  local yResolution=" --y-resolution $resolution "
  unset yResolution # ggf. kommentieren, falls Parameter in Backend verfuegbar
  #----------------------------------------------------------------------------------------------------------------------------------------------------------------------
  out "Scanne ..."
  #----------------------------------------------------------------------------------------------------------------------------------------------------------------------
  # Veraltet: Mit Angabe der Scan-Bereichs-Geometrie
  #
  # scanimage -d "$scanner" --batch=%d_scan -l $ulx -t $uly -x $lrx -y $lry --page-width $pagewidth --page-height $pageheight --resolution $resolution $yResolution --source "$source" --mode $mode 
  #----------------------------------------------------------------------------------------------------------------------------------------------------------------------
  scanimage -d "$scanner" --batch=%d_scan --page-width $pagewidth --page-height $pageheight --resolution $resolution $yResolution --source "$source" --mode $mode 
} #}}}}
usage () { #{{{{
  local scriptName=${0##*/}
  cat <<EOF

  $(out Aufruf)

  $scriptName [-g(ui)] 
              [-a|A(ppend)] 
              [-c(rop) <width>x<height>+<xOffset>+<yOffset>] 
              [-C(olor)] [-d(uplex)] 
              [-h(elp)] [-j(ustscan] 
              [-j(ustscan)]
              [-o(utput) <name>] 
              [-p(review)] [-r(esolution)] 
              [-q <quality>] [-t(est)] 
              [-r(esolution) <zahl>]
              [-R(otate) <l|r|L|R>]
              [-u(user) <user>]

    Cave: Vor-/Nach Optionen -t und -j muss in $scanTarget manuell aufgeraeumt werden

    -a: Verbindet je 2 Bilder horizontal, beginnend mit Seite 1
    -A: Verbindet je 2 Bilder horizontal, beginnend mit Seite 2
    -c: Scans beschneiden (Option "crop" von Imagemagick)
        Beispiel: -c "600x900+330+0" (Breite x Hoehe + xOffset + yOffset)
        Cave: crop findet immer VOR einer optionalen Rotation statt
    -C: farbig scannen 
    -d: Vorder- und Rueckseite scannen 
    -g: Dialoge mit GUI (fuer Aufruf durch myscangui)
    -j: Nur Scan, ggf. Rotation und Umbenennung in *.jpg 
    -o: Name der Ausgabedatei (ohne Endung ".pdf")
        Default: <Timestamp>_Dokument.pdf
        Cave: Dateinamen darf keine Leerzeichen beinhalten !
    -p: Preview (Default: nein)
        Cave: Nur bei X-Session moeglich
    -R: Rotation
          l: alle Scans nach links
          r: alle Scans nach rechts
          L: ungerade Scans nach links, gerade nach rechts
          R: gerade Scans nach links, ungerade nach rechts
    -r: Aufloesung (100 dpi ist Default. 150 dpi sind sehr fein)
    -t: Testlauf bricht nach Scan vor Nachbearbeitung ab, behaelt also alle Original-Scans
        Zweck: Folgende manuelle Bestimmung der notwendigen Crop-Parameter
               Cave: Manuell ermittelte Clipping-Koordinaten nur "korrekt", wenn jeweils mit derselben 
                     Aufloesung (Parameter "-r <Rasterung>" scanned wird !
        Cave: Keine Rotation, da sonst evtl. wechselnde Crop-Parameter fuer alternierende Seiten
        Erzeugt auf dem ersten Test-Scan ein Hilfsgitter mit Abstand 100 und gibt dieses als grid.jpg aus
    -q: Qualitaetsstufe fuer Kompression der Einzelseiten (Default: 40% der Ausgangsdatenmenge)
    -u: Scanablage fuer (gabriele|anja|heinz) => bestimmt lediglich das Zielverzeichnis
        Default ist 'heinz'

  Der auszuwaehlende Scanner wird anhand des Hostnamens bestimmt

  $(out Beispiele)

      Regel-Archivierung
      ------------------
      beidseitig, 100 dpi, Qualitaet 40%, Graustufen, Ergebnisanzeige, Ausgabedatei automatisch 

      $scriptName -d 

      Farbige Vorlage mit kleiner Schrift, Artikel, Zeitschrift
      ---------------------------------------------------------
      beidseitig, Farbe, 150 dpi, Ergebnisanzeige, Ausgabedatei automatisch

      myscan -d -C -r 150 

      Kleinformatiges, dickes Reclam-Buch, beschnitten und 2-seitig zusammengesetzt
      ---------------------------------------------------------
      
      myscan -t -d -r 150
        erzeugt Testscans fuer Folgeaufruf convert -crop ...
        Cave: Testaufruf immer mit spaeter verwendeter Rasterung -r 100/150 !
              anschliessend 
                convert -crop "980x1400+130+0" 5_scan cropped.jpg
              oder
                erzeugte Hilfsdatei grid.jpg mit Gitterlinien aus erstem Scan verwenden,
                um Schnittparameter abzulesen
      
      Beispiel
        myscan -C -d -r 150 -A -c "1115x1575+60+0" (Grundwissen Mathematik Band 1 und 2)
        Naehrwerttabelle  Graefe und Unzer
          kann nur hochkant scanned werden, da eigentliche Tabelle Griffhilfen angebracht hat
          myscan -d -r 150 -t
          => mit ermittelten Schnittmarken
            myscan -C -d -r 150 -c "1100x1225+75+0" -R R -A
            
EOF
} #}}}}
#{{{{ main
line="======================================================================="
host="$(hostname)"
checkReturn "Konnte Host nicht bestimmen"
#-------------------------------------------------------------------------------
gui="no"
dieCalledBefore=""
pagewidth="210"
pageheight="297" # 300 hier besser als 297, sonst Blatt unten abgeschnitten
#ulx=0
#uly=0
#lrx=210
#lry=297
#-------------------------------------------------------------------------------
# Falls Scanner mit 'scanimage -L' als Normalnutzer nicht erkannt wird
#   chmod u+s /usr/bin/scanimage
# Fuer saned-Freigabe evtl. erforderliche Gruppenzugehoerigkeit
# usermod -aG plugdev (scanner) saned 
#-------------------------------------------------------------------------------
hostName=$(hostname)
case ${hostName,,} in
    # buero) scanner="fujitsu:ScanSnap S510M:748" ;;
   # rasp31) scanner="fujitsu:fi-5110EOX:6810" ;;
buero|awow*) scanner="fujitsu:fi-7160:2025047" ;;
  P500|P400) scanner="fujitsu:fi-7160:2016956" ;;
          *) die "Unbekannter Hostname des Rechners => kein Scanner" ;;
esac
#-------------------------------------------------------------------------------
append=""
appendFromPageOne=""
appendFromPageTwo=""
crop=""
cropOpts=""
justScan=""
mode="Gray"
source="ADF Front"
name=""
namePdf=""
resolution=100
rotation=""
quality=40
scanTarget=/home/heinz/daten/Tmp/Heinz/Scans
showPreview=""
testRun=""
user="heinz"
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
while getopts "aAc:Cdghjo:pr:R:q:stu:" opt; do
  case $opt in
    a) append="yes"; appendFromPageOne="yes" ;;
    A) append="yes"; appendFromPageTwo="yes" ;;
    C) mode="Color" ;;
    c) crop="yes"
       cropOpts="$OPTARG" 
       case "$cropOpts" in
         *[^0-9x+]*) die "Falsches Format crop-Optionen: $cropOpts" ;;
       esac ;;
    d) source="ADF Duplex" ;;
    g) gui="yes" ;;
    h|\?) usage; exit ;;
    j) justScan="yes" ;;
    o) name=$OPTARG; namePdf=${name}.pdf ;;
    p) showPreview="ja" ;;
    R) rotation=$OPTARG 
       [[ ! $rotation =~ ^(l|r|L|R)$ ]] && die "Ungueltiger Rotationsparameter $rotation ( l|r|L|R )" ;;
    r) resolution=$OPTARG ;;
    t) testRun="yes" ;;
    q) quality=$OPTARG ;;
    u) user=$OPTARG 
       case ${user,,} in
             anja) scanTarget=/home/heinz/daten/Tmp/Anja/Scans ;;
         gabriele) scanTarget=/home/heinz/daten/Tmp/Gabriele/Scans ;;
            heinz) scanTarget=/home/heinz/daten/Tmp/Heinz/Scans ;; # unnoetig, da Default
                *) die "Unbekannter Nutzer: $user" ;;
       esac ;;
  esac   
done
#-------------------------------------------------------------------------------
# In Zielverzeichnis wechseln
#-------------------------------------------------------------------------------
[ ! -d "$scanTarget" ] && die "Zielverzeichnis $scanTarget existiert nicht"
cd "$scanTarget"
checkReturn "Konnte nicht nach $scanTarget wechseln"
#-------------------------------------------------------------------------------
# Falls Gruppe 'scanner' vorhanden:
#   Ist Nutzer in Gruppe 'scanner' ?
#     Cave: "scanadf -L" liefert sonst einen Leerwert (keine Fehlermeldung) !
#-------------------------------------------------------------------------------
if grep -q -i scanner /etc/group ; then
  if ! grep -i -q scanner <(groups) ; then
    die "Nutzer $user ist nicht in Gruppe 'scanner' ( usermod -a -G scanner $user )"
  fi
fi
#----------------------------------------------------------------------------
# Ggf. systematischer Name der Ausgabedatei
#----------------------------------------------------------------------------
[ -z "$name" ] && name=$(date "+%Y%m%d_%H%M%S")_Dokument && namePdf="${name}.pdf"
#----------------------------------------------------------------------------
# Nicht unabsichtlich existierende Scans ueberschreiben ...
#----------------------------------------------------------------------------
if [ -f "$namePdf" ]; then
  if [ "$gui" == "yes" ]; then
    clear
    dialog --yesno "Datei $namePdf existiert bereits. Loeschen ?" 5 50
    if [ $? -eq 0 ]; then
      clear
      rm -f "$namePdf"; checkReturn "Konnte Datei $namePdf nicht loeschen"
    else
      dialog --clear
      dialog --msgbox "$namePdf wird beibehalten => Abbruch" 5 50
      clear
      exit
    fi
  else
    read -p "Datei $namePdf existiert bereits. Loeschen ? (j/n) [j] ? " -n1 answer
    printf "\n"

    if [ -z "$answer" -o "$answer" == "j" ]; then
      out "Loesche $namePdf"
      rm -f "$namePdf"
      checkReturn "Konnte $namePdf nicht loeschen"
    else
      out "$namePdf wird beibehalten => Abbruch"
      exit
    fi
  fi
fi
#----------------------------------------------------------------------------
cat <<EOF

$line
Host:        $hostName
Scanner:     $scanner
Modus:       $mode
Quelle:      $source 
Scan fuer:   $user
Ausgabe:     ${scanTarget}/$namePdf
Aufloesung:  $resolution
Qualitaet:   ${quality}% (komprimiertes Jpeg)
Crop:        ${cropOpts:-"nein"}
Rotation:    ${rotation:-"nein"}
Nur Scan:    ${justScan:-"nein"}
Testlauf:    ${testRun:-"nein"}
Append:      ${append:-"nein"}
Append_1:    ${appendFromPageOne:-"nein"}
Append_2:    ${appendFromPageTwo:-"nein"}
Vorschau:    ${showPreview:-"nein"}
$line

EOF
#----------------------------------------------------------------------------
scan
#----------------------------------------------------------------------------
# Testlauf (hierzu duerfen Bilder nicht gedreht werden)
#----------------------------------------------------------------------------
if [ -n "$testRun" ]; then
  makeGrid
  cat <<EOF

  T E S T L A U F - E N D E (CAVE: K E I N E   D R E H U N G)

EOF
  exit
#----------------------------------------------------------------------------
# Nur Scan ?
#----------------------------------------------------------------------------
elif [ -n "$justScan" ]; then
  #--------------------------------------------------------------------------
  # Erst beschneiden, dann drehen
  #--------------------------------------------------------------------------
  [ -n "$crop" ] && cropScans
  [ -n "$rotation" ] && rotateScans
  #--------------------------------------------------------------------------
  # => *.jpg
  #--------------------------------------------------------------------------
  while read scan; do
    mv "$scan" "$scan".jpg 
    checkReturn "mv-Fehler $scan"
  done < <(ls [0-9]*scan)
  cat <<EOF

  => *.jpg

EOF
  exit
#----------------------------------------------------------------------------
# Nein, Default-Lauf
#----------------------------------------------------------------------------
else
  #--------------------------------------------------------------------------
  # Erst beschneiden, dann drehen, zuletzt verketten
  #--------------------------------------------------------------------------
  [ -n "$crop" ] && cropScans
  [ -n "$rotation" ] && rotateScans
  [ -n "$append" ] && appendScans
  convertScans2Pdf
  concatenate
  cleanup
  out "Ausgabe: ${scanTarget}/$namePdf"
  #--------------------------------------------------------------------------
  # Falls X-Server laeuft und Preview gewuenscht => Vorschau
  #--------------------------------------------------------------------------
  if [ -n "$(echo $DISPLAY)" -a -n "$showPreview" ]; then
    preview
    keepScan
  fi
fi
#}}}}
