#!/bin/sh
#
# Contribution to "Sprachwahl" from Linux Magazin 2008/10
#
# Solution for script language TCL
#
#    (c) Gerhard Reithofer,
#    Techn. EDV Reithofer; 2008-09-07
#
# Usage: Usage: tclsh reorder [sortopt] [memopt] infile [outfile]
#
#  sortopt: none .. footnotes are rebuild in order as they appear in
#                   footnote list (1..n)
#           -t .... footnotes are renumbered as they appear in text,
#                   order of footnotes in list is preserved
#           -f .... footnotes are renumbered as they appear in text,
#                   footnotes list is reordered also
# memopt:   none .. file is preprocessed line by line
#           -m .... complete file is read into memory at begin, 
#                   no check for existing memory is done
# infile: ......... name of input file, not checked (exit, readable...)
# outfile: ........ name of output file, will be overwritten without 
#                   confirmation, default is stdout
#
#
# starting tclsh \
exec tclsh $0 "$@"

### switch between file mode and memory mode 
set file_mode_solution 1

### footnote begin marker
set fn(mark) "@footnote:"

### footnote counter
set fn(cntr) 0

### default - do not sort footnotes in text
set fn(sort) 0

### default - do not (re)sort footnotes
set fn(note) 0

### footnote data
array set fn_data {}

### default standard output
set ofd stdout

###
proc syntax {args} {
  if {[llength $args]} { puts [lindex $args 0] }
  puts "Usage: [file tail $::argv0] \[-t|-f\] \[-m\] infile \[outfile\]"
  puts " -t .. sort footnotes in text (default: sort footnotes at end only)"
  puts " -f .. sort footnotes in text and reorder footnotes at end"
  puts " -m .. read complete file to memory (default: line by line file I/O)"
  exit 1
}

### simple add & count note function
proc add_note id {
  global fn_data fn
  if {![info exists fn_data($id)]} {
    set fn_data($id) [incr fn(cntr)]
  }
  return $fn_data($id)
}

###
### process text and reorder footnotes 
### in text if ($reorder == 1)
###
proc process_text {fd ofd reorder} {
  global fn fn_data
  set cnt 0
  next $fd line
  while {$line ne $fn(mark)} {
    set rres [regexp -indices {\[([0-9]+)\]} $line pos num]

    if {!$rres} {
      puts $ofd $line
      next $fd line
      continue
    }

### process all footnotes in $line
###
    set eidx 0
    set rlin ""
    while {$rres} {
      set id [string range $line [lindex $num 0] [lindex $num 1]]
      set s1 [string range $line $eidx [lindex $pos 0]]
      append rlin $s1
      if {$reorder} {
        append rlin $fn_data($id)
      } else {
        set new [add_note $id]
        append rlin $new
      }
      set eidx [lindex $pos 1]
      set rres [regexp -indices -start $eidx {\[([0-9]+)\]} $line pos num]
    }

    set s2 [string range $line [lindex $pos 1] end]
    append rlin $s2

### write out corrected line and read next...
###
    puts $ofd $rlin
    next $fd line
  }
}

###
### process footnote section  
###
proc write_notes {fd ofd reorder} {
  global fn fn_data

  puts $fn(mark)

  next $fd line
  while {![enddata $fd]} {
    if {$line eq ""} {
      if {![info exists data]} {puts $ofd ""}
      next $fd line
      continue
    }
    if {[string first {[} $line]>=0} {
      set lc [string first {]} $line]
      set id [string range $line 1 [incr lc -1]]
      set line [string range $line [incr lc 2] end]
      lappend data [list $fn_data($id) $line]
    } 
    next $fd line
  }

  if {$reorder} {
    set data [lsort -index 0 -integer $data]
  }

  foreach r $data {
    set id [lindex $r 0]
    puts $ofd "\[$id\][lindex $r 1]"
  }
}

###
### read footnote section 1st, for 
### determination of footnote order
proc preproc_notes fd {
  global fn fn_data

  # read over all text lines...
  set line ""
  while {$line ne $fn(mark)} {
    next $fd line
  }

  while {![enddata $fd]} {
    next $fd line
    if {[string index $line 0] ne {[}} continue
    set lc [string first {]} $line]
    set id [string range $line 1 [incr lc -1]]
    add_note $id 
  }
  
  # rewind file
  reset $fd
}

###
### commandline handling and opening of 
### files - no further file checks are made!!!
###
if {$argc<1} { syntax }
foreach p $argv {
  if {[string index $p 0] eq "-"} {
    switch -- $p {
      "-t" {set fn(sort) 1}
      "-f" {set fn(sort) 1;set fn(note) 1}
      "-m" {set file_mode_solution 0}
      default {syntax "Invalid option: '$p'"}
    }
  } else {
    if {[info exists ifd]} {
      set ofd [open $p w+]
    } else {
      set ifd [open $p r]
    }
  }
}

##########################################################
### begin of dummy file functions
###
if {$file_mode_solution} {
  proc next {dv rv} { upvar 1 $rv line ; gets $dv line }
  proc enddata {dv} { return [eof $dv] }
  proc reset {dv}   { seek $dv 0 }
} else {
  array set lines {cnt -1 max 0 mem {}}
  proc next {dv rv} {
    global lines ; upvar 1 $rv line
    set line [lindex $lines(mem) [incr lines(cnt)]]
  }
  proc enddata {dv} { 
    global lines
    return [expr {$lines(cnt)>=$lines(max)}] 
  }
  proc reset {dv} { global lines ; set lines(cnt) -1}
}
###
### end of dummy file functions
##########################################################


if {![info exists ifd]} {
  syntax "Syntax error: no inputfile given!"
}

###
### M A I N part starts here
###
if {!$file_mode_solution} {
  set lines(mem) [split [read -nonewline $ifd] "\n"]
  set lines(max) [llength $lines(mem)]
}

if {!$fn(sort)} { 
  preproc_notes $ifd 
  process_text $ifd $ofd 1
} else {
  process_text $ifd $ofd 0
}

write_notes  $ifd $ofd $fn(note)

close $ifd
close $ofd
