##
## Watermelon3D Builder for VMD
##
## A script to create XML file for wm3d
##
## Author: tamanegi (tamanegi@users.sourceforge.jp)

package provide wm3d 0.21

namespace eval ::WM3D:: {
  namespace export wm3d
  
  variable win     ;# window handle
  variable win_set ;# window for default setting
  variable Haxe
  variable wm3d
  variable outFileXML
  variable playerCommand

  variable cb     ;# use player for debug
  variable cb_ar  0
  variable cb_abs 1
}

proc ::WM3D::wm3d {} {
  variable win
  variable win_set
  variable Haxe
  variable wm3d
  variable outFileXML
  variable playerCommand
  variable cb
  global env

  if { [winfo exists .wm3d] } {
    wm deiconify $win
    return
  }

  # set default values to variables
  ::WM3D::setDefaultVariables
  set win [toplevel ".wm3d"]
  wm title $win "WM3D Builder"
  wm resizable $win 0 0

  # menu bar
  ::WM3D::setMenubar

  # file selector
  ::WM3D::fileSelectors

  # setting
  frame $win.setting
  button $win.setting.button -bd 2 -text "Default Setting" -command ::WM3D::modifyDefaultSetting
  pack $win.setting.button -side left
  pack $win.menubar $win.setting -side top -anchor w

  labelframe $win.checkview -bd 2 -relief groove -text "View Options"
  frame $win.cb
  checkbutton $win.cb.cb -text "Display SWF with external command " -variable ::WM3D::cb
  entry $win.cb.ent -textvariable ::WM3D::playerCommand -width 20
  button $win.cb.button -text "Select" -command ::WM3D::selectPlayer
  pack $win.cb.cb $win.cb.ent $win.cb.button -side left -anchor w -ipadx 3 -ipady 3
  pack $win.cb -in $win.checkview -side top -anchor w -ipadx 3 -ipady 3
  pack $win.menubar $win.checkview -anchor nw -fill x -padx 3 -pady 3

  # xml data window
  labelframe $win.xml -bd 2 -relief groove -text "XML data"
  frame $win.xml.field
  text $win.xml.field.text -bg White -bd 2 \
                     -yscrollcommand "$::WM3D::win.xml.field.vscr set"
  scrollbar $win.xml.field.vscr -command "$::WM3D::win.xml.field.text yview"
  pack $win.xml.field.text $win.xml.field.vscr -side left -fill y -anchor w

  frame $win.xml.buttons
  button $win.xml.buttons.genxml_button -bd 2 -text "Generate XML" -command ::WM3D::generateXML
  button $win.xml.buttons.gen_button -bd 2 -text "Generate SWF" -command ::WM3D::generateSWF
  button $win.xml.buttons.clear_button -bd 2 -text "Clear" -command ::WM3D::clearField
  pack $win.xml.buttons.genxml_button $win.xml.buttons.gen_button $win.xml.buttons.clear_button -side left
  pack $win.xml.field $win.xml.buttons -side top
  pack $win.menubar $win.xml -padx 3 -pady 3
}

proc ::WM3D::clearField {} {
  variable win

  $win.xml.field.text delete 1.0 end
}

proc ::WM3D::modifyDefaultSetting {} {
  variable win_set
  variable cb_ar
  variable cb_abs

  if { [winfo exists .wm3d_set] } {
    wm deiconify $win_set
    return
  }

  set win_set [toplevel ".wm3d_set"]
  wm title $win_set "WM3D Default Settings"
  wm resizable $win_set 0 0

  # checkbuttons
  labelframe $win_set.setting -bd 2 -relief groove -text "Options"
  checkbutton $win_set.setting.cb0 -text "Prevent Auto-Rotation" -variable ::WM3D::cb_ar
  checkbutton $win_set.setting.cb1 -text "Use Absolute Scale (Recommended)" -variable ::WM3D::cb_abs
  pack $win_set.setting.cb0 $win_set.setting.cb1 -in $win_set.setting -side top -anchor w
  pack $win_set.setting -anchor nw -fill x

  # atom/bond/ribbon/coil ... not implemented yet
}

proc ::WM3D::generateInputForBuilder { fname datafile args } {
  set f [open $fname a]
  puts $f "load $datafile"
  puts $f "s"
  foreach a $args {
    puts $f "$a"
  }
  close $f
}

proc ::WM3D::generateXML {} {
  variable win
  variable wm3d
  variable cb_ar
  variable cb_abs

  clearField

  set nummol [molinfo num]
  $win.xml.field.text insert end "<WMXML>\n"
  # setting
  if { $cb_ar != 0 } {
    $win.xml.field.text insert end "  <GLOBAL arrate=\"0\">\n"
  } else {
    $win.xml.field.text insert end "  <GLOBAL>\n"
  }
  if { $cb_abs != 0 } {
    $win.xml.field.text insert end "    <RADIUS method=\"absolute\" />\n"
    $win.xml.field.text insert end "    <AUTOSCALE manual=\"1\" />\n"
  }
  $win.xml.field.text insert end "  </GLOBAL>\n"

  # script location
  set scriptdir [file join $wm3d scripts]
  set script_py_dir [file join $scriptdir python]
  set builderdir [file join $script_py_dir builder]
  set builder [file join $builderdir builder.py]

  #set workpdb "wm3d-autogen_xml.pdb"
  # tempfile is not available for vmd-bundled tcl?
  set tempinput "wm3d-temporary.input"

  # loop over molecules to create pdb files
  # currently, a scene is assumed to contain only one molecule
  # therefore, foreach loop just below is meaningless so far
  foreach molid [molinfo list] {
    set numrep [molinfo $molid get numreps]
    # loop over representations
    for { set crep 0 } { $crep < $numrep } { incr crep } {
      set repres [molinfo $molid get "{ rep $crep }"]
      set select [molinfo $molid get "{ selection $crep }"]
      set sel [atomselect $molid $select]
      set workpdb [format {wm3d-autogen_xml%d.pdb} $crep]
      set pdbfiles($crep) $workpdb
      $sel writepdb $workpdb
    }
  }

  foreach molid [molinfo list] {
    set numrep [molinfo $molid get numreps]
    for { set crep 0 } { $crep < $numrep } { incr crep } {
      set myid [join [list $molid $crep] ""]
      set repres [molinfo $molid get "{ rep $crep }"]
      set select [molinfo $molid get "{ selection $crep }"]
      set sel [atomselect $molid $select]
      set workpdb $pdbfiles($crep)
      # color
      set mycolor [lindex [molinfo $molid get "{ color $crep }"] 0]
      set mycolorsplit [split $mycolor " "]
      set colorname ""
      if { [llength $mycolorsplit] > 1 && \
           [lindex $mycolorsplit 0] == "ColorID" } {
        # color in swf can be specified only by ColorID
        set colorname [lindex [colorinfo colors] [lindex $mycolorsplit 1]]
      }
      # representations:
      # Lines Bonds Licorice - BOND
      # VDW - ATOM
      # CPK - ATOM and BOND
      # Tube - COIL
      # Ribbons NewRibbons Cartoon NewCartoon - COIL and RIBBON
      if { [regexp {Lines} $repres] != 0 || \
           [regexp {Bonds} $repres] != 0 || \
           [regexp {Licorice} $repres] != 0 } {       ;# BOND
        ::WM3D::generateInputForBuilder $tempinput $workpdb "genbonds"
        puts stdout "Info) Sticks"
      } elseif { [regexp {VDW} $repres] != 0 } {      ;# ATOM
        ::WM3D::generateInputForBuilder $tempinput $workpdb "genatoms"
        puts stdout "Info) Balls"
      } elseif { [regexp {CPK} $repres] != 0 } {      ;# ATOM & BOND
        ::WM3D::generateInputForBuilder $tempinput $workpdb "genatoms norad" "genbonds offset=2"
        puts stdout "Info) Balls and Sticks"
      } elseif { [regexp {Tube} $repres] != 0 } {     ;# COIL
        set mysuffix [join [list "suffix" $myid]]
        if { $colorname != "" } {
          set seccol [join [list "acol" $colorname]]
          ::WM3D::generateInputForBuilder $tempinput $workpdb $seccol $mysuffix "gencoils"
        } else {
          ::WM3D::generateInputForBuilder $tempinput $workpdb $mysuffix "gencoils"
        }
        puts stdout "Info) Tube"
      } elseif { [regexp {Ribbons} $repres] != 0 || \
                 [regexp {Cartoon} $repres] != 0 } {  ;# COIL & RIBBON
        set mysuffix [join [list "suffix" $myid]]
        if { $colorname != "" } {
          set seccol [join [list "acol" $colorname]]
          ::WM3D::generateInputForBuilder $tempinput $workpdb $seccol $mysuffix "genchains"
        } else {
          ::WM3D::generateInputForBuilder $tempinput $workpdb $mysuffix "genchains"
        }
        puts stdout "Info) Ribbon"
      } else {
        continue
      }
    }
  }
  set fl [open "| python $builder < $tempinput "]
  set line ""
  while { [gets $fl line] != -1 } {
    $win.xml.field.text insert end "$line\n"
  }
  $win.xml.field.text insert end "</WMXML>"
  file delete $tempinput
}

proc ::WM3D::generateSWF {} {
  variable win
  variable Haxe
  variable wm3d
  variable outFileXML
  variable cb
  variable playerCommand

  # filename of this structure; may be pdb?
  set datafiles [molinfo top get filename]
  set inputfile [join [lindex $datafiles 0]]
  set basedir [file normalize [file dirname $inputfile]]

  set xmltext [$win.xml.field.text get 1.0 end]

  if { $Haxe == "" } {
    tk_messageBox -message "path to Haxe must be specified" -type ok
    puts stdout "Haxe must be specified"
    return
  }

  if { $wm3d == "" } {
    tk_messageBox -message "path of WM3D must be specified" -type ok
    puts stdout "WM3D must be specified"
    return
  }

  # do nothing if the output file name is empty
  if { $outFileXML == "" } {
    tk_messageBox -message "Please specify OUTPUT XML file name" -type ok
    puts stdout "Output XML file name is empty."
    return
  }

  # do nothing if the XML data is too small
  if { [$win.xml.field.text count -displaychars 1.0 end] < 10 } {
    tk_messageBox -message "please input XML data" -type ok
    puts stdout "insufficient XML data."
    return
  }

  if { [file pathtype $outFileXML] == "relative" } {
    set myxml [file join $basedir $outFileXML]
  } else {
    set myxml $outFileXML
  }

  # save xml file
  set rc [ catch { set fd [open $myxml "w"] } ]
  if { $rc != 0 } {
    tk_messageBox -message "failed to open output file" -type ok
    puts stdout "failed to open output file"
    return
  }
  
  # write data and close file
  puts $fd $xmltext
  close $fd

  # generate .hxml file
  set hxmlfile [regsub -all {\.xml$} $myxml {.hxml} ]
  set rc [ catch { set fd [open $hxmlfile "w"] } ]
  if { $rc != 0 } {
    tk_messageBox -message "failed to create .hxml file. please check the write permission of WM3D install directory" -type ok
    puts stdout "failed to create .hxml file"
    return
  }
  set resource $myxml
  set swffile [regsub -all {\.xml$} $resource {.swf} ]
  puts $fd "-cp tinylib"
  puts $fd "-cp pdb"
  puts $fd "-swf $swffile"
  puts $fd "-swf-version 9"
  puts $fd "--flash-strict"
  puts $fd "-swf-version 11.2"
  puts $fd "-lib format"
  puts $fd "-resource $resource@structure"
  puts $fd "-main Main"
  close $fd

  # generate swf file
  set keep_pwd [pwd]
  ## cd to wm3d src directory
  cd [file join $wm3d src]
  ## compile into swf
  eval exec "{$Haxe} {$hxmlfile}"
  cd $keep_pwd

  # show compiled swf
  if { $cb != 0 && $playerCommand != "" } {
    eval exec "{$playerCommand} {$swffile} &"
  }
}

proc ::WM3D::setDefaultVariables {} {
  variable playerCommand
  variable Haxe
  variable wm3d
  variable outFileXML
  global env

  set outFileXML "wm3d.xml"

  # default player for debug
  if { [info exists env(WMPLAYER)] } {
    set playerCommand $env(WMPLAYER)
  } else {
    set playerCommand ""
  }

  # Haxe command path
  if { [info exists env(HAXE)] } {
    set Haxe $env(HAXE)
  } else {
    set Haxe "haxe"
  }

  # wm3d install directory
  if { [info exists env(WM3D) ] } {
    set wm3d $env(WM3D)
  } else {
    set wm3d ""
  }
}

proc ::WM3D::setMenubar {} {
  variable win

  frame $win.menubar -relief raised -bd 2 ;# menubar frame
  pack $win.menubar -fill x
  menubutton $win.menubar.file -text File -underline 0 -menu $win.menubar.file.menu

  # "File" menu
  menu $win.menubar.file.menu -tearoff no
  $win.menubar.file.menu add command -label "Open XML File" -command ::WM3D::openxml
  $win.menubar.file config -width 5
  pack $win.menubar.file -side left
}

proc ::WM3D::fileSelectors {} {
  variable win
  variable Haxe
  variable wm3d
  variable outFileXML

  # Haxe path
  frame $win.haxe
  label $win.haxe.lab -text "Haxe"
  entry $win.haxe.ent -textvariable ::WM3D::Haxe -width 20
  button $win.haxe.button -text "Select" -command ::WM3D::selectHAXE
  pack $win.haxe.lab $win.haxe.ent $win.haxe.button -side left -anchor w
  pack $win.menubar $win.haxe -side top -anchor w

  # WM3D directory
  frame $win.wm3d
  label $win.wm3d.lab -text "WM3D Directory"
  entry $win.wm3d.ent -textvariable ::WM3D::wm3d -width 20
  button $win.wm3d.button -text "Select" -command ::WM3D::selectWMDir
  pack $win.wm3d.lab $win.wm3d.ent $win.wm3d.button -side left -anchor w
  pack $win.menubar $win.wm3d -side top -anchor w

  # xml file name
  frame $win.filename
  label $win.filename.lab -text "Output XML Filename"
  entry $win.filename.ent -textvariable ::WM3D::outFileXML -width 20
  button $win.filename.button -text "Select" -command ::WM3D::selectFilename
  pack $win.filename.lab $win.filename.ent $win.filename.button -side left -anchor w
  pack $win.menubar $win.filename -side top -anchor w
}

proc ::WM3D::selectHAXE {} {
  variable Haxe

  set afile [tk_getOpenFile -title "Select OUTPUT file" \
                            -filetypes { { "All Files" * } } \
                            -initialdir pwd ]
  if { $afile != "" } { set Haxe $afile }
}

proc ::WM3D::selectWMDir {} {
  variable wm3d

  set dir [tk_chooseDirectory -title "Select WM3D Install Directory" \
                              -initialdir pwd ]
  if { $dir != "" } { set wm3d $dir }
}

proc ::WM3D::selectFilename {} {
  variable outFileXML

  set afile [tk_getOpenFile -title "Select OUTPUT file" \
                            -filetypes { { "XML Files" { .xml } } \
                                         { "All Files" * } } \
                            -initialdir pwd ]
  if { $afile != "" } { set outFileXML $afile }
}

proc ::WM3D::selectPlayer {} {
  variable playerCommand

  set afile [tk_getOpenFile -title "Select Flash Player" \
                            -filetypes { { "All Files" * } } \
                            -initialdir pwd ]

  if { $afile != "" } { set playerCommand $afile }
}

proc ::WM3D::openxml {} {
  variable win

  set xmlfile "untitled.xml"

  set file_types {
    { "XML Files" { .xml } }
    { "All Files" * }
  }

  set xmlfile [tk_getOpenFile -filetypes { { "XML Files" { .xml } } \
                                           { "All Files" * } } \
                              -initialdir pwd \
                              -initialfile "$xmlfile" \
                              -defaultextension .xml]
  set rc [ catch { set fd [open $xmlfile "r"] } ]
  if { $rc == 1 } {
    set xmlfile "untitled.xml"
    return
  }

  clearField

  set line ""
  while { [gets $fd line] != -1 } {
    set dtext "$line\n"
    $win.xml.field.text insert end $dtext
  }

  close $fd
}

proc wm3d_tk {} {
  WM3D::wm3d
  return $WM3D::win
}
