#!/bin/sh
# the next line restarts using tclNExS \
exec /usr/local/bin/tclNExS "$0" "$@"

set ndm_dir /usr/local/lib/NExS/src/ndm

option add *Entry.background white startupFile
option add *Text.background white startupFile

#
# NExS Data Mailer (ndm) $Id: ndm,v 1.6 1999/02/20 12:38:16 tkm Exp $
#
# Copyright 1998 X Engineering Software Systems Corp.
# http://www.xess.com
#
# Permission is hereby granted to use and/or modify this program
# for any purpose so long as this copyright notice remains unaltered.
# X Engineering Software Systems Corp. makes this software freely
# available in hopes that it will be useful, but does not claim
# that it is free of errors or suitable for any purpose.
#


#
# Procedure to retrieve the contents of cell "r,c" from the spreadsheet
# recognizing that r=* means "current_row", and c=* means "current_column"
#

proc get_cell_text { current_row current_col r c } {
  global port
  if {$r == "*"} { set r $current_row }
  if {$c == "*"} { set c $current_col }
  return [ port get_string $c$r ]
  }


#
# Procedure to send the message text to /usr/lib/sendmail, or wherever
# you want it to go using "exec"
#

proc send_message { to_whom msg } {
  exec echo "$msg" | /usr/lib/sendmail $to_whom
  update_sending "sending mail to $to_whom..."
  }


#
# Procedure to process the message text, substituting the appropriate
# cell text for each instance of "<cr>" in the message, then passing
# the processed message to "send_message"
#

proc process_message { current_row current_col } {
  set msg [.t get 1.0 end]
  while {[regexp {<([a-z]+|\*)([0-9]+|\*)>} $msg match c r]} {
    set text [get_cell_text $current_row $current_col $r $c]
    regsub {\*} $match {\\*} match
    regsub $match $msg $text msg
    }
  return $msg
  }


#
# Procedure to create a "preview" window which displays the processed
# message for the first cell in the address range as it will appear
# when delivered.
#

proc preview {} {
  global range
  set rc [decode_and_loop 1]
  set r [lindex $rc 0]
  set c [lindex $rc 1]
  catch {destroy .p}
  toplevel .p
  wm title .p "Preview: Message for [port get_string $c$r]"
  wm iconname .p "Preview"
  wm protocol .p WM_DELETE_WINDOW {}
  frame .p.f
  text .p.t -yscrollcommand {.p.yscroll set}
  .p.t insert end [process_message $r $c]
  .p.t configure -state disabled
  scrollbar .p.yscroll -command {.p.t yview}
  pack .p.yscroll .p.t -in .p.f -side right -fill y
  button .p.ok -text "OK" -command {destroy .p}
  pack .p.ok .p.f -side bottom
  }


#
# Procedure to create a "help" window which displays ndm.doc
#

proc show_help {} {
  global ndm_dir
  if [ catch { set fd [ open $ndm_dir/ndm.doc r ] } ] {
    tk_messageBox -type ok -icon error \
	-message "Sorry, the ndm.doc help file is not available..."
    return
    }
  catch {destroy .h}
  toplevel .h
  wm title .h "ndm.doc"
  wm iconname .h "ndm.doc"
  wm protocol .h WM_DELETE_WINDOW {}
  frame .h.f
  text .h.t -yscrollcommand {.h.yscroll set}
  .h.t insert end [ read $fd ]
  .h.t configure -state disabled
  scrollbar .h.yscroll -command {.h.t yview}
  pack .h.yscroll .h.t -in .h.f -side right -fill y
  button .h.ok -text "OK" -command {destroy .h}
  pack .h.ok .h.f -side bottom
  }


#
# Procedure to load demo files
#

proc load_demo {} {
  global ndm_dir port
  catch {destroy .h}
  toplevel .d
  wm title .d "ndm demo"
  wm iconname .d "ndm demo"
  wm protocol .d WM_DELETE_WINDOW {}
  frame .d.f
  text .d.t -yscrollcommand {.d.yscroll set} -width 60 -height 12
  scrollbar .d.yscroll -command {.d.t yview}
  pack .d.yscroll .d.t -in .d.f -side right -fill y
  button .d.ok -text "OK" -command {destroy .d}
  pack .d.ok .d.f -side bottom
  .d.t insert end "Changing directory to $ndm_dir...\n"
  cd $ndm_dir
  .d.t insert end "Loading $ndm_dir/grades.ndm...\n"
  open_file grades.ndm
  .d.t insert end "Loading $ndm_dir/grades.xs3 into NExS...\n"
  port read_file $ndm_dir/grades.xs3
  .d.t insert end "\nClick \"Preview\" to see the email that this\n"
  .d.t insert end "message file and spreadsheet would generate.\n"
  .d.t insert end "\nClick \"Open\" and select \"probation.ndm\" for\n"
  .d.t insert end "an example that uses a conditional expression.\n"
  .d.t insert end "\nClick \"OK\" to get rid of this dialog box."
  .d.t configure -state disabled
  }


#
# Procedure to create a text window to display the progress of
# the mailing list
#

proc mk_sending_window {} {
  catch {destroy .s}
  toplevel .s
  wm title .s "NExS Data Mailer - Sending Email"
  wm iconname .s "Sending"
  wm protocol .s WM_DELETE_WINDOW {}
  frame .s.f
  text .s.t -width 40 -yscrollcommand {.s.yscroll set}
  .s.t configure -state disabled
  scrollbar .s.yscroll -command {.s.t yview}
  pack .s.yscroll .s.t -in .s.f -side right -fill y
  button .s.ok -text "OK" -command {destroy .s}
  pack .s.ok .s.f -side bottom
  tkwait visibility .s
  }

proc update_sending {t} {
  if [winfo exists .s] {
    .s.t configure -state normal
    .s.t insert end "$t\n"
    .s.t configure -state disabled
    }
  }


#
# Procedure to verify that we really want to send this e-mail out
# to all the recipients
#

proc verify_send {} {
  set answer [tk_messageBox \
		-message "Confirm send e-mail to all recipients..." \
		-type yesno -icon question]
  switch $answer {
    yes { mk_sending_window; decode_and_loop }
    no { return }
    }
  }

#
# Procedure to loop through the range specified in the "Address Range"
# box, evaluating the conditional expression and calling "send_message"
# on each iteration that the conditional expression (if there is one)
# evaluates to "true".
#
# If the function is invoked in "preview" mode, instead of sending email
# it returns the row and column of the first cell from "range" that
# corresponds to the first email message that would be sent if it were
# not in preview mode.
#

proc decode_and_loop { {preview 0} } {
  global range port condition
  set cell1 [lindex [nexs decode_range $range] 0]
  set cell2 [lindex [nexs decode_range $range] 1]
  set r1 [lindex $cell1 0]
  set c1 [lindex $cell1 1]
  set r2 [lindex $cell2 0]
  set c2 [lindex $cell2 1]
  for {set r $r1} {$r <= $r2} {incr r} {
    for {set c $c1} {$c <= $c2} {incr c} {
      set col [nexs encode_column $c 0]
      set condexpr [string trim $condition]
      while {[regexp {<([a-z]+|\*)([0-9]+|\*)>} $condexpr match xc xr]} {
	if {$xc == "*"} { set xc $col }
	if {$xr == "*"} { set xr $r }
	set text $xc$xr
	regsub {\*} $match {\\*} match
	regsub $match $condexpr $text condexpr
	}
      if {$condexpr == "" || [port evaluate_expr $condexpr]} {
	if {$preview} {
	  return "$r $col"
	  } else {
	  send_message [port get_string $col$r] [process_message $r $col]
	  }
	}
      }
    }
  }


#
# Procedure to display the start-up message
#

proc display_startup_msg {} {
  global file_text
  .t delete 1.0 end
  .t insert end "                 NExS Data Mailer 1.1\n\n"
  .t insert end "  Copyright 1998, X Engineering Software Systems Corp.\n\n"
  .t insert end "This program is unsupported software, provided as-is to\n"
  .t insert end "demonstrate some features of the tclNExS API for the NExS\n"
  .t insert end "spreadsheet from X Engineering Software Systems Corp.\n"
  .t insert end "The NDM software may be used, modified, and freely\n"
  .t insert end "for any purpose as long as the XESS Corp copyright is\n"
  .t insert end "acknowledged.\n\n"
  .t insert end "For a demonstration of the email-merge capabilities of\n"
  .t insert end "NDM, click the \"Demo\" button."
  set file_text [.t get 1.0 end]
  }

#
# Procedure to open a .ndm file
#

proc open_file { {name ""} } {
  global msg_file types range condition file_range file_condition file_text
  if { [check_for_save] == 0 } { return }
  if { $name != "" } {
      set msg_file $name
    } else {
      set msg_file [tk_getOpenFile -filetypes $types \
	-defaultextension .ndm -initialdir [pwd]]
    }
  if {$msg_file == ""} { return }
  if [catch {open $msg_file r} NDM] {
      puts stderr "Open: $NDM"
    } else {
      regexp "RANGE: *(\[^\n\]*)\nCOND: *(\[^\n\]*)\n(.*)" \
	[read -nonewline $NDM] match file_range file_condition file_text
      .t delete 1.0 end
      .t insert end $file_text
      set file_text [.t get 1.0 end]
      set range $file_range
      set condition $file_condition
      close $NDM
    }
  }


#
# Procedure to save the message file
#

proc save_file {} {
  global msg_file types range condition file_range file_condition file_text
  set msg_file [tk_getSaveFile -filetypes $types -defaultextension .ndm \
	-initialfile $msg_file]
  if {$msg_file == ""} { return 0 }
  if [catch {open $msg_file w} NDM] {
      puts stderr "Save: $NDM"
      return 0
    } else {
      set file_text [ .t get 1.0 end ]
      set file_range $range
      set file_condition $condition
      puts -nonewline $NDM \
		"RANGE: $file_range\nCOND: $file_condition\n$file_text"
      close $NDM
      return 1
    }
  }


#
# Procedure to check as to whether the data has changed and prompting a
# "save" in that case.  Returns 1 (true) unless the "Cancel" button
# clicked on the "save" prompt, or the save fails.
#

proc check_for_save {} {
  global range condition file_range file_condition file_text
  if { $range != $file_range || $condition != $file_condition || \
       [.t get 1.0 end] != $file_text } {
    set answer [tk_messageBox -message "Save data first?" \
		-type yesnocancel -icon question]
    switch $answer {
      yes { return [save_file] }
      no { return 1 }
      cancel { return 0 }
      }
    } else {
      return 1
    }
  }


#
# Build the GUI
#

frame .text_frame
frame .entry_frame -relief ridge -borderwidth 2
frame .entry_fill -width 50
frame .button_frame

text .t -yscrollcommand {.yscroll set}
scrollbar .yscroll -command {.t yview}

label .laddr -text "Address Range"
entry .eaddr -textvariable range

label .lcond -text "Condition"
entry .econd -textvariable condition

button .b1 -text "Open" -command { open_file }
button .b2 -text "Save" -command save_file
button .b3 -text "Preview" -command preview
button .b4 -text "Send" -command { verify_send }
button .b5 -text "Help" -command { show_help }
button .b6 -text "Demo" -command { load_demo }
button .b7 -text "Exit" -command { if [check_for_save] { exit } }

pack .yscroll -in .text_frame -side right -fill y
pack .t -in .text_frame

pack .laddr .eaddr .entry_fill .lcond .econd -in .entry_frame \
	-padx 4 -pady 4 -side left
pack .b1 .b2 .b3 .b4 .b5 .b6 .b7 -in .button_frame -side left

pack .button_frame .entry_frame .text_frame -side bottom -pady 4

wm title . "NExS Data Mailer"

#
# Connect to NExS running on the local display
#

puts "Starting NExS with connections enabled..."
set nexs_pid [exec /usr/local/bin/nexs -con a &]
set wait 10
if [catch {nexs connect port -name DataMailer -timeout $wait \
	-closecommand { tk_messageBox -message "NExS Disconnected..." } }] {
  puts "Couldn't connect to NExS..."
  destroy .
  }

#
# Perform variable initialization; open file if one is specified
#

global file_range file_condition file_text types
set msg_file ""
set file_range ""
set file_condition ""
set file_text "\n"
set types {
  {"Message files"	{.ndm}	}
  {"All files"		*	}
  }

if { $argc } {
  open_file [lindex $argv 0]
  } else {
  display_startup_msg
  }
