#!/usr/bin/env zsh
#
# WebNomad, your slick and static website publisher
#
# Copyright (C) 2012-2017 Denis Roio <jaromil@dyne.org>
#
# This source  code is free  software; you can redistribute  it and/or
# modify it under the terms of  the GNU Public License as published by
# the Free  Software Foundation; either  version 3 of the  License, or
# (at your option) any later version.
#
# This source code 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.
# Please refer to the GNU Public License for more details.
#
# You should have received a copy of the GNU Public License along with
# this source code; if not, write to:
# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.



# full path to webnomad's system

SYS="`pwd`/webnomad"

helpers=1
source $SYS/zuper/zuper

vars+=(SYS DIR CMD dst)

vars+=(TITLE DESCRIPTION KEYWORDS TYPE IMAGE EXTENSION)
vars+=(WEB_ROOT FILES_ROOT THUMB_SIZE BOOTSTRAP destination)
vars+=(FLOWTYPE FONT_RATIO EXIF_CLEAN EXIF_COMMENT EXIF_ROTATE)
vars+=(total_fonts cssfound)
arrs+=(includecss includejs fonts)
arrs+=(custom_fonts)

# used by maildir
vars+=(mail_id mail_render mail_date mail_insert_date mail_from mail_subj)
vars+=(mdsrc mdname)

source $SYS/zuper/zuper.init


VERSION=0.6

# fill path to the source website root
DIR="`pwd`"

CMD="$1"

[[ -r config.zsh ]] || {
    error "Directory not configured for WebNomad. First use /webnomad/init"
    return 1 }

source config.zsh

# thumbnail size
THUMB_SIZE=${THUMB_SIZE:-256}

####################################

# string match case insensitive
unsetopt CASE_GLOB

notice "Rendering your website"
act "Title: $B $TITLE $r"

# destination directory to render
# also used by test to substitute pub/
destination="$DIR/pub"

# setup paths for test
{ test "$CMD" = "test" } && {
    WEB_ROOT="file://`PWD=${SYS} pwd`/test"
    destination="$DIR/test"
    notice "Test settings for indexing"
}

# base to be added to all urls, from config.zsh
baseurl="$WEB_ROOT"

act "SYS = $SYS"
act "WEB_ROOT = $WEB_ROOT"

render_file() {
	if [[ -r $1 ]]; then
		sed -e "s@\${baseurl}@${baseurl}@g" $@
	else
		warn "$1: render_file not found"
	fi
}

render_header() {
    if [[ "$IMAGE" =~ "http://" ]] ; then
        _image=$IMAGE
	elif [[ "$IMAGE" =~ "https://" ]] ; then
        _image=$IMAGE
    else
        _image=$WEB_ROOT/$IMAGE
    fi
    cat <<EOF
<!DOCTYPE html>
<html>
  <head>
    <title>$TITLE</title>
    <meta name="description" content="$DESCRIPTION" />
    <meta name="keywords" content="$KEYWORDS" />
    <meta charset="utf-8" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="Generator" content="WebNomad $VERSION - http://dyne.org/software/webnomad" />
    <meta name="MSSmartTagsPreventParsing" content="True" />
EOF

    [[ $BOOTSTRAP = 0 ]] || {
        cat <<EOF
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements
     http://html5shim.googlecode.com/svn/trunk/html5.js -->
<!--[if lt IE 9]>
    <script src="js/html5.js"></script>
    <![endif]-->

<!-- Bootstrap -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link href="${baseurl}/css/bootstrap.min.css" rel="stylesheet" />
    <link href="${baseurl}/css/bootstrap-responsive.css" rel="stylesheet" />

EOF
    }

    cat <<EOF
<!-- Open Graph for social networks -->
    <meta property="og:title" content="$TITLE" />
    <meta property="og:description" content="$DESCRIPTION" />
    <meta property="og:type" content="$TYPE" />

EOF
    [[ "$WEB_ROOT" = "" ]] || {
        cat <<EOF
    <meta property="og:url" content="$WEB_ROOT" />
EOF
    }
    cat <<EOF
    <meta property="og:image" content="$_image" />
EOF

    [[ "$TWITTER" = "" ]] || {
        cat <<EOF
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:site" content="$TWITTER">
    <meta name="twitter:title" content="$TITLE">
    <meta name="twitter:description" content="$DESCRIPTION">
    <meta name="twitter:image" content="$_image">

EOF
    }
    # include all css files found in views/css
    cssfound=`find $DIR/views/css -iname '*.css'`

    for c in ${(f)cssfound}; do
        includecss+=(${c##*/})
    done


    # include also generated css for fonts if present
    [[ -f "${destination}"/css/custom.fonts.css ]] && includecss+=(custom.fonts.css)


    # add all css needed
    for c in $includecss; do
        act "+ css: $c"
        func "<link href=\"${baseurl}/css/${c}\" rel=\"stylesheet\" />"

        cat <<EOF
    <link href="${baseurl}/css/${c}" rel="stylesheet" />

EOF
        if [[ -r "$DIR"/views/css/$c ]]; then
            cp -a $DIR/views/css/$c "${destination}"/css/
        elif [[ -r "$SYS"/css/${c} ]]; then
            cp -a "$SYS"/css/${c} "${destination}"/css/
        fi

    done

    # add the user configured header
    render_file "$DIR"/tmpl/header.html


}

render_footer() {
    cat <<EOF
</div><!--/.container-->
EOF

    # include all css files found in views/css
    jsfound=`find $DIR/views/js -type f -name '*.js'`
    for j in ${(f)jsfound}; do
        includejs+=(${j##*/})
    done

    # insert and copy all js files
    for j in $includejs; do
        act "+ js:  $j"
        cat <<EOF
<script type="text/javascript" src="${baseurl}/js/$j"></script>
EOF
        if [[ -r "$DIR"/views/js/$j ]]; then
            cp -f "$DIR"/views/js/$j "$destination"/js
        elif [[ -r "$SYS"/js/$j ]]; then
            cp -f "$SYS"/js/$j "$destination"/js
        fi
    done


    render_file "$DIR"/tmpl/footer.html

    # add any string argument to the footer
    [[ "$1" = "" ]] || { print "${@}"; print }

    # if test mode then render the test footer
    [[ "$CMD" = "test" ]] && render_test_footer

    [[ "$FLOWTYPE" = "" ]] || { # if there is flowtype.js then use it

        cat <<EOF
<script type="text/javascript">
\$('p').flowtype({
   minimum   : 500,
   maximum   : 1200,
   minFont   : 12,
   maxFont   : 40,
   fontRatio : $FONT_RATIO
});
\$('blockquote').flowtype({
   minimum   : 500,
   maximum   : 1200,
   minFont   : 12,
   maxFont   : 40,
   fontRatio : $(( $FONT_RATIO + 3 ))
});
\$('h1').flowtype({
   minimum   : 500,
   maximum   : 1200,
   minFont   : 12,
   maxFont   : 40,
   fontRatio : $(( $FONT_RATIO - 20 ))
});
\$('h2').flowtype({
   minimum   : 500,
   maximum   : 1200,
   minFont   : 12,
   maxFont   : 40,
   fontRatio : $(( $FONT_RATIO - 15 ))
});
\$('h3').flowtype({
   minimum   : 500,
   maximum   : 1200,
   minFont   : 12,
   maxFont   : 40,
   fontRatio : $(( $FONT_RATIO - 10 ))
});
\$('h4').flowtype({
   minimum   : 500,
   maximum   : 1200,
   minFont   : 12,
   maxFont   : 40,
   fontRatio : $(( $FONT_RATIO - 5 ))
});
\$('h5').flowtype({
   minimum   : 500,
   maximum   : 1200,
   minFont   : 12,
   maxFont   : 40,
   fontRatio : $(( $FONT_RATIO - 3 ))
});

</script>
EOF
    }

    cat <<EOF
  </body>
</html>
EOF
}

render_html() {

    #######################################
    ## we support the <markdown> tag inline

    # parses the html and put all stuff contained in <markdown /> tags
    # inside separate files, leaving a trace of them into the main html
    # (a line starting with tmp.md$RAND)
    tmp="tmp.$RANDOM"
    awk 'BEGIN { srand(); markdown=0; }
/<markdown>/ { markdown=1; out="tmp.md" rand(); print out; next }
/<\/markdown>/ { markdown=0; next }
{ if(markdown==1) { print $0 >out; next } else { print $0 } }
' > $tmp

    # first pass marks the markdown parts and saves them separate
    mds=(`find . -name 'tmp.md*'`)
    [[ "${#mds}" = "0" ]] || {
        # second pass substituted saved parts with rendered markdown
        act -n "${#mds} markdown fields "

        # check which markdown parser is available in PATH
        if command -v pandoc > /dev/null; then
			parser="pandoc -f markdown_github -t html5"

			# parses all html and renders each markdown in the html
			for i in $mds; do
				md=`basename $i`
				newtemp="tmp.$RANDOM"
				cat $tmp | awk '
/^'"$md"'/ { system("cat '"$md"' | '"$parser"'"); next }
{ print $0; }' > $newtemp
				rm $tmp; tmp=$newtemp
			done

		else
			error "Required program not found: pandoc"
			error "Install pandoc on this system to render markdown"
		fi
    }

    cat $tmp

    # extra js for html pages
    [[ "$FLOWTYPE" = "" ]] || {
        includejs+=(jquery.min.js)
        includejs+=(flowtype.js)
    }

    # clean up from temporary files
    rm -f tmp.*


}

strip_meta() {
	awk 'BEGIN { meta=0; }
/^<meta>/ { meta=1; next }
/^<\/meta>/ { meta=0; next }
{ if(meta==1) next;
  else        print $0; }'
}

# read metadata on each file which can change global settings
source_meta() {
	# source the <meta> contents
	source config.zsh
	ztmp
    awk 'BEGIN { meta=0; }
/^<meta>/ { meta=1; next }
/^<\/meta>/ { meta=0; next }
{ if(meta==1) { print $0 } }
' > $ztmpfile
	source $ztmpfile
}

# calculate the destination path for a file or folder to render from views
calc_dest() {
    [[ "$1" = "" ]] && {
        error "error calculating destination: cannot read $1"
        return 0 }

    if [[ "$CMD" = "test" ]]; then
        # when in test mode force .html as an extension
        # since that will make the local preview in browsers work
        ext=".html"
    else
        # optional 2nd arg: extension
        ext="$2"
        [[ "$ext" = "" ]] && { ext="$EXTENSION" }
    fi

    func "calc_dest \"$1\" \"$2\""

    dstfile=${1##*/}
    dstfile=${dstfile%.*}${ext}
    func "destination file: $dstfile"
    dstdir=${1##*views/}
    dstdir=${dstdir%/*}
    func "destination subdir: $dstdir"

    # compute destination file
    if [ "${dstfile%.*}" = "${dstdir%.*}" ]; then
        # no subdirs, root level
        dst="${destination}/${dstfile}"
    else
        dst="${destination}/${dstdir}/${dstfile}"
        mkdir -p ${destination}/${dstdir}
    fi
    func "calculated destination: $dst"
    print $dst
}

#########
# MAIN
func MAIN

{ test "$1" = "source" } && { return 0 }
{ test "$1" = "test" } && {
    act "Local test rendering inside test/"
    source $SYS/test }

# render the base skeleton dir tree
mkdir -p "$destination/css"
mkdir -p "$destination/js"
mkdir -p "$destination/img"

[[ "$BOOTSTRAP" = "" ]] || {
    #{ test -r "$destination"/css/bootstrap.css } || {
    cp "$SYS"/css/bootstrap.css            "$destination"/css/
    cp "$SYS"/img/*                        "$destination"/img/
    cp "$SYS"/css/bootstrap.min.css        "$destination"/css/
    cp "$SYS"/css/bootstrap-responsive.css "$destination"/css/
    #}
    cp "$SYS"/js/bootstrap.min.js          "$destination"/js/
    cp "$SYS"/js/html5.js                 "$destination"/js/
}





# copy to destination all subdirs in views/
act -n "publishing all $B views $r ... "
rsync -a -W "$DIR/views/" "${destination}/"
# make sure that new css styles are updated
[[ -r "$DIR/views/css/custom.css" ]] && {
    cp -f "$DIR/views/css/custom.css" "${destination}/css/" }



# prepare all fonts
source $SYS/fonts
# [[ ${#fonts} -gt 0 ]] && {
#     rsync -a -W "$DIR/fonts/" "$destination/" }

act -n "Clean up all temp files ... "
temps=(`find "$destination" -type f -name 'temp-*'`)
for t in $temps; do rm -f $t; done
[[ $QUIET = 1 ]] || print "done"

# publish all .txt files as-is
# useful for robots.txt
txts=(`find views -maxdepth 1 -type f -name '*.txt'`)
for t in $txts; do
    txt=`basename $t`
    dst=`calc_dest "$t" .txt`
    act "publishing plain text: $txt"
    cp $t ${destination}/$txt
done


publish_html() {
    src="$1"
    ext=${2:-.html}

	# read page specific <meta> settings (overrides config.zsh)
	cat ${src} | source_meta

    dst=`calc_dest "$src" $ext`

    [[ "$FLOWTYPE" = "" ]] || includecss+=(flowtype.css)

    # render html
    act "Html rendering: $B $dst $r"

    render_header > $dst

    # close <head> as nothing else is needed
    cat <<EOF >> $dst
</head> <!-- end of <head> -->

<body>
EOF

    # don't forget the navbar
    render_file "$DIR"/tmpl/navbar.html >> $dst

    [[ $BOOTSTRAP = 0 ]] || {
        cat <<EOF >> $dst
<p>&nbsp;</p>
<div class="container-fluid">
<article>
EOF
    }

    # read meta commands
    cat ${src} | strip_meta | render_html >> $dst

    [[ $BOOTSTRAP = 0 ]] || {
        cat <<EOF >> $dst
</article>
<p>&nbsp;</p>
<p>&nbsp;</p>

EOF
    }

    render_footer >> $dst

    act "done"

}



# render all maildirs
maildirs=(`find views -type f -name '*.maildir'`)
maildirs+=(`find views -type f -name '*.maildirs'`)
[[ "${#maildirs}" > 0 ]] && {
	source $SYS/maildir
	for mdsrc in $maildirs; do
		act "Maildirs list: $mdsrc"

		# base directory where to render
		mdname=${"$(basename $mdsrc)"[(ws:.:)1]}
		act "rendering in views/${mdname}.msg"
		mkdir -p views/${mdname}.msg

		cat ${mdsrc} | source_meta
		for md in ${(f)"$(cat $mdsrc | strip_meta)"}; do
			act "Maildir rendering: $md"
			maildircheck "$md" || continue
			mdb="${mdsrc}.db"

			[[ -r "$mdb" ]] || maildb_create "$mdb"
			# iterate through emails in maildir and insert new ones
			mails=`find "${md}/new" "${md}/cur" "${md}/tmp" -type f`
			act "`print ${mails} | wc -l` total emails found"
			for m in ${(f)mails}; do
				mail_id=${"$(sha256sum $m)"[(w)1]}
				u=`maildb_lookup_uid "$mdb" "$mail_id"`
				[[ "$u" = "" ]] && {
					mail_getinfo "$m"
					# $u is a new message
					act "new message found: $mail_subj"
					maildb_insert "$mdb" "$m"
				}
			done

			# iterate through database and render emails missing
			for m in ${(f)"$(maildb_list $mdb)"}; do
				mail_fill "$m"
				[[ -r pub/${mdname}.msg/${mail_id}.html ]] && {
					func "skip already rendered: $mail_id"
					continue }
				mail_file_render \
					"$mail_file" \
					> "views/${mdname}.msg/${mail_id}.html"
			done
		done

		# render indexes
		mail_index_render

		mail_atom_render
	done
}


# render all HTML views
htmls=(`find views -type f -name '*.html'`)
for src in $htmls; do
    publish_html $src
done

# render PHP files the same way
phps=(`find views -type f -name '*.php'`)
for src in $phps; do
    publish_html $src .php
done



# render all image galleries
gals=(`find views -type f -name '*.gal'`)
gals+=(`find views -type f -name '*.gallery'`)
[[ "${#gals}" > 0 ]] && {
	source $SYS/gallery

    cp "$SYS"/js/*blueimp*     "$destination"/js/
    cp "$SYS"/css/*blueimp*    "$destination"/css/
    cp "$SYS"/js/jquery.min.js "$destination"/js/

    for src in $gals; do
        cat ${src} | source_meta
        dst=`calc_dest "$src"`
        act -n "Gallery rendering: $B $dst $r ... "
        cat $src | strip_meta | render_gallery > $dst
        [[ $QUIET = 1 ]] || print "done"
    done
}

# render all directory indexes (TODO: properly fix recursion)
idxs=(`find views -type f -name '*.idx'`)
idxs+=(`find views -type f -name '*.index'`)
{ test ${#idxs} = 0 } || {
    source $SYS/index
    # loop through all .idx files
    for idx in $idxs; do
        # destination dir is named after the .idx file
        dst=`calc_dest "$idx"`
        # strip extension: an index builds a dir structure
        dst="${dst%.*}"
        notice "Directory index rendering to: $dst"
        # loop through all contents of the idx:
        # one directory to index recursively on each line
        dirs=`cat ${idx}`
        for d in ${(f)dirs}; do
            mkdir -p "${dst}"
            pushd "${dst}"
            # recursion wrap:    $1=archive   $2=diralias     $3=indextype
            recursive_index "${d[(ws: :)1]}" "${d[(ws: :)2]}" "${d[(ws: :)3]}"
            # archive: full path where the files to be indexed are found
            # diralias: index directory appended to url
            # indextype: type of index (short | long)
            popd
        done
    done
}

# Here all files are copied inside the destination
# we can run special operations on file-types and overwrite them now
# for instance edit EXIF information on images etc.

# clean up jpegs if jhead is installed
command -v jhead && {
    jpegs=`find "$destination" -iname "*.jpg"`
    [[ $EXIF_CLEAN = 1 ]] && {
        for j in ${(f)jpegs}; do
            jhead -purejpg $j
        done
    }

    [[ "$EXIF_COMMENT" != "" ]] && {
        for j in ${(f)jpegs}; do
            jhead -cl "$EXIF_COMMENT" $j
        done
    }
    [[ $EXIF_ROTATE = 1 ]] && {
        for j in ${(f)jpegs}; do
            jhead -autorot $j
        done

    }
}

[[ $QUIET = 1 ]] || print "done"

# if the whole website is a "slideshow" (set in config.zsh) then we start with
# a full screen slideshow of all uploaded photos, cycling random every time.
# galleries are supported and can be linked in menu and pages.
{ test "$WEBSITE" = "slideshow" } && {
    notice "Site is configured as slideshow"
    # generate a list of all images (removing duplicates)
    act "Indexing all images ... "
    find pub -iname '*.jpg' | sed -e 's/^pub\///g' -e 's/^.\/pub\///g' -e "s@'@@g" | sort | uniq \
        | render_gallery views/index.html > ${destination}/index
}


notice "Website refreshed."
