# ===========================================================================
# File: convert.tcl
# Target: latex
#                        Created: 2010-08-29 09:51:41
#              Last modification: 2013-12-18 17:09:01
# Author: Bernard Desgraupes
# e-mail: <bdesgraupes@users.sourceforge.net>
# (c) Copyright: Bernard Desgraupes 2010-2013
# All rights reserved.
# Description: Aida callbacks for target latex
# ===========================================================================

namespace eval latex {
	variable needs_graphicx 0
	variable split_file ""
	
	variable longSectNames [list "chapter" "section" "subsection" "subsubsection" "paragraph" "subparagraph" "subparagraph"]
	variable shortSectNames [list "chap" "sec" "subsec" "subsubsec" "par" "subpar" "subpar"]

	# Ensure fallback on base commands
	namespace path ::base
	
}


# Hooks
# -----

proc latex::preConvertHook {} {}
proc latex::postConvertHook {} {}

proc latex::splitHook {file} {}


# Callbacks
# ---------

##
 # ------------------------------------------------------------------------
 # 
 # "latex::anchorProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::anchorProc {label} {
	set label [string trim $label "\"' "]
	return "\\label\{$label\}"
}

##
 # ------------------------------------------------------------------------
 # 
 # "latex::commentProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::commentProc {str} {
	return "%% $str"
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::horizRuleProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::horizRuleProc {} {
	return "\\vspace{2mm}\\hrule\\vspace{2mm}"
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::imageProc" --
 # 
 # Build an includegraphics command for the image.
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::imageProc {str attr} {
	variable needs_graphicx

	set needs_graphicx 1
	set str [string trim $str "\"' "]
	set attrDict [aida::getAttr img $attr]
	set grafAttr [list]

	if {[dict exists $attrDict height]} {
		lappend grafAttr "height=[dict get $attrDict height]pt"
	} 
	if {[dict exists $attrDict width]} {
		lappend grafAttr "width=[dict get $attrDict width]pt"
	} 
	if {[dict exists $attrDict clip]} {
		if {[dict get $attrDict clip]} {
			lappend grafAttr "clip"
		} 
	} 
	set dir [latex::_getAlignment $attrDict]
	# Build the latex macro
	set caption [dict exists $attrDict alt]
	set cmd [list "\\begin\{$dir\}"]
	if {$caption} {
		lappend cmd "\\begin\{table\}\[h\]"
	} 
	if {[llength $grafAttr] > 0} {
		lappend cmd "\\includegraphics\[[join $grafAttr ","]\]\{$str\}"
	} else {
		lappend cmd "\\includegraphics\{$str\}"
	} 
	if {$caption} {
		lappend cmd "\\caption\{[dict get $attrDict alt]\}"
		lappend cmd "\\end\{table\}"
	} 
	lappend cmd "\\end\{$dir\}"

	return [join $cmd "\n"]
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::linkProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::linkProc {str url} {
	set url [string trim $url "\"' "]
	return "$str (\\verb|$url|)"
}


## 
 # ------------------------------------------------------------------------
 # 
 # "latex::listProc" --
 # 
 # Build a list.
 # 
 # The type argument is: ol, ul, dl
 # 
 # The supported list attributes should be: 
 # 		start	-> ordered list
 # 		type	-> ordered list: 1 A a I i
 # 				-> unordered list: disc square circle
 # 
 # NB: LaTeX supports only four levels of nesting. They are labelled by macros 
 # \labelitemi, \labelitemii, \labelitemiii, \labelitemiv for ul lists
 # \labelenumi, \labelenumii, \labelenumiii, \labelenumiv for ol lists
 # The counters for ol lists are:
 # \theenumi, \theenumii, \theenumiii, \theenumiv
 # 
 # In the case of an ul list, the type can also be any symbol understood by
 # Latex, for instance $\bigstar$ or $\triangledown$, etc.
 # ------------------------------------------------------------------------
 ##
proc latex::listProc {kind depth attr itemList} {
	set attrDict [aida::getAttr $kind $attr]
	
	set result [list]
	set prfx ""
	if {[catch {dict get $attrDict start} cnt]} {
		set cnt 1
	} 
	if {[catch {dict get $attrDict type} tp]} {
		set tp ""
	} 

	switch -- $kind {
		"ol" {
			set env "enumerate"
			switch -- $tp {
				"a" {set enummacro "\\alph"}
				"A" {set enummacro "\\Alph"}
				"i" {set enummacro "\\roman"}
				"I" {set enummacro "\\Roman"}
			}
		}
		"ul" {
			set env "itemize"
			switch -- $tp {
				"circle" {set prfx "$\\circ$"}
				"disc" {set prfx "$\\bullet$"}
				"square" {set prfx "$\\blacksquare$"}
				"none" {set prfx ""}
				default {set prfx $tp}
			}
		}
		"dl" {
			set env "description"
		}
	}

	lappend result "\\begin{$env}"
	if {$kind eq "ol"} {
		set dp [aida::getDepth "ol"]
		switch -- $dp {
			"1" - "2" - "3" {set sufx [string repeat "i" $dp]}
			"4" {set sufx "iv"}
			default {
				set sufx "iv"
				aida::verbose 1 "Latex supports only four levels of nested '$kind' lists"
				aida::verbose 1 "    setting theenum to $sufx"
			}
		}
		if {[info exists enummacro]} {
			lappend result "\\renewcommand\{\\theenum${sufx}\}\{${enummacro}\{enum${sufx}\}\}"
		} 
		if {$cnt != 1} {
			lappend result "\\setcounter\{enum${sufx}\}\{[expr $cnt-1]\}"
		} 
	}
	
	foreach itm $itemList {
		if {$kind eq "dl"} {
			set line "\\item\[[lindex $itm 0]\] [lindex $itm 1]"
		} else {
			if {$prfx eq ""} {
				set line "\\item $itm"
			} else {
				set line "\\item\[$prfx\] $itm"
			} 
		} 		

		lappend result $line
	} 
	
	lappend result "\\end{$env}"
	
	return [join $result "\n"]
}


## 
 # ------------------------------------------------------------------------
 # 
 # "latex::navBarProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::navBarProc {curr prev next top} {
	return ""
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::newLineProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::newLineProc {} {
	return "\\par "
}


## 
 # ------------------------------------------------------------------------
 # 
 # "latex::postambleProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::postambleProc {} {
	set str "\n\\end{document}\n"
	return $str
}


## 
 # ------------------------------------------------------------------------
 # 
 # "latex::preambleProc" --
 # 
 # This proc looks for additional contents to insert in the header. This can
 # be done via
 #    * a user-defined proc named [latex::addHeader] 
 #    * a AddHeader parameter whose value is a string
 #    * a Preamble parameter whose value is a file
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::preambleProc {} {
	variable needs_graphicx
	
	set cls [aida::getParam DocClass]
	set opt [aida::getParam DocOptions]
	regsub -all {[ ,]+} $opt "," opt
	set result [list "\\documentclass\[$opt\]{$cls}"]
	
	# Build the \usepackage commands
	set pkgDict [dict merge [aida::getParam "Packages"] [aida::getParam "AddPackages"]]
	set result [concat $result [latex::_loadPackages $pkgDict]]

	# Ensure graphicx and makeindex are loaded
	if {$needs_graphicx} {
		if {![dict exists $pkgDict "graphicx"]} {
			lappend result "\\usepackage{graphicx}"
		} 
	} 
	if {![dict exists $pkgDict "makeidx"]} {
		lappend result "\\usepackage{makeidx}"
	} 
	lappend result "\\makeindex"
	
	set tocdp [aida::getParam TocDepth]
	lappend result "\\setcounter{tocdepth}{$tocdp}"
	set secdp [aida::getParam SectionDepth]
	lappend result "\\setcounter{secnumdepth}{$secdp}"
	
	# Finally look for preamble data
	set result [concat $result [aida::addPreamble]] 

	lappend result "\\begin{document}\n"
	
	# Build the \maketitle macro
	set result [concat $result [latex::_addTitle latex]] 
	
	return [join $result "\n"]
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::printIndexProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::printIndexProc {} {
	return "\\printindex"
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::refProc" --
 # 
 # The format used to print a reference can be customized in the header or
 # in a configuration file via the RefText parameter. It is a string ˆ la
 # printf which receives the string and the label.
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::refProc {str label {file ""}} {
	set reftxt [aida::getParam RefText]
	set label [string trim $label]
	return [format $reftxt $str $label $label]
}


## 
 # ------------------------------------------------------------------------
 # 
 # "latex::sectionProc" --
 # 
 # Build a latex section.
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::sectionProc {str level {file ""}} {
	variable aida_head
	
	set numsecs [aida::getParam NumberSections]
	if {$numsecs} {
		set star ""
	} else {
		set star "*"
	}
	set secName [latex::_sectionName $level "long"]
	set abbrev [latex::_sectionName $level "short"]
	set title [string trim $str]
	set sc [aida::setSectionMark $level $file $title]
	set label "${abbrev}-[aida::getParam SectionMark]$sc"
	
	return "\\$secName$star\{$title\}\n\\label\{$label\}"
}


## 
 # ------------------------------------------------------------------------
 # 
 # "latex::setIndexProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::setIndexProc {str {file ""}} {
	return "\\index{[string trim $str]}"
}


## 
 # ------------------------------------------------------------------------
 # 
 # "latex::styleProc" --
 # 
 # The 'style' argument can be: "i", "b", "u", or "y". The 'begin' argument
 # tells if it is an opening or a closing tag.
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::styleProc {style begin} {
	set tag [string toupper $style]
	if {$begin} {
		switch -- $style {
			"i" {
				set macro "\\textit\{"
			}
			"b" {
				set macro "\\textbf\{"
			}
			"u" {
				set macro "\\underline\{"
			}
			"y" {
				set macro "\\texttt\{"
			}
			default {
				error "unknown style tag (($style"
			}
		}
		return "$macro"
	} else {
		return "\}"
	} 
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::tableProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::tableProc {attr rowList} {
	set tbl [list]
	set ncol 0
	set attrDict [aida::getAttr table $attr]
	if {[catch {dict get $attrDict border} bord]} {
		set bord 1
	} 
	if {$bord} {lappend tbl "\\hline"} 
	
	foreach row $rowList {
		set rlen [regsub -all "\t" $row " \\& " row]
		incr rlen
		if {$ncol < $rlen} {
			set ncol $rlen
		} 
		lappend tbl "\t$row \\\\"
		if {$bord} {lappend tbl "\\hline"} 
	} 
	lappend tbl "\\end\{tabular\}"
	
	if {[catch {dict get $attrDict format} frmt]} {
		if {$bord} {
			set sep "|"
		} else {
			set sep ""
		}
		set frmt "$sep[string repeat "l$sep" $ncol]"
	} 
	
	set tbl [linsert $tbl 0 "\\begin\{tabular\}\{$frmt\}"]
	set dir [latex::_getAlignment $attrDict]
	set tbl [linsert $tbl 0 "\\begin\{$dir\}"]
	lappend tbl "\\end\{$dir\}"
	
	return [join $tbl "\n"]
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::tocProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::tocProc {} {
	if {![aida::splitting]} {
		return "\\tableofcontents"
	} else {
		variable aida_head
		variable split_file
		
		set result [list]
		set depth [aida::getParam TocDepth]
		set count 1
		if {[aida::countSectionMarks] > 0} {
			lappend result "\\begin{itemize}"
			for {set sc 0} {$sc < [aida::countSectionMarks]} {incr sc} {
				lassign [aida::getSectionMark $sc] lv fl title
				if {$lv <= $depth} {
					if {$fl ne $split_file} {
						if {$count > 1} {
							lappend result "\\end{itemize}"
						} 
						lappend result "\\item\[\] [aida::applyCharMapping $fl]"
						lappend result "\\begin{itemize}"
						set split_file $fl
					} 			
					lappend result "\\item\[[aida::newSectionNumber $lv]\] $title"
					incr count
				} 
			} 
			lappend result "\\end{itemize}" "\\end{itemize}"
		} 
		return [join $result "\n"]
	} 
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::verbProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::verbProc {str} {
	set str [aida::unwrapText $str]
	# Find an appropriate \verb delimiter
	set delims [list "|" "=" "-" "_" ":" ";" "," "@" "!" "/" "'" "\""]
	set len [llength $delims]
	for {set i 0} {$i < $len} {incr i} {
		if {![regexp "[lindex $delims $i]" $str]} {
			set symb [lindex $delims $i]
			break
		} 
	}	
	if {![info exists symb]} {
		aida::verbose 1 "can't find delimiter for \\verb macro with '$str'"
		set symb "|"
	} 
	
	return "\\verb$symb[aida::unwrapText $str]$symb"
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::verbatimProc" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::verbatimProc {str} {
	return "\\begin{verbatim}$str\\end{verbatim}"
}



# Target specific utility procs
# =============================

##
 # ------------------------------------------------------------------------
 # 
 # "latex::defaultExtension" --
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::defaultExtension {} {
	if {[catch {aida::getParam Extension latex} result]} {
		set result ".tex"
	} 
	return $result
}


##
 # ------------------------------------------------------------------------
 # 
 # "latex::_getAlignment" --
 # 
 # Provide the alignment environment name from the 'align' attribute.
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::_getAlignment {attrDict} {
	if {![dict exists $attrDict align]} {
		set dir "flushleft"
	} else {
		switch -- [dict get $attrDict align] {
			"left" {
				set dir "flushleft"
			}
			"right" {
				set dir "flushright"
			}
			"center" {
				set dir "center"
			}
		}
	} 
	return $dir
}


## 
 # ------------------------------------------------------------------------
 # 
 # "latex::_loadPackages" --
 # 
 # Return a list of \usepackage instructions built with the given dictionary
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::_loadPackages {pkgDict} {
	set result [list]
	foreach p [dict keys $pkgDict] {
		set pkgOpt [dict get $pkgDict $p]
		set cmd "\\usepackage"
		if {$pkgOpt ne ""} {
			regsub -all {[ ,]+} $pkgOpt "," pkgOpt
			append cmd "\[$pkgOpt\]"
		} 
		append cmd "\{$p\}"
		lappend result $cmd
	} 
	
	return $result
}


## 
 # ------------------------------------------------------------------------
 # 
 # "latex::_sectionName" --
 # 
 # Get the full or abbreviated section name. The abbreviated name is used
 # to create the section labels.
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::_sectionName {level {which "long"}} {
	variable longSectNames
	variable shortSectNames
	
	set cls [aida::getParam DocClass]
	if {$cls eq "book"} {
		set offset -1
	} else {
		set offset 0
	}	
	set secName [lindex [set ${which}SectNames] [expr $level + $offset]]
	
	return $secName
}

## 
 # ------------------------------------------------------------------------
 # 
 # "latex::_addTitle" --
 # 
 # Build data for a \maketitle macro. This proc is also used by the
 # hyperref target.
 # 
 # ------------------------------------------------------------------------
 ##
proc latex::_addTitle {target} {
	set result [list]

	if {![catch {aida::getParam Title $target} title] && $title ne ""} {
		lappend result "\\title{$title}"
		if {![catch {aida::getParam Author $target} author]} {
			lappend result "\\author{$author}"
		} 
		if {![catch {aida::getParam Date $target} date]} {
			lappend result "\\date{$date}"
		} 
	} 
	set mktitle [expr {[aida::getParam MakeTitle $target] == 1}]
	if {$mktitle && [llength $result] > 0} {
		lappend result "\\maketitle"
	} 
	return $result
}

