#!/bin/busybox sh
#
# exit 1 is not fatal
export LANG=C LC_ALL=C GZIP=-9
df /tmp | awk '$NF=="/tmp"&&$4<4096{exit 1;}' || exit 1
pickel=/usr/local/Kobo/pickel
if [ -x $pickel ] ; then
  pictd=/mnt/onboard/screenShots
else
  pickel=: # kobo or not
  pictd="$HOME"/screenShots
fi

# prepare DONT NAP
dnp=$FKSH_HOM/tools/dontNap.sh
onEXIT () { $dnp OFF $$ ; }
if [ -f $dnp -a -x $dnp ] ; then
  trap onEXIT EXIT
  $dnp ON $$
fi

# parse args
timer=0
opts=/
pictw=''     # waiting file of the pict with name .wait added
sil=''
while : ; do
  case "$1" in
  '' | -* ) break ;; # no farther check of options of cjpeg, take care.
  */?*) pictw="$1" ;;
  [1-9][0-9] | [1-9][0-9][0-9] ) timer=$1 ;;
  [Ss]il*) sil=YES ;;
  *) opts="$opts$1/" ;;
  esac
  shift
done

case "$opts" in
*/fmt* ) fmt="${opts#*/fmt}"; fmt="${fmt%%/*}" ;;
*) fmt="${0%.*}" ;;
esac

# output format switch, jpeg, ppm, pgm, raw;
# G)rayscale, [1234])bit, L)andscape, r)everse video
case "$fmt" in
*g)   ext=jpg;    dp=8; hdpos=1; fil1=toP3i; fil2=sed_cjpeg ;;
*gL)  ext=L.jpg;  dp=8; hdpos=1; fil1=toP3L; fil2=cjpeg ;;
*gG)  ext=G.jpg;  dp=8; hdpos=1; fil1=toP2i; fil2=sed_cjpeg ;;
*gGL) ext=GL.jpg; dp=8; hdpos=1; fil1=toP2L; fil2=cjpeg ;;
*pm)   ext=ppm.gz;    dp=7; hdpos=2; fil1=toP3i; fil2=P3i2P6gz ;;
*pmL)  ext=L.ppm.gz;  dp=7; hdpos=1; fil1=toP6L; fil2=gzip ;;
*pm4)  ext=4.ppm.gz;  dp=4; hdpos=2; fil1=toP3i; fil2=P3i2P6gz ;;
*pm4L) ext=4L.ppm.gz; dp=4; hdpos=1; fil1=toP6L; fil2=gzip ;;
*pm2)  ext=2.ppm.gz;  dp=2; hdpos=2; fil1=toP3i; fil2=P3i2P6gz ;;
*pm2L) ext=2L.ppm.gz; dp=2; hdpos=1; fil1=toP6L; fil2=gzip ;;
*gm)   ext=pgm.gz;    dp=7; hdpos=2; fil1=toP2i; fil2=P2i2P5gz ;;
*gmL)  ext=L.pgm.gz;  dp=7; hdpos=1; fil1=toP5L; fil2=gzip ;;
*gm4)  ext=4.pgm.gz;  dp=4; hdpos=2; fil1=toP2i; fil2=P2i2P5gz ;;
*gm4L) ext=4L.pgm.gz; dp=4; hdpos=1; fil1=toP5L; fil2=gzip ;;
*gm2)  ext=2.pgm.gz;  dp=2; hdpos=2; fil1=toP2i; fil2=P2i2P5gz ;;
*gm2L) ext=2L.pgm.gz; dp=2; hdpos=1; fil1=toP5L; fil2=gzip ;;
*raw4)  ext=raw.gz; dp=4; hdpos=0; fil1=toRAWG; fil2=gzip ;; # G, 4bit, L
*raw4r) ext=raw.gz; dp=4; hdpos=1; fil1=toRAWG; fil2=gzip ;; # G, 4bit, L, r
*raw3)  ext=raw.gz; dp=3; hdpos=0; fil1=toRAWG; fil2=gzip ;; # G, 3bit, L
*raw3r) ext=raw.gz; dp=3; hdpos=1; fil1=toRAWG; fil2=gzip ;; # G, 3bit, L, r
*raw2)  ext=raw.gz; dp=2; hdpos=0; fil1=toRAWG; fil2=gzip ;; # G, 2bit, L
*raw2r) ext=raw.gz; dp=2; hdpos=1; fil1=toRAWG; fil2=gzip ;; # G, 2bit, L, r
*raw1)  ext=WB.raw.gz; dp=1; hdpos=0; fil1=toRAWG; fil2=gzip_rv2 ;; # BW, L, +r
*rawr)  ext=raw.gz; fmt=rawr; dp=1 ;; # raw, L, set $dp to 1, r
*) ext=raw.gz; fmt=raw; dp=1 ;;  # raw, L, set $dp to 1
esac

# set waiting file $pictw
case "$pictw" in
'')
  if [ $pickel != : -a ! -d $pictd ] ; then
    while : ; do
      grep -Eq '^ *[^ ]+ +/mnt/onboard +[^ ]+ +rw,' /proc/mounts && break
      usleep 100000  # 100ms
    done
    [ -d $pictd ] || mkdir -p $pictd
  fi
  for k in "$pictd"/[0-9][0-9][0-9].* ; do : ; done
  if [ -f "$k" ] ; then
    k=${k##*/}; k=${k#0}; k=${k#0}; k=${k%%.*}; k=$((k+1))
    [ $k -gt 999 ] && exit 1
  else
    k=1
  fi
  pictw="$(printf %s/%03d.%s.wait "$pictd" $k $ext)"
;;
*.$ext) pictw="$pictw.wait" ;;
*) pictw="$pictw.$ext.wait" ;;
esac

# help
case "$opts" in
*/[Hh]*)
  [ ${ext##*.} = jpg ] && cjpegopt=' [cjpeg_opts]'
  cat <<- EOHELP
	Usage: ${0##*/} [pathto/pict] [10-999] [cmd]$cjpegopt
	  pathto/pict : path to output file. (${pictw%.wait})
	  10-999 : shutter timer sec. ($timer)
	  cmd : help , conv , wait, showfb , fb:_params_ , silent,
	        fmt:_output_format_ (${fmt##*[!jpegm1234LGraw]}).
	EOHELP
  exit
;;
esac

# touch waiting file $pictw
touch "$pictw" || exit 4
pictw="$(readlink -f "$pictw")"  # absolute path
if [ $timer -gt 0 ] ; then
  whinker="$(PATH="$FKSH_HOM/tools:${PATH%:}:." which whinker.sh)"
  wnkCyc=$(mktemp /tmp/whinkerCyc_XXXXXX)
  [ -x "$whinker" ] && $whinker $wnkCyc
  printf '10 8 8 8\n1490 0 0 0\n' >$wnkCyc
  sleep $((timer-9))
  printf '10 8 8 8\n190 0 0 0\n' >$wnkCyc
  sleep 7
  printf '10 8 8 8\n90 0 0 0\n' >$wnkCyc
  sleep 2
  rm -f $wnkCyc
fi
cd /

# generate substitution statements and eval
# w=width;h=height;bpp=bits_per_pix;
# rB=red_bits;gB=green_bits;rG=blue_bits;
# rP=red_pos;gP=green_pos;rP=blue_pos;
# landscape orientation
w=''; rB=''
eval $(fbset | awk '
  $1=="geometry"{printf "w=%d; h=%d; bpp=%d;", $2, $3, $6;}
  $1=="rgba"{
    gsub(/[\/,]/," ");
    printf "rB=%d; gB=%d; bB=%d;", $2, $4, $6;
    printf "rP=%d; gP=%d; bP=%d;", $3, $5, $7;
}')
[ -n "$SCREENSHOT_FB_PARAMS" ] && eval "$SCREENSHOT_FB_PARAMS" 
case "$opts" in
*/fb:[!Ss]*)
  fbparm="${opts#*/fb:}"; fbparm="${fbparm%%/*}"
  eval "$fbparm"  ## no farther check
;;
esac
case "$opts" in
*/showfb*)
  echo "w=$w;h=$h;rB=$rB;rP=$rP;gB=$gB;gP=$gP;bB=$bB;bP=$bP;"
  rm -f "$pictw"
  exit 0
esac

# select options of "od" command
case "$bpp/$rB" in
/* | */ ) rm -f "$pictw"; exit 5 ;;  # not a comment of C @_@
8/*)  odcmd='od -v -A n -t u1 --width='${SSODWD:-32768} ;;
16/*) odcmd='od -v -A n -t u2 --width='${SSODWD:-32768} ;;
32/*) odcmd='od -v -A n -t u4 --width='${SSODWD:-32768} ;; # also 24bit color?
*) rm -f "$pictw"; exit 6 ;;
esac

# select parameters of "dd" command
# w*h*bpp/8 is too large, i think, it is beyond pipe capa.
bs=$((w*h*bpp/8)); cnt=1
while [ $bs -ge 65536 ] ; do
  [ $((bs%1)) -eq 1 ] && break  # hardly breaks
  bs=$((bs/2)); cnt=$((cnt*2))
done
dda="bs=$bs count=$cnt"

## shoot
wd=`mktemp -d /tmp/ss_XXXXXX`
case "$opts${0##*/}" in
*/conv*)
  gzip -d > $wd/raw
;;
*)
  if ! dd if=/dev/fb0 of=$wd/raw $dda ; then
    rm -rf $wd "$pictw"
    exit 7
  fi
  if [ -z "$sil" ] ; then
    dd if=/dev/zero $dda | $pickel showpic 1
    $pickel showpic 1 < $wd/raw
  fi
;;
esac 2>/dev/null

## set parameters for conversion
export TMPDIR=/tmp   # for the command sort. stupid solution ^o^;
xf=5                 # num of hexadecimal figures
[ $((w*h)) -ge $(printf %d 0x100000) ] && xf=6
# On decoloring, by man 1 ppmtopgm, we adopt .299 r + .587 g + .114 b
awkBEGIN="BEGIN{
  w=$w; h=$h; n=0; nn=w*h-1; h1=h-1; mx1=2^$dp; mx=mx1-1;
  rD=2^$rB; rQ=2^$rP; rR=mx1/(rD-1);
  gD=2^$gB; gQ=2^$gP; gR=mx1/(gD-1);
  bD=2^$bB; bQ=2^$bP; bR=mx1/(bD-1);
  r2G=0.299; g2G=0.587; b2G=1-r2G-g2G;
  rG=rR*r2G; gG=gR*g2G; bG=bR*b2G;
  opp=$bpp/8; r4G=rD/mx; g4G=gD/mx; b4G=bD/mx;
  fmtP3i=\"%0${xf}x %d %d %d\n\";
  fmtP2i=\"%0${xf}x %d\n\";
}"
# some "sh"s do not recog $((2**dp))
maxv=$(printf \\n | awk "$awkBEGIN{printf mx}")

#### 1st filters

## P3 means ascii ppm, P2 ascii pgm, P6 bin ppm, P5 bin pgm; L)andscape
## suffix 'i' means index for "sort"
toP3L () {
  [ $hdpos = 1 ] && printf 'P3\n%d %d\n%d\n' $w $h $maxv
  awk "$awkBEGIN"'{
    for (i=1; i<=NF; i++) {
      r=(int($i/rQ)%rD)*rR;
      g=(int($i/gQ)%gD)*gR;
      b=(int($i/bQ)%bD)*bR;
      print (r<mx)?int(r):mx, (g<mx)?int(g):mx, (b<mx)?int(b):mx;
      if (++n>nn) {exit;}
    }
  }'
}

toP3i () {
  [ $hdpos = 1 ] && printf 'idx P3 %d %d %d\n' $h $w $maxv
  awk "$awkBEGIN"'{
    for (i=1; i<=NF; i++) {
      r=(int($i/rQ)%rD)*rR;
      g=(int($i/gQ)%gD)*gR;
      b=(int($i/bQ)%bD)*bR;
      printf fmtP3i, h*(n%w)+h1-int(n/w), (r<mx)?r:mx, (g<mx)?g:mx, (b<mx)?b:mx;
      if (++n>nn) {exit;}
    }
  }' | sort
  # have not cut the index
}

toP2L () {
  [ $hdpos = 1 ] && printf 'P2\n%d %d\n%s\n' $w $h $maxv
  awk "$awkBEGIN"' {
    for (i=1; i<=NF; i++) {
      g=(int($i/rQ)%rD)*rG+(int($i/gQ)%gD)*gG+(int($i/bQ)%bD)*bG;
      print (g<mx)?int(g):mx;
      if (++n>nn) {exit;}
    }
  }'
}

toP2i () {
  [ $hdpos = 1 ] && printf 'idx P2 %d %d %d\n' $h $w $maxv
  awk "$awkBEGIN"'{
    for (i=1; i<=NF; i++) {
      g=(int($i/rQ)%rD)*rG+(int($i/gQ)%gD)*gG+(int($i/bQ)%bD)*bG;
      printf fmtP2i, h*(n%w)+h1-int(n/w), (g<mx)?g:mx;
      if (++n>nn) {exit;}
    }
  }' | sort
  # have not cut the index
}

## "tr" is BUGGY? : tr '\200-\377' '\0-\177' does not work!
trset="$(printf '\\%o\\377 \\%o\\0' $((maxv+1)) $maxv)"
# awk : printf "%c", x+0; <==> printf "%c", int(x); # x+0 : str to real
toP6L () {
  [ $hdpos = 1 ] && printf 'P6\n%d %d\n%d\n' $w $h $maxv
  awk "$awkBEGIN"'{
    for (i=1; i<=NF; i++) {
      r=(int($i/rQ)%rD)*rR;
      g=(int($i/gQ)%gD)*gR;
      b=(int($i/bQ)%bD)*bR;
      printf "%c%c%c", (r<1)?255:r, (g<1)?255:g, (b<1)?255:b;
      if (++n>nn) {exit;}
    }
  }' | tr $trset
}

toP5L () {
  [ $hdpos = 1 ] && printf 'P5\n%d %d\n%d\n' $w $h $maxv
  awk "$awkBEGIN"' {
    for (i=1; i<=NF; i++) {
      g=(int($i/rQ)%rD)*rG+(int($i/gQ)%gD)*gG+(int($i/bQ)%bD)*bG;
      printf "%c", (g<1)?255:g;
      if (++n>nn) {exit;}
    }
  }' | tr $trset
}

# raw gray, opp : octets par pix, {printf "%c", g+0;} eqv. {printf "%c", g%256;}
toRAWG () {
  awk -v rv=$hdpos "$awkBEGIN"' {
    for (i=1; i<=NF; i++) {
      g=(int($i/rQ)%rD)*rG+(int($i/gQ)%gD)*gG+(int($i/bQ)%bD)*bG;
      if (rv) {g=mx-int(g);} else {g=int(g);}
      if (g<1) {
        for (j=0; j<opp; j++) {printf "%c", 1;}
      } else if (g < mx) {
        g=int(g*r4G)*rQ+int(g*g4G)*gQ+int(g*b4G)*bQ;
        for (j=0; j<opp; j++) {printf "%c", g; g=int(g/256);}
      } else {
        for (j=0; j<opp; j++) {printf "%c", 255;}
      }
      if (++n>nn) {exit;}
    }
  }' | tr \\1 \\0
}


#### 2nd filters

sed_cjpeg () {
  sed 's/^[^ ]* //' | cjpeg "$@"
}

P3i2P6gz () {
  {
    [ $hdpos = 2 ] && printf 'P6\n%d %d\n%s\n' $h $w $maxv
    awk '{printf "%c%c%c", ($2<1)?255:$2, ($3<1)?255:$3, ($4<1)?255:$4;}' |
      tr \\377 \\0
  } | gzip
}

P2i2P5gz () {
  {
    [ $hdpos = 2 ] && printf 'P5\n%d %d\n%s\n' $h $w $maxv
    awk '{printf "%c", ($2<1)?255:$2;}' | tr \\377 \\0
  } | gzip
}

gzip_rv2 () {
  tee $wd/BW.raw | tr \\0\\377 \\377\\0 | gzip
  gzip $wd/BW.raw
}

#### convert
convert () {
  if [ $fmt = raw ] ; then
    # output to _img_.raw.gz 
    gzip < $wd/raw
  elif [ $fmt = rawr ] ; then
    # output to _img_.raw.gz rv
    rvFF=' '
    n=0
    while [ $n -le 255 ] ; do
      rvFF="$(printf '\\%o%s\\%o' $n "$rvFF" $n)"
      n=$((n+1))
    done
    tr $rvFF < $wd/raw | gzip
  else
    $odcmd $wd/raw | $fil1 | $fil2 "$@"
  fi > $wd/$ext
  rm -f $wd/raw
  [ -f $dnp -a -x $dnp ] && onEXIT
  [ $pickel = : ] || case "$pictw" in
  /mnt/onboard/*) 
    while : ; do
      grep -Eq '^ *[^ ]+ +/mnt/onboard +[^ ]+ +rw,' /proc/mounts && break
      usleep 100000  # 100ms
    done
  ;;
  /mnt/sd/*)
    while : ; do
      grep -Eq '^ *[^ ]+ +/mnt/sd +[^ ]+ +rw,' /proc/mounts && break
      usleep 100000  # 100ms
    done
  ;;
  esac
  rm -rf "${pictw%.wait}"
  mv $wd/$ext "${pictw%.wait}"
  [ -f $wd/BW.raw.gz ] && mv $wd/BW.raw.gz "${pictw%.W*}.BW.raw.gz"
  rm -rf "$wd" "$pictw"
}

## main
trap - EXIT
case "$opts" in
*/wait*) 
  [ $ext = WB.raw.gz ] && echo "output: ${pictw%.W*}.BW.raw.gz"
  echo "output: ${pictw%.wait}"
  convert "$@"
  exit
;;
*)
  convert "$@" >/dev/null 2>&1 </dev/null &
  exit
;;
esac

exit
################ TEST CODES ################
exit
################################################################
## check "awk" cannot output NUL : it is libc's printf?
cat > test-awkOutputNUL.sh << 'EOCODE1'
#!/bin/busybox sh
awk -v "a=$1" -v "b=$2" -v "c=$3" '
  BEGIN{ printf "%c%c%c%c", a, b, 0 ,c;}
  ' < /dev/null | tee /dev/tty | od -v -A n -t u1 -c
printf \\n
EOCODE1
chmod +x test-awkOutputNUL.sh
./test-awkOutputNUL.sh 65 66 67
./test-awkOutputNUL.sh 65  0 67
./test-awkOutputNUL.sh 0  66 67
## but (busybox) sh's printf can.
busybox sh -c 'printf \\0' | od -A n -t x1 -c
## btw (busybox) sh's printf %c outputs only top char.
busybox sh -c 'printf %c abc' | od -A n -t x1 -c
## any of followings may not append \n to a sh var tail,
v1="AB\n"; v2="AC$(printf \\n)"; v3="$(printf 'AD\n\n')"'';
printf '#%s#%s#%s#' "$v1" "$v2" "$v3"
## neither
eval $(printf v4=AE\'\\n\') ; printf '#%s#' "$v4" | od -A n -t x1 -c
## though the next works
printf v4=AE\'\\n\' > tmp.sh ; . ./tmp.sh ; cat tmp.sh
printf '#%s#' "$v4" | od -A n -t x1 -c
## invoke
v="ABC$(printf \\n_)"; v="${v%?}"; printf '#%s#' "$v"
## or
eval "$(printf v=ABC\'\\n\')" ; printf '#%s#' "$v"
## for putting the true stdout of command $cmd to var vv,
## the next omit last \n s. 
vv="$($cmd args)"
## use
vv="$($cmd args;printf _)"; vv="${vv%_}"
## i do not like "echo" with options -n, ...

################################################################
## check exit status on using trap EXIT on exit
cat > test-onEXIT.sh << 'EOCODE2'
#!/bin/busybox sh
if [ -z "$CHILDonEXIT" ] ; then
  export CHILDonEXIT=CHILD
  "$0" "$@"
  echo "status=$?"
  exit
fi

onEXIT () { echo onEXIT; : ;}
trap onEXIT EXIT

exit $1
EOCODE2
chmod +x test-onEXIT.sh
for i in 0 1 2 255 256 65535 65536 65537 ; do
  ./test-onEXIT.sh $i; printf \\n
done

################################################################
## BUGGY? busybox "tr"
cat > test-tr.sh << 'EOCODE3'
#!/bin/busybox sh
export LANG=C LC_ALL=C
n=0
while [ $# -ge 2 ] ; do
  n=$1; shift
  m=$1; shift
  [ 0 -le $n ] || continue
  [ $n -le $m ] || continue
  [ $m -le 255 ] || continue
  while [ $n -le $m ] ; do
    printf \\$(printf %03o $n)
    n=$((n+1))
  done
done > tr_0-255
busybox tr '\200-\377' '\0-\177' < tr_0-255 > tr_0-127
od -v -A x -t o1 --width=16 tr_0-127
exit
EOCODE3
chmod +x test-tr.sh
./test-tr.sh 0 255

################################################################
## Can "sed" output NUL
cat >| test-sed.sh << 'EOCODE4'
#!/bin/busybox sh
tmpsed=`mktemp /tmp/sed.XXXXXX`
printf 's/NUL/\\x00/;' > $tmpsed
od -v -A n -t x1 $tmpsed
printf \\n
echo ANULB | sed -f $tmpsed | od -v -A n -t x1
rm -f $tmpsed
EOCODE4
chmod +x test-sed.sh
./test-sed.sh

