# fitting.py
# Copyright 2005, 2006, 2007 by Bernhard Lohkamp 
# Copyright 2004, 2005, 2006, 2008, 2009 by The University of York
# Copyright 2009 by Bernhard Lohkamp
#
#    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, either version 3 of the License, or
#    (at your option) any later version.
#
#    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.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.


# For each residue in the protein (molecule number @var{imol}), do a
# rotamer fit and real-space refinement.  Update the graphics and
# rotate the scene at each residue for eye candy goodness.
#
# Note that residue with alt confs do not undergo auto-fit-rotamer.
# This is because that autofit-rotamer then refine will tend to put
# both rotamers into the same place.  Not good.  It seems a
# reasonable expectation that residues with an alternate conformation
# are already resonably well-fitted.  So residues with alternate
# conformations undergo only real space refinement.
#
# This is simple-minded and outdated now we have the interruptible
# version (below).
#
def fit_protein(imol):

    set_go_to_atom_molecule(imol)
    make_backup(imol) # do a backup first
    backup_mode = backup_state(imol)
    imol_map  = imol_refinement_map()
    replacement_state = refinement_immediate_replacement_state()

    if imol_map == -1:
	info_dialog("Oops.  Must set a map to fit")
    else:

	turn_off_backup(imol)
	set_refinement_immediate_replacement(1)
	  
	for chain_id in chain_ids(imol):
         if (not is_solvent_chain_qm(imol,chain_id)):
	     n_residues = chain_n_residues(chain_id,imol)
             print "There are %(a)i residues in chain %(b)s" % {"a":n_residues,"b":chain_id}
	       
	     for serial_number in range(n_residues):
		  
                res_name = resname_from_serial_number(imol, chain_id, serial_number)
                res_no = seqnum_from_serial_number(imol, chain_id, serial_number)
                ins_code = insertion_code_from_serial_number(imol, chain_id, serial_number)
                res_atoms = residue_info(imol, chain_id, res_no, ins_code)
                
                if (ins_code is not None):
                    if (len(res_atoms) > 3):
                    #if (not res_name=="HOH"): not needed if only refining 3 or  more atoms
                        for alt_conf in residue_alt_confs(imol, chain_id, res_no, ins_code):
                            print "centering on ",chain_id,res_no," CA"
                            set_go_to_atom_chain_residue_atom_name(chain_id,res_no,"CA")
                            rotate_y_scene(30, 0.3) # n-frames frame-interval(degrees)
                            if (alt_conf == ""):
                                auto_fit_best_rotamer(res_no, alt_conf, ins_code, chain_id, imol,
                                                      imol_map, 1, 0.1)
                            if (imol_map >= 0):
                                refine_zone(imol, chain_id, res_no, res_no, alt_conf)
                                accept_regularizement()
                            rotate_y_scene(30, 0.3)
      
    if (replacement_state == 0):
	  set_refinement_immediate_replacement(0)
    if (backup_mode == 1):
	  turn_on_backup(imol)

# Paul: 20090517: thinking about making the fit-protein function
# interruptible with a toolbar button press.  How do we do that? 
# fit_protein needs to be split into 2 parts, one, that generates a
# list of residues specs the other that does a refinement given a
# residue spec, then we run and idle function that calls
# fit_residue_by_spec or each spec in turn and at the end return
#

# These 2 variables are used by multi-refine function(s), called by
# idle functions to refine just one residue.
# 
global continue_multi_refine
global multi_refine_spec_list
global multi_refine_idle_proc
global multi_refine_stop_button
global multi_refine_cancel_button
global multi_refine_continue_button
global multi_refine_separator
continue_multi_refine = False
multi_refine_spec_list = []
multi_refine_idle_proc = False
multi_refine_stop_button = False
multi_refine_cancel_button = False
multi_refine_continue_button = False
multi_refine_separator = False

# Return a list of residue specs
#
# chain-specifier can be a string, where it is the chain of interest.
# or 'all-chains, where all chains are chosen.
#
def fit_protein_make_specs(imol, chain_specifier):
    from types import StringType
    if (not valid_model_molecule_qm(imol)):
        return []
    else:
        if (chain_specifier == 'all-chains'):
            chain_list = chain_ids(imol)
        elif (type(chain_specifier) == StringType):
            chain_list = [chain_specifier]
        else:
            chain_list = []   # return null list - incomprehensible

        spec_list = []
        for chain_id in chain_list:
            serial_number_list = range(chain_n_residues(chain_id, imol))
            #print " seqnum from ", imol, chain_id, serial_number_list
            for serial_number in serial_number_list:
                res_no   = seqnum_from_serial_number(imol, chain_id,
                                                   serial_number)
                ins_code = insertion_code_from_serial_number(imol, chain_id,
                                                             serial_number)
                if ins_code is not None:
                    spec_list.append([imol, chain_id, res_no, ins_code])
                else:
                    # bad ins_code mean no such residue -> pass
                    pass
        return spec_list

    
def fit_protein_make_specs_from_residue_range(imol, chain_id,
                                              res_no_start, res_no_end):
    if valid_model_molecule_qm(imol):
        if res_no_end > res_no_start:
            ret = map(lambda res_no: [chain_id, res_no, ""],
                      range(res_no_start, res_no_end + 1 ))
            return ret
    return []
    
    
def fit_protein_fit_function(res_spec, imol_map):

    imol     = res_spec[0]
    chain_id = res_spec[1]
    res_no   = res_spec[2]
    ins_code = res_spec[3]

    res_name = residue_name(imol, chain_id, res_no, ins_code)
    if isinstance(res_name, str):
        if (res_name != "HOH"):
            for alt_conf in residue_alt_confs(imol, chain_id, res_no, ins_code):
                print "centering on", chain_id, res_no, "CA"
                set_go_to_atom_chain_residue_atom_name(chain_id, res_no, "CA")
                rotate_y_scene(10, 0.3) # n_frames frame_interval(degrees)        
                res_atoms = residue_info(imol, chain_id, res_no, ins_code)
                if (len(res_atoms) > 3):
                    # if (not res_name == "HOH"):
                    # not needed as we only refine more than 3 atom res
                    if (alt_conf == ""):
                        with NoBackups(imol):
                            auto_fit_best_rotamer(res_no, alt_conf, ins_code, chain_id, imol,
                                                  imol_map, 1, 0.1)
                    if (valid_map_molecule_qm(imol_map)):
                        with NoBackups(imol):
                            with AutoAccept():
                                refine_zone(imol, chain_id, res_no, res_no, alt_conf)
                    rotate_y_scene(10, 0.3)


def fit_protein_stepped_refine_function(res_spec, imol_map, use_rama = False):

    imol     = res_spec[0]
    chain_id = res_spec[1]
    res_no   = res_spec[2]
    ins_code = res_spec[3]
    current_steps_per_frame = dragged_refinement_steps_per_frame()
    current_rama_state = refine_ramachandran_angles_state()
    set_dragged_refinement_steps_per_frame(400)
    
    for alt_conf in residue_alt_confs(imol, chain_id, res_no, ins_code):
        if use_rama:
            set_refine_ramachandran_angles(1)
        res_name = residue_name(imol, chain_id, res_no, ins_code)
        if (not res_name == "HOH"):
            print "centering on", chain_id, res_no, "CA"
            set_go_to_atom_chain_residue_atom_name_full(chain_id, res_no,
                                                        ins_code, "CA",
                                                        alt_conf)
            rotate_y_scene(10, 0.3) # n_frames frame_interval(degrees)
            with NoBackups(imol):
                with AutoAccept():
                    refine_auto_range(imol, chain_id, res_no, alt_conf)
            rotate_y_scene(10, 0.3)    

    set_refine_ramachandran_angles(current_rama_state)
    set_dragged_refinement_steps_per_frame(current_steps_per_frame)

    
def fit_protein_rama_fit_function(res_spec, imol_map):
    # BL says: make it more generic
    fit_protein_stepped_refine_function(res_spec, imol_map, True)

# func is a refinement function that takes 2 args, one a residue
# spec, the other the imol_refinement_map.  e.g. fit_protein_fit_function
#
def interruptible_fit_protein(imol, func):

    import gobject
    global multi_refine_spec_list
    global continue_multi_refine
    global multi_refine_idle_proc
    global multi_refine_stop_button
    global multi_refine_separator
    specs = fit_protein_make_specs(imol, 'all-chains')
    if specs:
        # lets make a backup before we start
        make_backup(imol)
        
        multi_refine_separator = add_coot_toolbar_separator()
        multi_refine_stop_button = coot_toolbar_button("Stop",
                                                       "stop_interruptible_fit_protein()"
                                                       , "gtk-stop")
        multi_refine_spec_list = specs
        def idle_func():
            global multi_refine_spec_list
            global continue_multi_refine
            global multi_refine_idle_proc
            global multi_refine_stop_button
            global multi_refine_separator
            if not multi_refine_spec_list:
                #set_visible_toolbar_multi_refine_stop_button(0)
                #set_visible_toolbar_multi_refine_continue_button(0)
                multi_refine_stop_button.destroy()
                multi_refine_stop_button = False
                multi_refine_separator.destroy()
                multi_refine_separator = False
                return False
            if continue_multi_refine:
                imol_map = imol_refinement_map()
                func(multi_refine_spec_list[0], imol_map)
                del multi_refine_spec_list[0]
                return True
            else:
                # finish what we have been doing first before we stop
                accept_regularizement()
                return False
        gobject.idle_add(idle_func)
        multi_refine_idle_proc = idle_func
        set_go_to_atom_molecule(imol)

# this will stop the currently running interuptible fit protein function
#
def stop_interruptible_fit_protein():
    global continue_multi_refine
    global multi_refine_stop_button
    global multi_refine_cancel_button
    global multi_refine_continue_button
    
    continue_multi_refine = False

    multi_refine_cancel_button = coot_toolbar_button("Cancel",
                                                     "cancel_interruptible_fit_protein()",
                                                     "gtk-cancel")
    multi_refine_continue_button = coot_toolbar_button("Continue",
                                                     "continue_interruptible_fit_protein()",
                                                     "gtk-apply")
    
    multi_refine_stop_button.set_sensitive(False)
    #multi_refine_cancel_button.set_sensitive(True)
    #multi_refine_continue_button.set_sensitive(True)

# Continue with the interruptible fitting
#
def continue_interruptible_fit_protein():
    global multi_refine_stop_button
    global multi_refine_cancel_button
    global multi_refine_continue_button
    global continue_multi_refine
    global multi_refine_idle_proc

    multi_refine_stop_button.set_sensitive(True)
    multi_refine_cancel_button.destroy()
    multi_refine_cancel_button = False
    multi_refine_continue_button.destroy()
    multi_refine_continue_button = False
    continue_multi_refine = True
    gobject.idle_add(multi_refine_idle_proc)

# use to completely stop the interruptible fit function
#
def cancel_interruptible_fit_protein():
    global multi_refine_stop_button
    global multi_refine_cancel_button
    global multi_refine_continue_button
    global multi_refine_separator
    global continue_multi_refine

    continue_multi_refine = False
    multi_refine_stop_button.destroy()
    multi_refine_stop_button = False
    multi_refine_cancel_button.destroy()
    multi_refine_cancel_button = False
    multi_refine_continue_button.destroy()
    multi_refine_continue_button = False
    multi_refine_separator.destroy()
    multi_refine_separator = False
    

# For each residue in chain chain-id of molecule number imol, do a
# rotamer fit and real space refinement of each residue.  Don't
# update the graphics while this is happening (which makes it faster
# than fit-protein, but much less interesting to look at).
#
def fit_chain(imol, chain_id):

    make_backup(imol)
    backup_mode = backup_state(imol)
    imol_map = imol_refinement_map()
    alt_conf = ""
    replacement_state = refinement_immediate_replacement_state()

    turn_off_backup(imol)
    set_refinement_immediate_replacement(1)

    if (imol_map == -1):
       print "WARNING:: fit-chain undefined imol-map. Skipping!!"
    else:
       n_residues = chain_n_residues(chain_id,imol)
       for serial_number in range(n_residues):
           res_name = resname_from_serial_number(imol,chain_id,serial_number)
           res_no = seqnum_from_serial_number(imol,chain_id,serial_number)
           ins_code = insertion_code_from_serial_number(imol,chain_id,serial_number)
           if ins_code is not None:
               res_atoms = residue_info(imol, chain_id, res_no, ins_code)
               if (len(res_atoms) > 3):  # actually then we dont need the water check any more?!
                   #if (not res_name == "HOH"):
                       print "centering on ", chain_id, res_no, " CA"
                       set_go_to_atom_chain_residue_atom_name(chain_id,res_no,"CA")
                       auto_fit_best_rotamer(res_no, alt_conf, ins_code,
                                             chain_id, imol, imol_map, 1, 0.1)
                       if (imol_map >= 1):
                           refine_zone(imol,chain_id,res_no,res_no,alt_conf)
                           accept_regularizement()
                   
    if (replacement_state == 0):
          set_refinement_immediate_replacement(0)
    if (backup_mode == 1):
          turn_on_backup(imol)

# As fit_chain, but only for a residue range from resno_start to resno_end
def fit_residue_range(imol, chain_id, resno_start, resno_end):

    make_backup(imol)
    backup_mode = backup_state(imol)
    imol_map = imol_refinement_map()
    alt_conf = ""
    replacement_state = refinement_immediate_replacement_state()

    turn_off_backup(imol)
    set_refinement_immediate_replacement(1)

    if (imol_map == -1):
       print "WARNING:: fit-chain undefined imol-map. Skipping!!"
    else:
       n_residues = chain_n_residues(chain_id,imol)
       ins_code = ""
       for res_no in number_list(resno_start, resno_end):
           print "centering on ", chain_id, res_no, " CA"
           set_go_to_atom_chain_residue_atom_name(chain_id, res_no, "CA")
           res_atoms = residue_info(imol, chain_id, res_no, ins_code)
           if (len(res_atoms) > 3):
               auto_fit_best_rotamer(res_no, alt_conf, ins_code, chain_id,
                                     imol, imol_map, 1, 0.1)
               if (imol_map >= 1):
                   refine_zone(imol,chain_id,res_no,res_no,alt_conf)
                   accept_regularizement()

    if (replacement_state == 0):
          set_refinement_immediate_replacement(0)
    if (backup_mode == 1):
          turn_on_backup(imol)


# For each residue in the solvent chains of molecule number
# @var{imol}, do a rigid body fit of the water to the density.
#
# BL says: we pass *args where args[0]=imol and args[1]=animate_qm (if there)
def fit_waters(imol, animate_qm = False):

    print "animate?:", animate_qm
    imol_map = imol_refinement_map()
    do_animate_qm = False
    if (animate_qm):
        do_animate_qm = True

    print "do_animate?: ", do_animate_qm
 
    if (imol_map != -1):
        replacement_state = refinement_immediate_replacement_state()
        backup_mode = backup_state(imol)
        alt_conf = ""

        turn_off_backup(imol)
        set_refinement_immediate_replacement(1)
        set_go_to_atom_molecule(imol)

        # refine waters
        for chain_id in chain_ids(imol):
            if (is_solvent_chain_qm(imol, chain_id)):
                n_residues = chain_n_residues(chain_id, imol)
                print "There are %(a)i residues in chain %(b)s" % {"a":n_residues,"b":chain_id}
                for serial_number in range(n_residues):
                    res_no = seqnum_from_serial_number(imol,chain_id,serial_number)
                    if do_animate_qm:
                        res_info = residue_info(imol, chain_id, res_no, "")
                        if not res_info == []:
                            atom = res_info[0]
                            atom_name = atom[0]
                            set_go_to_atom_chain_residue_atom_name(chain_id, res_no, atom_name)
                            refine_zone(imol, chain_id, res_no, res_no, alt_conf)
                            rotate_y_scene(30, 0.6)	# n-frames frame-interval(degrees)
                    else:
                        refine_zone(imol,chain_id,res_no,res_no,alt_conf)
                    accept_regularizement()

        if (replacement_state == 0):
            set_refinement_immediate_replacement(0)
        if (backup_mode == 1):
            turn_on_backup(imol)

    else:
        add_status_bar_text("You need to define a map to fit the waters")


# BL thingy:
# as fit_waters, but will only fit a range of waters (start to end).
# speeds things up when refining a lot of waters
#
def fit_waters_range(imol, chain_id, start, end):

    imol_map = imol_refinement_map()
    if (imol_map != -1):
       replacement_state = refinement_immediate_replacement_state()
       backup_mode = backup_state(imol)
       alt_conf = ""

       turn_off_backup(imol)
       set_refinement_immediate_replacement(1)

       if (is_solvent_chain_qm(imol,chain_id)):
          serial_number = start
          while serial_number <= end:
                res_no = serial_number
                refine_zone(imol,chain_id,res_no,res_no,alt_conf)
                accept_regularizement()
                serial_number += 1

       if (replacement_state == 0):
          set_refinement_immediate_replacement(0)
       if (backup_mode == 1):
          turn_on_backup(imol)

# Step through the residues of molecule number imol and at each step
# do a residue range refinement (unlike fit-protein for example,
# which does real-space refinement for every residue).
#
# The step is set internally to 2.
#
def stepped_refine_protein(imol, res_step = 2):

    import types
    from types import IntType

    imol_map = imol_refinement_map()
    if (not valid_map_molecule_qm(imol_map)):            
        info_dialog("Oops, must set map to refine to")
    else:
        def refine_func(chain_id, res_no):
            #print "centering on ",chain_id,res_no," CA"
            set_go_to_atom_chain_residue_atom_name(chain_id, res_no, "CA")
            rotate_y_scene(30, 0.3) # n-frames frame-interval(degrees)
            refine_auto_range(imol, chain_id, res_no, "")
            accept_regularizement()
            rotate_y_scene(30,0.3)
        stepped_refine_protein_with_refine_func(imol, refine_func, res_step)


# refine each residue with ramachandran restraints
#
def stepped_refine_protein_for_rama(imol):

    imol_map = imol_refinement_map()
    if (not valid_map_molecule_qm(imol_map)):
        info_dialog("Oops, must set map to refine to")
    else:
        current_steps_frame = dragged_refinement_steps_per_frame()
        current_rama_state  = refine_ramachandran_angles_state()
        def refine_func(chain_id, res_no):
            set_go_to_atom_chain_residue_atom_name(chain_id, res_no, "CA")
            refine_auto_range(imol, chain_id, res_no, "")
            accept_regularizement()

        set_dragged_refinement_steps_per_frame(400)
        set_refine_ramachandran_angles(1)
        stepped_refine_protein_with_refine_func(imol, refine_func, 1)
        set_refine_ramachandran_angles(current_rama_state)
        set_dragged_refinement_steps_per_frame(current_steps_frame)

#
def stepped_refine_protein_with_refine_func(imol, refine_func, res_step):

    set_go_to_atom_molecule(imol)
    make_backup(imol)
    backup_mode = backup_state(imol)
    alt_conf = ""
    imol_map = imol_refinement_map()
    replacement_state = refinement_immediate_replacement_state()

    if (imol_map == -1):
        # actually shouldnt happen as we set check the map earlier...
        add_status_bar_text("Oops.  Must set a map to fit")
    else:
        turn_off_backup(imol)
        set_refinement_immediate_replacement(1)
        res_step = int(res_step)
        if (res_step <= 1):
            set_refine_auto_range_step(1)
            res_step = 1
        else:
            set_refine_auto_range_step(int(res_step / 2))

        for chain_id in chain_ids(imol):
            n_residues = chain_n_residues(chain_id,imol)
            print "There are %(a)i residues in chain %(b)s" % {"a":n_residues,"b":chain_id}
            
            for serial_number in range(0, n_residues, res_step):
                res_name = resname_from_serial_number(imol, chain_id, serial_number)
                res_no = seqnum_from_serial_number(imol, chain_id, serial_number)
                ins_code = insertion_code_from_serial_number(imol, chain_id, serial_number)
                if ins_code is not None:
                    print "centering on ", chain_id, res_no, " CA"
                    refine_func(chain_id, res_no)
                
        if (replacement_state == 0):
            set_refinement_immediate_replacement(0)
        if (backup_mode == 1):
            turn_on_backup(imol)


# The gui that you see after ligand finding. 
# 
def post_ligand_fit_gui():

     def test(imol):
       if not is_valid_model_molecule(imol):
          return False
       else:
          name = molecule_name(imol)
          if "Fitted ligand" in name:
             list = [name]
             # BL says: I think there must be a cleverer way!
             for i in molecule_centre(imol):
                list.append(i)
             return list
          else:
             return False 
     molecules_matching_criteria(lambda imol: test(imol))

# test_func is a function given one argument (a molecule number) that
# returns either False if the condition is not satisfied or something
# else if it is.  And that "something else" can be a list like
# [label, x, y, z]
# or 
# ["Bad Chiral", 0, "A", 23, "", "CA", "A"]
# 
# It is used in the create a button label and "what to do when the
# button is pressed".
# 
def molecules_matching_criteria(test_func):

    try:
        import pygtk
        pygtk.require("2.0")
        import gtk, pango

        # first count the number of fitted ligands, and post this if is
        # is greater than 0.

        passed_molecules = []
        for molecule_numbers in molecule_number_list():

            if (test_func(molecule_numbers)):
               passed_molecules.append(molecule_numbers)

        if (len(passed_molecules) > 0):
        # ok proceed

           def delete_event(*args):
               window.destroy()
               return False

           def centre_on_mol(widget, imol, name):
               s = "Centred on " + name
               add_status_bar_text(s)
               centre = molecule_centre(imol)
               set_rotation_centre(*centre)

           window = gtk.Window(gtk.WINDOW_TOPLEVEL)
           scrolled_win = gtk.ScrolledWindow()
           outside_vbox = gtk.VBox(False,2)
           inside_vbox = gtk.VBox(False,0)

           window.set_default_size(200,140)
           window.set_title("Fitted Ligands")
           inside_vbox.set_border_width(2)

           window.add(outside_vbox)
           outside_vbox.pack_start(scrolled_win, True, True, 0) # expand fill padding

           scrolled_win.add_with_viewport(inside_vbox)
           scrolled_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)

           # (format #t "debug:: passed-molecules ~s~%" passed-molecules)
           # ((("Fitted ligand #9" 68.4 11.9 4.6) 21)
           #  (("Fitted ligand #8" 68.3 12.8 8.1) 20))

           for molecule_numbers in passed_molecules:
             imol = molecule_numbers
             name = molecule_name(imol)
             button = gtk.Button(str(name))
             inside_vbox.pack_start(button, False, False, 1)
             button.connect("clicked", centre_on_mol, imol, name)

           outside_vbox.set_border_width(6)
           ok_button = gtk.Button("OK")
           outside_vbox.pack_start(ok_button,False,False,0)
           ok_button.connect_object("clicked",delete_event,window)
           window.connect("delete_event", delete_event)

           window.show_all()

        else:
           # no matching molecules
           add_status_bar_text("No matching molecules!")

    except:
        print "BL WARNING:: no pygtk2. This function doesnt work!"

# This totally ignores insertion codes.  A clever algorithm would
# need a re-write, I think.  Well, we'd have at this end a function
# that took a chain_id res_no_1 ins_code_1 res_no_2 ins_code_2 
# 
# And refine-zone would need to be re-written too, of course.  So
# let's save that for a rainy day (days... (weeks)).
# 
# BL says:: this seems to ignore backup!
def refine_active_residue_generic(side_residue_offset):

    active_atom = active_residue()

    if not active_atom:
       print "No active atom"
    else:
       imol       = active_atom[0]
       chain_id   = active_atom[1]
       res_no     = active_atom[2]
       ins_code   = active_atom[3]
       atom_name  = active_atom[4]
       alt_conf   = active_atom[5]
    
       print "active-atom:", active_atom
       imol_map = imol_refinement_map()
       replacement_state = refinement_immediate_replacement_state()
       if imol_map == -1:
          info_dialog("Oops.  Must Select Map to fit to!")
       else:
          set_refinement_immediate_replacement(1)
          refine_zone(imol, chain_id, res_no - side_residue_offset,
                                      res_no + side_residue_offset,
                                      alt_conf)
          accept_regularizement()
       if replacement_state == 0:
          set_refinement_immediate_replacement(0)

# Function for keybinding. Speaks for itself
def refine_active_residue():
    refine_active_residue_generic(0)

# And another one
def refine_active_residue_triple():
    refine_active_residue_generic(1)


# For just one (this) residue, side-residue-offset is 0.
# 
def manual_refine_residues(side_residue_offset):

    active_atom = active_residue()

    if not active_atom:
       print "No active atom"
    else:
       imol       = active_atom[0]
       chain_id   = active_atom[1]
       res_no     = active_atom[2]
       ins_code   = active_atom[3]
       atom_name  = active_atom[4]
       alt_conf   = active_atom[5]

    imol_map = imol_refinement_map()

    if (imol_map == -1):
        info_dialog("Oops.  Must Select Map to fit to!")
    else:
        refine_zone(imol, chain_id,
                    res_no - side_residue_offset,
                    res_no + side_residue_offset,
                    alt_conf)

# generic spherical refinement (use_map) (or regularization, dont use map):
def sphere_refine_regularize_generic(use_map=True, radius=3, expand=False):
    from types import ListType
    active_atom = active_residue()
    if (not active_atom):
        add_status_bar_text("No active residue")
    else:
        if (use_map and not valid_map_molecule_qm(imol_refinement_map())):
            show_select_map_dialog()
        else:
            imol      = active_atom[0]
            chain_id  = active_atom[1]
            res_no    = active_atom[2]
            ins_code  = active_atom[3]
            atom_name = active_atom[4]
            alt_conf  = active_atom[5]
            centred_residue = active_atom[1:4]
            other_residues = residues_near_residue(imol, centred_residue, radius)
            all_residues = [centred_residue]
            if (type(other_residues) is ListType):
                all_residues += other_residues
            # extend?
            if expand:
                all_residues.sort()
                tmp_ls = all_residues[:]
                for res in tmp_ls:
                    before_res = res[:]
                    after_res = res[:]
                    before_res[1] = before_res[1]-1
                    after_res[1] = after_res[1]+1
                    if not before_res in all_residues:
                        all_residues.append(before_res)
                    if not after_res in all_residues:
                        all_residues.append(after_res)
                all_residues.sort()  # not needed

            print "imol: %s residues: %s" %(imol, all_residues)
            if use_map:
                refine_residues(imol, all_residues)
            else:
                regularize_residues(imol, all_residues)
        
# Sphere refinement (around radius)
#
def sphere_refine(radius=4.5, expand=False):
    sphere_refine_regularize_generic(True, radius, expand)

def sphere_refine_plus(radius=4.5):
    sphere_refine(radius, True)

# Sphere regularization (as above)
def sphere_regularize(radius=4.5, expand=False):
    sphere_refine_regularize_generic(False, radius, expand)

def sphere_regularize_plus(radius=4.5):
    sphere_regularize(radius, True)


# Pepflip the active residue - needs a key binding
#
def pepflip_active_residue():
    active_atom = active_residue()
    if not active_atom:
       print "No active atom"
    else:
       imol       = active_atom[0]
       chain_id   = active_atom[1]
       res_no     = active_atom[2]
       ins_code   = active_atom[3]
       atom_name  = active_atom[4]
       alt_conf   = active_atom[5]

    pepflip(imol, chain_id, res_no, ins_code, alt_conf)
    

# Another cool function that needs a key binding
#
def auto_fit_rotamer_active_residue():

    active_atom = active_residue()

    if not active_atom:
       print "No active atom"
    else:
       imol       = active_atom[0]
       chain_id   = active_atom[1]
       res_no     = active_atom[2]
       ins_code   = active_atom[3]
       atom_name  = active_atom[4]
       alt_conf   = active_atom[5]
   
       print "active-atom:", active_atom
       imol_map = imol_refinement_map()
       replacement_state = refinement_immediate_replacement_state()
       if imol_map == -1:
          info_dialog("Oops.  Must Select Map to fit to!")
       else:
          auto_fit_best_rotamer(res_no, alt_conf, ins_code, chain_id, imol, imol_map, 1, 0.1)

# Restrain the atoms in imol (in give range selection) to
# corresponding atoms in imol_ref.
# 
# atom_sel_type is either 'all' 'main-chain' or 'ca'
#
def add_extra_restraints_to_other_molecule(imol, chain_id,
                                           resno_range_start, resno_range_end,
                                           atom_sel_type, imol_ref):

    for res_no in range(resno_range_start, resno_range_end):
        pass # guess should do seomething?!?!

def add_extra_start_pos_restraints(imol, residue_spec, esd):

    ri = residue_info(imol,
                      residue_spec2chain_id(residue_spec),
                      residue_spec2res_no(residue_spec),
                      residue_spec2ins_code(residue_spec))
    for atom_info in ri:
        atom_name = residue_atom2atom_name(atom_info)
        alt_conf  = residue_atom2alt_conf(atom_info)
        add_extra_start_pos_restraint(imol,
                                      residue_spec2chain_id(residue_spec),
                                      residue_spec2res_no(residue_spec),
                                      residue_spec2ins_code(residue_spec),
                                      atom_name, alt_conf, esd)

        
        pass # fill me
