#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# Script   : md2term
# Descrição: Converte markdown para texto formatado no terminal
# Versão   : 0.0.7
# Data     : 30/01/2022
# Licença  : GNU/GPL v3.0
# -----------------------------------------------------------------------------
# Copyright (C) 2022  Blau Araujo <blau@debxp.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------------
# Usage: md2term [OPTIONS] -f ARQUIVO
# -----------------------------------------------------------------------------

# strings ---------------------------------------------------------------------

version=0.0.7
depends='pandoc'
msg_exit='\e[33;1mSaindo...\e[0m\n\n'

msg_error[1]='\e[31;1mERRO:\e[0m Dependência não encontrada: \e[31m%s\e[0m\n'
msg_error[2]='\e[31;1mERRO:\e[0m Arquivo \e[33;1m%s\e[0m não encontrado!\n'
msg_error[3]='\e[31;1mERRO:\e[0m Arquivo de tema não encontrado (\e[33;1m%s\e[0m)!\n'
msg_error[4]='\e[31;1mERRO:\e[0m A opção \e[33;1m-%s\e[0m exige um argumento!\n'
msg_error[5]='\e[31;1mERRO:\e[0m Número incorreto de argumentos!\n'

usage='
\e[1mmd2term ('$version') - Converte markdown para estilos do terminal\e[0m

\e[34;1mUSO:\e[0m md2term [OPÇÕES] -f ARQUIVO

\e[34;1mDESCRIÇÃO:\e[0m

    Converte textos em markdown para comandos de cores e estilos do
    terminal. Este script foi criado tendo em mente a estilização de
    apresentações no terminal e suporta um conjunto limitado de
    marcações básicas.

\e[34;1mOPÇÕES:\e[0m

    -b CARACTERE     Caractere marcador de listas.

    -c               Limpa o terminal antes de exibir o texto.

    -i ESPAÇOS       Indentação de listas, códigos e citações em espaços.

    -k VALOR         Manter as cerquilhas nos títulos:
                     0=Descarta todas 1=Mantém todas 2=Apenas no título 1

    -m ESPAÇOS       Recuo da margem esquerda em espaços.

    -p               Carrega em um pager (less).

    -s ARQUIVO       Esquema de cores alternativo.

    -t LINHAS        Afastamento do topo em linhas.

    -h               Exibe esta ajuda.

\e[2mCopyright (C) 2022 Blau Araujo <blau@debxp.org>

Licença GPLv3+: GNU GPL versão 3 ou posterior <https://gnu.org/licenses/gpl.html>
Este é um software livre: você é livre para alterá-lo e redistribuí-lo.
NÃO HÁ QUALQUER GARANTIA, na máxima extensão permitida em lei.

Desenvolvido por Blau Araujo e Romeu Alfa\e[0m
'

# functions -------------------------------------------------------------------

die() {
    printf "\n${msg_error[$1]}" $2
    printf "$usage\n"
    printf "$msg_exit"
    exit $1
}

dep_check() {
    local dep
    for dep in $depends; do
        command -v $dep > /dev/null || die 1 $dep
    done
}

file_check() {
    [[ -f $1 ]] && { md_file=$1; return; } || die 2 $1
}

# helpers ----------------------------------------------------------------------

get_tag() {
    : "${*%%>*}"
    echo "${_#<}"
}

get_text() {
    : "${*#<*>}"
    echo "${_%<*>}"
}

indent_spaces() {
    printf "%${default_indent}s" ' '
}

margin_spaces() {
    [[ $left_margin -gt 0 ]] && printf "%${left_margin}s" ' '
}

center_str() {
    local margin=$(( ($(tput cols) - ${#1}) / 2  ))
    local spaces=$(printf "%${margin}s" ' ')
    echo "$spaces$*"
}

# parsers ----------------------------------------------------------------------

header_parser() {
    local tag text style mark

    tag=$(get_tag "$*")
    text=$(get_text "$*")

    
    mark=$(printf -v m "%${tag#h}s" ' '; echo ${m// /\#})
    style="${!tag}"

    case $keep_title_hash in
        1) text="$mark $text";;
        2) [[ $tag = h1 ]] && text="$mark $text";;
    esac

    [[ $tag = h1 ]] || echo
    echo "$style$text$reset"
}

quote_parser() {
    local text indent style
    
    text=$(get_text "$*")
    indent=$(indent_spaces)
    style="$quote"

    text=$(inline_parser "$text")

    text=$(perl -pe 's/^<BR>//;' \
	        -pe 's/(\e\[0?m)/\1'"$style"'/g;' \
                -pe 's/<\/?p>//g;' <<< "$text")

    draw_line
    echo "$style$indent$text$reset" | \
    perl -pe 's/<br \/>//g;' -pe 's/<BR>/\n'"$indent"'/g'
    draw_line
}

bcode_parser() {
    local indent style text
    
    indent=$(indent_spaces)
    style="$block_code"

    draw_line
    perl -pe 's/<pre><code>(.*?)<\/code><\/pre>/'"$style$indent"'\1'"$reset"'/;' \
 	 -pe 's/<BR>/\n'"$indent"'/g' <<< "$*"
    # tput cuu 1
    printf '%s' $'\e[A'
    draw_line
}

list_parser() {
    local tag text indent style list_style count
    
    tag=$(get_tag "$*")
    text=$(get_text "$*")
    indent=$(indent_spaces)
    list_style="$bullet_color$list_bullet$reset"
    
    text=$(inline_parser "$text")
    
    text=$(perl -pe 's/<p>//g;' \
	        -pe 's/<li>(.*?)<\/li>/'"$indent$list_style "'\1/g;' \
                -pe 's/^<BR>//;' \
		-pe 's/<br \/><BR>/\n'"$indent  "'/g;' \
                -pe 's/<(BR|\/p)>/\n/g' <<< "$text")
    
    if [[ $tag = 'ol' ]]; then
        count=1
        text=$(while read -r; do
            echo "${REPLY//$list_style/$count.}"
            ((count++))
        done <<< "$text")
    fi

    echo "$text"
}

p_parser() {
    local text
    
    text=$(get_text "$*")
    inline_parser "$text" | perl -pe 's/<br \/>//g;' -pe 's/<BR>/\n/g;'
}

inline_parser() {
    perl -pe 's/<a.*?>(.*?)<\/a>/'$link'\1'"$reset"'/g;' \
         -pe 's/<strong>(.*?)<\/strong>/'$bold'\1'"$reset"'/g;' \
         -pe 's/<em>(.*?)<\/em>/'$italic'\1'"$reset"'/g;' \
         -pe 's/<code>(.*?)<\/code>/'$inline_code'\1'"$reset"'/g;' \
         -pe 's/<del>(.*?)<\/del>/'$strike'\1'"$reset"'/g;' \
         -pe 's/<u>(.*?)<\/u>/'$underline'\1'"$reset"'/g' <<< "$*"
}

draw_line() {
    local line
    printf -v line "%$(( $(tput cols) - (2 * $left_margin) ))s" ' '
    printf "$line_color${line// /·}$reset\n"
}

contents_cleaner() {
    local text
    local tags='<\/(h[1-6]|p|ul|ol|blockquote|code><\/pre)>(?!<\/(blockquote|li)>)'
    
    # Make text one single line...
    text=$(perl -pe 's/\n/<BR>/' <<< "$md_file")
    # Fix pandoc blockquote and add line breaks after block tags...
    text=$(perl -pe 's/(<\/p>)<BR>(<\/blockquote>)/\1\2/g;' \
		-pe 's/(<hr \/>)<BR>/\1\n/;' \
                -pe 's/'$tags'/<\/\1>\n/g;' <<< "$text")
    # Remove leading <BR>s and fix pandoc headers...
    text=$(perl -pe 's/^<BR>//;' \
                -pe 's/^(<h[1-6]).*?>/\1>/' <<< "$text")
    echo "$text"
}

contents_parser() {
    local out
    while read -r; do
        case "${REPLY%%\>*}" in
            \<h[1-6]) out+=$(header_parser "$REPLY")$'\n\n';;
               \<pre) out+=$(bcode_parser "$REPLY")$'\n\n';;
           \<ul|\<ol) out+=$(list_parser "$REPLY")$'\n\n';;
        \<blockquote) out+=$(quote_parser "$REPLY")$'\n\n';;
	       \<hr*) out+=$(draw_line)$'\n\n';;
                 \<p) out+=$(p_parser "$REPLY")$'\n\n';;
        esac
    done <<< "$(contents_cleaner)"

    [[ $clear_before_print -ne 0 ]] && clear
    [[ $top_lines -ne 0 ]] && { printf -v l "%$((top_lines - 1))s" ''; echo "${l// /$'\n'}"; }
    
    echo "$out"

}

# defaults ---------------------------------------------------------------------

left_margin=0
default_indent=2
top_lines=0
clear_before_print=0
keep_title_hash=0
list_bullet='▪'
color_scheme='/usr/share/md2term/themes/dark-theme'

# main ------------------------------------------------------------------------

[[ $# -eq 0 ]] && die 5

while getopts ':chpf:m:t:i:b:k:s:' opts; do
    case $opts in
        c) clear_before_print=1;;
        t) top_lines=$OPTARG;;
        i) default_indent=$OPTARG;;
        m) left_margin=$OPTARG;;
        b) list_bullet=$OPTARG;;
        k) keep_title_hash=$OPTARG;;
        f) input_file=$OPTARG;;
        s) color_scheme=$OPTARG;;
        p) pager='less -r';;
        ?) die 4 $OPTARG;;
        h|*) printf "$usage\n"; exit;;
    esac
done

dep_check
file_check $input_file

[[ -f $color_scheme  ]] && . $color_scheme || die 3 $color_scheme

md_file=$(pandoc -f gfm $input_file -t html)$'\n'

output=$(contents_parser | \
    perl -MHTML::Entities -pe 'decode_entities($_);' | \
    sed "s/^/$(margin_spaces)/")

[[ $pager ]] && $pager <<< "$output" || echo "$output"





