# coding: utf-8
#
#  mopac6.rb
#
#  Created by Toshi Nagata on 2013/11/01.
#  Copyright 2013 Toshi Nagata. All rights reserved.
#
# 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 version 2 of the License.
# 
# 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.

class Molecule

  #  Execute mopac6
  #  inpname is the input file
  #  mol (optional) is the molecule from which the MOPAC input was built.
  #  Currently not used (mopac6 is not bundled)
  def Molecule.execute_mopac(inpname, mol = nil)

    #  MOPAC executable
    mopexe = "#{ResourcePath}/mopac/mopac606"

	inpname = File.expand_path(inpname)
    inpbase = File.basename(inpname)
    inpdir = File.dirname(inpname)
    inpbody = inpbase.sub(/\.\w*$/, "")

    #  Prepare the scratch directory in the home directory
    scrdir = document_home.sub(/\/My Documents/, "") + "/mopac"
    n = 0
    while File.exist?(scrdir) && !File.directory?(scrdir)
      if n == 0
        scrdir += ".1"
      else
        scrdir = scrdir.sub(".#{n}", ".#{n + 1}")
      end
      n += 1
    end
    if !File.exist?(scrdir)
      Dir.mkdir(scrdir)
    end
    scrdir = scrdir + "/" + inpbody + "." + $$.to_s + ".0"
    n = 0
    while File.exist?(scrdir)
      scrdir = scrdir.sub(".#{n}", ".#{n + 1}")
      n += 1
    end
    Dir.mkdir(scrdir)

	#  Copy the input file to the scratch directory
	cwd = Dir.pwd
	filecopy(inpname, scrdir + "/FOR005")
	term_callback = lambda { |m, n|
	    if File.exist?("#{scrdir}/FOR006")
		  filecopy("#{scrdir}/FOR006", "#{inpdir}/#{inpbody}.out")
		end
	    msg = "MOPAC execution on #{inpbase} "
	    hmsg = "MOPAC "
	    if n == 0
	      msg += "succeeded."
	  	  hmsg += "Completed"
		  icon = :info
	    else
	      msg += "failed with status #{n}."
		  hmsg += "Failed"
		  icon = :error
	    end
	    msg += "\n(In directory #{inpdir})"
	    message_box(msg, hmsg, :ok, icon)
		erase_old_logs(scrdir, "latest", 5)  #  Keep latest 5 logs
	}
	timer_callback = nil
	
	#  Execute mopac
	Dir.chdir(scrdir)
	if mol
	  pid = mol.call_subprocess_async(mopexe, term_callback, timer_callback, "NUL", "NUL")
	else
	  pid = call_subprocess(mopexe, "Running MOPAC", nil, "NUL", "NUL")
	  term_callback.call(nil, pid)
	end
	Dir.chdir(cwd)
	if pid < 0
	  error_message_box("MOPAC failed to run.")
	  return
	end

  end
  
  def create_mopac
    if !@mopac_settings || (com = @mopac_settings["commands"]) == nil
	  com = " PM3"
	  (@mopac_settings ||= Hash.new)["commands"] = com
	end
	str = com + "\n"
	str += sprintf("# %-76.76s\n", self.name)
	str += sprintf("# %-76.76s\n", "Generated by Molby at #{Time.now.to_s}")
	#  Create geometry input
	geom = ""
	if self.natoms <= 3
	  #  Z-matrix
	  if self.natoms >= 1
	    geom += atoms[0].element + "\n"
        if self.natoms >= 2
		  r = calc_bond(0, 1)
		  geom += sprintf("%s %.6f 1\n", atoms[1].element, r)
		  if self.natoms >= 3
			con = atoms[2].connects
			if con.include?(0)
			  r = calc_bond(0, 2)
			  a = calc_angle(2, 0, 1)
			  c = "1 2"
			else
			  r = calc_bond(1, 2)
			  a = calc_angle(2, 1, 0)
			  c = "2 1"
			end
			geom += sprintf("%s %.6f 1 %.3f 1 %s\n", atoms[2].element, r, a, c)
		  end
		end
	  end
	else
	  #  Cartesian
	  atoms.each { |ap|
	    geom += sprintf("%s %.6f 1 %.6f 1 %.6f 1\n", ap.element, ap.x, ap.y, ap.z)
	  }
	end
	return str + geom + "\n"
  end
  
  def cmd_create_mopac_input

    mol = self
    h = Dialog.run("Create MOPAC6 Input") {
	  layout(1,
	    item(:text, :title=>"MOPAC input"),
	    item(:textview, :width=>480, :height=>200, :tag=>"mopac_commands"),
	  )
	  item_with_tag("mopac_commands")[:font] = [:fixed, 10]
	  item_with_tag("mopac_commands")[:value] = mol.create_mopac
	}
	#  Record the command lines for next invocation
	mop = h["mopac_commands"]
	com = (mop.split(/[\r\n]/))[0..0].join("\n")
	(@mopac_settings ||= Hash.new)["commands"] = com
	if h[:status] == 0
	  basename = (self.path ? File.basename(self.path, ".*") : self.name)
      fname = Dialog.save_panel("Export MOPAC input file:", self.dir, basename + ".mopin", "MOPAC input file (*.mopin)|*.mopin|All files|*.*")	  
	  return nil if !fname
      open(fname, "w") { |fp|
	    fp.write(mop)
	  }
	end
	
  end
  
#  if lookup_menu("Create MOPAC6 Input...") < 0
#    register_menu("", "")
#    register_menu("Create MOPAC6 Input...", :cmd_create_mopac)
#  end

end
