#!/bin/sh

alterator_api_version=1

data_dir=/etc/alterator/logs
order_file="$data_dir/.order"
cache_dir=/var/cache/alterator/logs

mkdir -p -- "$cache_dir"

. alterator-sh-functions
. shell-quote

qdata_dir=
quote_sed_regexp_variable qdata_dir "$data_dir"

log_list()
{
    # ordered list
    local ordered_list="$(sed "s/.*/$qdata_dir\/&.desktop/" "$order_file")"
    for i in $ordered_list;do
	[ ! -s "$i" ] || echo "$i"
    done

    local sorted_list=" $(echo "$ordered_list"|tr '\n' ' ') "
    local f
    find "$data_dir" -name '*.desktop'|
	while read f;do
	     [ -z "${sorted_list##* $f *}" ] || echo "$f"
	done
}

log_basename()
{
    local n=${1##*/}
    echo "${n%%.desktop}"
}

log_cat()
{
    local f="$1";shift
    local t="$(file -b "$f")"
    if [ -z "${t##bzip2 compressed data*}" ]; then
	bzcat "$f" 2>/dev/null
    elif [ -z "${t##gzip compressed data*}" ]; then
	zcat "$f" 2>/dev/null
    else
	cat "$f" 2>/dev/null
    fi
}

__make_timestamp()
{
    path="$1";shift
    find "$(dirname $path)" -type f -name "$(basename $path).*"|
	LANG=C LC_ALL=C LC_COLLATE=C sort -r|
	xargs -r stat -c '%n	%Y'
}

# log_line_dump <log_name> <log_file>
# dump line database
log_line_dump()
{
    local log_name="$1";shift
    local log_file="$1";shift

    #rotated path, has caching
    local new_timestamp_file="$cache_dir/$log_name.newtimestamp"
    local old_timestamp_file="$cache_dir/$log_name.timestamp"
    local line_file="$cache_dir/$log_name.lines"

    __make_timestamp "$log_file" >"$new_timestamp_file"

    if cmp -- "$old_timestamp_file" "$new_timestamp_file" 2>/dev/null;then
	cat "$line_file" 2>/dev/null
    else
	local total="0"
	cut -f1 "$new_timestamp_file"|
	    while read f;do
		local size="$(log_cat "$f"|wc -l)"
		total="$(($total + $size))"
		printf '%s\t%s\n' "$total" "$f"
	    done|
		tee "$line_file"
    fi
    mv -f -- "$new_timestamp_file" "$old_timestamp_file"
}

# log_line_count <log_name> <log_file>
# return total number of log lines
log_line_count()
{
    local log_name="$1";shift
    local log_file="$1";shift

    local tail_wc="$(cat "$log_file"|wc -l)"
    local head_wc="$(log_line_dump "$log_name" "$log_file"|tail -n1|cut -f1)"
    [ -n "$head_wc" ] || head_wc=0

    echo "$(($tail_wc + $head_wc))"
}

# log_line_range <log_name> <log_file> <start> <stop>
# dump line ranges inside fileset
log_line_range()
{
    local log_name="$1";shift
    local log_file="$1";shift

    local start="$1";shift
    local stop="$1";shift

    log_line_dump "$log_name" "$log_file"|
	awk -v "start=$start" \
	    -v "stop=$stop" \
	    -v "log_file=$log_file" \
	'BEGIN {
	    lo=-1; prev1=0
	}
	{
	    if (start > $1) { prev1=$1; next; }

	    lo=(lo == -1)?(start - prev1):1;
	    hi=(stop <= $1)?(stop - prev1):"$";
	    printf "%s\t%s\t%s\n",lo,hi,$2;
	    prev1=$1;
	    if (stop <= $1) exit;
	}
	END {
	    lo=(lo == -1)?(start - prev1):1;
	    hi=stop - prev1;
	    if (stop > prev1) printf "%s\t%s\t%s\n",lo,hi,log_file;
	}'
}

log_colors=("DarkRed" "FireBrick" "Red" "Crimson" "DarkOrange" "Green" "DodgerBlue" "Gray")

journald_log_format()
(
    while read -r timestamp; do
        read -r hostname
        read -r pid
        read -r message
        read -r syslog_identifier
        read -r priority
        timestamp="$(echo "$timestamp" | sed 's/.\{6\}$//' | sed 's/.*/@&/' | xargs -0 date -d)"
        # about priority see https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1
        local log_color="${log_colors[$priority]}" || "Magenta"
        echo "<font style=\"color:$log_color;\"> $timestamp $hostname $syslog_identifier[$pid]:  $message </font><br>"
    done
)

# usage: log_dump <log_name> <log_file> <start> <stop>
# print page from log file
log_dump_range()
{
    local log_name="$1";shift
    local log_file="$1";shift
    local start="$1";shift
    local stop="$1";shift

    log_line_range "$log_name" "$log_file" "$start" "$stop"|
	while read start stop path;do
	    log_cat "$path"|
		sed -n \
		    -e "$start,$stop p" \
		    -e "$stop q"
	done
}

desktop_awk()
{
    [ ! -f "$1" ] ||
	alterator-dump-desktop \
	    -v lang="$in_language" \
	    -v out="X-Alterator-URI;Name" \
	    -v def="/notfound;" \
	    $1
}

on_message()
{
	case "$in_action" in
		read)
		    [ -n "$in_name" ] || return

		    local name=$(basename "$in_name")
		    local desktopfile="$data_dir/$name.desktop"

		    #dump + total size
		    local path="$(desktop_awk "$desktopfile"|cut -f1)"
		    if [ "$path" = "/var/log/journald" ]; then
			    local JOURNALCTL=journalctl
			    "$JOURNALCTL" --system -n9998 --no-pager --output json \
			        --output-fields MESSAGE,_PID,_HOSTNAME,SYSLOG_IDENTIFIER,PRIORITY > /var/log/journald
		    fi
		    local total="$(log_line_count "$name" "$path")"
		    [ "$total" -gt 1 ] || total=1

		    #main data: page size and start shift
		    local start="${in_start:-1}"
		    local size="${in_size:-20}"
		    local old_start="$start"
		    local stop=

		    #change size
		    if [ -n "$in_new_size" -a "$in_new_size" != "$in_size" ];then
			size="$in_new_size"
		    fi

		    #next/back/first/last
		    if [ -n "$in_next" ]; then
			start="$(($start + $size))"
			[ "$start" -lt "$total" ] || start="$old_start"
		    fi

		    if [ -n "$in_back" ]; then
			start="$(($start - $size))"
			[ "$start" -ge 0 ] || start="$old_start"
		    fi

		    if [ -n "$in_first" ]; then
			start=1
		    fi

		    if [ -n "$in_last" ]; then
			start="$(( ( $total / $size ) * $size ))"
			[ "$start" -lt 1 -o "$start" -ne "$total" ] || start="$(($start - $size))"
			stop="$total"
		    else
			stop="$(($start + $size))"
		    fi
		    [ "$start" -gt 1 ] || start=1
		    [ "$stop" -le "$total" ] || stop="$total"

		    local log_text="$(log_dump_range "$name" "$path" "$start" "$stop")"
		    if [ "$path" = "/var/log/journald" ]; then
			log_text="$(echo "$log_text" | jq -r '.__REALTIME_TIMESTAMP, ._HOSTNAME, ._PID, .MESSAGE, .SYSLOG_IDENTIFIER, .PRIORITY' | journald_log_format)"
		    else
			log_text="$(echo "$log_text" | sed 's/$/<br>/')"
		    fi
		    write_string_param text "$log_text"

		    local format="`_ "Lines %s-%s of %s"`"
		    write_string_param range "$(printf "$format" "$start" "$stop" "$total")"

		    write_string_param name "$name"
		    write_string_param start "$start"
		    write_string_param size  "$size"
		    write_string_param new_size "$size"

		    rm -f -- "$cachefile"
		    ;;
		list)
		    case "$in__objects" in
			avail_size)
			    write_enum_item "10" "`_ "10 lines"`"
			    write_enum_item "20" "`_ "20 lines"`"
			    write_enum_item "50" "`_ "50 lines"`"
			    write_enum_item "100" "`_ "100 lines"`"
			    ;;
			avail_log)
			    log_list|
				while read n;do
					printf '%s\t' "$(log_basename "$n")"
				    desktop_awk "$n"
				done|
				while read desktopfile path description;do
				    [ -f "$path" ] || continue
				    write_enum_item "$desktopfile" "$description"
				done
			    ;;
		    esac
		    ;;
	esac
}

message_loop
