# LIBTBX_SET_DISPATCHER_NAME phenix.sceds
# LIBTBX_SET_DISPATCHER_NAME phaser.sceds

from __future__ import division
from phaser.command_line import nma_perturb
from libtbx.program_utils.result import program_result
from libtbx import runtime_utils
from libtbx.utils import Usage, Sorry
import os
import sys

def master_params () :
  return nma_perturb.master_params(include_domains=True)

def run (args, out=None) :
  if (out is None) : out = sys.stdout
  master_phil = master_params()
  if (len(args) == 0) :
    raise Usage("""phenix.sceds model.pdb [ndom=X] [options]

Full keywords:
%s""" % master_phil.as_str())
  import iotbx.phil
  cmdline = iotbx.phil.process_command_line_with_files(
    args=args,
    master_phil=master_phil,
    pdb_file_def="file_name")
  params = cmdline.work.extract()
  nma_perturb.validate_params_basic(params)
  if (params.root is None) :
    params.root = os.path.basename(os.path.splitext(params.file_name)[0]) + \
      "_NMA"
  log_file = params.root + ".log"
  out2 = nma_perturb.setup_logging(log_file, out)
  phaser_result = nma_perturb.run_phaser(
    pdb_file=params.file_name,
    root=params.root,
    params=params.keywords,
    domain_finding=True,
    log=out2)
  selections = analyze_domains(
    phaser_result=phaser_result,
    pdb_file=params.file_name,
    validate=params.validate_domains,
    run_from_command=True)
  phil_file = params.root + "_selections.phil"
  f = open(phil_file, "w")
  print >> out2, "Atom selections"
  for sele in selections :
    print >> out2, "  " + sele
    print >> f, "selection = %s" % sele
  print >> out2, ""
  # FIXME this is dangerous
  #out2.close()
  pdb_files = phaser_result.getPdbFiles()
  pdb_files = [ os.path.basename(f) for f in pdb_files ]
  try :
    from phenix.utilities import citations
  except ImportError :
    pass
  else :
    citations.show_citation("sceds", out=out2)
  return sceds_result(
    program_name="phenix.sceds",
    job_title=params.job_title,
    directory=os.getcwd(),
    log_file=log_file,
    pdb_files=pdb_files,
    phil_files=[phil_file])

def load_selections (file_name) :
  import iotbx.phil
  sele_master = iotbx.phil.parse("""
selection = None
  .type = atom_selection
""")
  file_phil = iotbx.phil.parse(file_name=file_name)
  return sele_master.fetch(source=file_phil).extract()

def analyze_domains (
    phaser_result,
    pdb_file=None,
    pdb_hierarchy=None,
    validate=False,
    run_from_command=False) :
  from iotbx import file_reader
  from iotbx.pdb.hierarchy import get_contiguous_ranges
  files = phaser_result.getPdbFiles()
  if (pdb_file is not None) :
    assert (pdb_hierarchy is None)
    pdb_in = file_reader.any_file(pdb_file, force_type="pdb")
    pdb_in.assert_file_type("pdb")
    pdb_hierarchy = pdb_in.file_object.construct_hierarchy()
  assert (pdb_hierarchy is not None)
  # TODO handle segids?
  selections = []
  for file_name in files :
    if (file_name.endswith("excluded.pdb")) :
      continue # TODO should this be analyzed?  may be empty...
    pdb_in = file_reader.any_file(file_name, force_type="pdb")
    pdb_in.assert_file_type("pdb")
    domain_hierarchy = pdb_in.file_object.construct_hierarchy()
    assert (len(domain_hierarchy.models()) == 1)
    clauses = get_contiguous_ranges(domain_hierarchy)
    assert (len(clauses) >= 1)
    if (len(clauses) > 1) :
      selections.append("(" + ") or (".join(clauses) + ")")
    else :
      selections.append(clauses[0])
  if (validate) :
    sel_cache = pdb_hierarchy.atom_selection_cache()
    for i, sel_str in enumerate(selections) :
      sele = sel_cache.selection(sel_str)
      for j, other_str in enumerate(selections) :
        if (i == j) : continue
        sele2 = sel_cache.selection(other_str)
        if (not (sele & sele2).all_eq(False)) :
          extra_msg = ""
          if (run_from_command) :
            extra_msg = "\nTo skip this check, set validate_domains=False "+\
              "(or from the GUI, uncheck \"Validate domain selections\")"
          raise Sorry("Overlapping selections:\n%d: \"%s\"\n%d: \"%s\"%s" %
            (i+1, sel_str, i+2, other_str, extra_msg))
  return selections

#-----------------------------------------------------------------------
# GUI stuff below here
def validate_params (params) :
  return nma_perturb.validate_params(params)

class sceds_result (program_result) :
  def finish_job (self) :
    output_files = [ (f, "Domain model") for f in self.get_pdb_files() ]
    return output_files, {}

class launcher (runtime_utils.target_with_save_result) :
  def run (self) :
    os.makedirs(self.output_dir)
    os.chdir(self.output_dir)
    return run(self.args, out=sys.stdout)

if (__name__ == "__main__") :
  run(sys.argv[1:])
