/* src/get-monomer.cc
 * 
 * Copyright 2010 by the University of Oxford
 * Copyright 2015 by Medical Research Council
 * Author: Paul Emsley
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License and
 * the GNU Lesser General Public License along with this program; if not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA, 02110-1301, USA.
 */


#if defined (USE_PYTHON)
#include "Python.h"  // before system includes to stop "POSIX_C_SOURCE" redefined problems
#endif

#include "utils/coot-utils.hh"
#include "graphics-info.h"
#include "cc-interface-scripting.hh"

#include "c-interface.h" // for is_valid_model_molecule()
#include "c-interface-gtk-widgets.h"
#include "cc-interface.hh" // for add_to_history()

#include "get-monomer.hh"

#include "utils/logging.hh"
extern logging logger;


// return a new molecule number
int get_monomer_molecule_by_network_and_dict_gen(const std::string &text) {

   int imol = -1;

   std::string function_name = "get-pdbe-cif-for-comp-id";
   std::vector<coot::command_arg_t> args;
   args.push_back(coot::util::single_quote(text));
   coot::command_arg_t retval = coot::scripting_function(function_name, args);
   if (retval.type == coot::command_arg_t::STRING) {
      std::string file_name = retval.s;
      args.clear();
      args.push_back(coot::util::single_quote(text));
      args.push_back(coot::util::single_quote(file_name));
      retval = coot::scripting_function("generate-molecule-from-mmcif", args);
      if (retval.type == coot::command_arg_t::INT) {
	 imol = retval.type;
      }
   }
   return imol;
} 


// Return the new molecule number, or else a negative error code.
//
int get_monomer(const std::string &comp_id_in) {

   int imol = -1;

   std::string comp_id = comp_id_in;

   // first check if three_letter_code is valid, i.e. not empty
   if (comp_id.empty())
     return imol;
   // fast
   imol = get_monomer_from_dictionary(comp_id, 1); // idealized

   logger.log(log_t::DEBUG, logging::function_name_t(__FUNCTION__),
	      "get_monomer_from_dictionary() returned imol", imol);

   if (is_valid_model_molecule(imol)) {
      return imol;
   } else {
      // std::cout << "INFO:: get_monomer(): trying non-idealized: " << comp_id_in << std::endl;
      logger.log(log_t::INFO, logging::function_name_t(__FUNCTION__),
		 "trying non-idealized", comp_id_in);
      imol = get_monomer_from_dictionary(comp_id, 0); // non-idealized
      // std::cout << "INFO:: get_monomer(): got imol " << imol << std::endl;
      logger.log(log_t::INFO, logging::function_name_t(__FUNCTION__), "got imol", imol);
      if (is_valid_model_molecule(imol)) {
	 return imol;
      } else {
	 std::vector<std::string> v = {comp_id_in};
	 graphics_info_t::show_missing_refinement_residues_dialog(v, true);
      }
   }

   if (coot::util::is_standard_residue_name(comp_id_in)) {

      molecule_class_info_t molci;
      mmdb::Residue *std_res = molci.get_standard_residue_instance(comp_id_in);

      // std::cout << "molci.get_standard_residue_instance()  returned " << std_res << std::endl;

      if (std_res == NULL) {
	 // std::cout << "WARNING:: Can't find standard residue for " << comp_id_in << "\n";
	 logger.log(log_t::WARNING, "Can't find standard residue for", comp_id_in);
      } else {
	 // happy path
	 graphics_info_t g;
	 std_res->seqNum = 1;
	 mmdb::Manager *std_res_mol = coot::util::create_mmdbmanager_from_residue(std_res);
	 atom_selection_container_t asc = make_asc(std_res_mol);
	 imol = g.create_molecule();
	 g.molecules[imol].install_model(imol, asc, g.Geom_p(), comp_id_in, true);
	 move_molecule_to_screen_centre_internal(imol);
	 graphics_draw();
      }
   }

   std::vector<std::string> command_strings;
   command_strings.push_back("get-monomer");
   command_strings.push_back(coot::util::single_quote(comp_id));
   add_to_history(command_strings);

   return imol;
}

//! get the monomer for the given
int get_monomer_for_molecule(const std::string &comp_id, int imol) {

   graphics_info_t g;

   bool idealised_flag = true;
   mmdb::Manager *mol = g.Geom_p()->mol_from_dictionary(comp_id, imol, idealised_flag);
   if (mol) {
      imol = graphics_info_t::create_molecule();
      atom_selection_container_t asc = make_asc(mol);
      std::string name = comp_id;
      name += "_from_dict";
      graphics_info_t::molecules[imol].install_model(imol, asc, g.Geom_p(), name, 1);
      move_molecule_to_screen_centre_internal(imol);
      graphics_draw();
   }
   return imol;
}



/* Use the protein geometry dictionary to retrieve a set of
   coordinates quickly.  There are no restraints from this method
   though. */
int get_monomer_from_dictionary(const std::string &comp_id,
				int idealised_flag) {

   auto debug_mol = [] (mmdb::Manager *mol) {
      for(int imod = 1; imod<=mol->GetNumberOfModels(); imod++) {
         mmdb::Model *model_p = mol->GetModel(imod);
         if (model_p) {
            int n_chains = model_p->GetNumberOfChains();
            for (int ichain=0; ichain<n_chains; ichain++) {
               mmdb::Chain *chain_p = model_p->GetChain(ichain);
               int n_res = chain_p->GetNumberOfResidues();
               for (int ires=0; ires<n_res; ires++) {
                  mmdb::Residue *residue_p = chain_p->GetResidue(ires);
                  if (residue_p) {
                     int n_atoms = residue_p->GetNumberOfAtoms();
                     for (int iat=0; iat<n_atoms; iat++) {
                        mmdb::Atom *at = residue_p->GetAtom(iat);
                        if (! at->isTer()) {
                           std::cout << "get_monomer_from_dictionary(): atom " << iat << " " << at->name
                                     << " at " << at->x << " "  << at->y << " " << at->z
                                     << std::endl;
                        }
                     }
                  }
               }
            }
         }
      }
   };

   int istat = -1; // unfound molecule
   graphics_info_t g;

   int imol_enc = coot::protein_geometry::IMOL_ENC_ANY;
   mmdb::Manager *mol = g.Geom_p()->mol_from_dictionary(comp_id, imol_enc, idealised_flag);

   if (false)
      std::cout << "debug:: in get_monomer_from_dictionary() mol: " << mol
                << " using idealised_flag " << idealised_flag << std::endl;

   // debug_mol(mol);

   if (mol) {
      int imol = graphics_info_t::create_molecule();
      atom_selection_container_t asc = make_asc(mol);
      std::string name = comp_id;
      name += "_from_dict";
      std::cout << "debug:: get_monomer_from_dictionary() installing " << name << " into model " << imol << std::endl;
      graphics_info_t::molecules[imol].install_model(imol, asc, g.Geom_p(), name, 1);
      move_molecule_to_screen_centre_internal(imol);
      graphics_draw();
      istat = imol;
   } else {
      std::cout << "WARNING:: Null mol from mol_from_dictionary() with comp_id " << comp_id << " "
		<< idealised_flag << std::endl;
   }
   return istat;
}


int get_monomer_for_molecule_by_index(int dict_idx, int imol_enc) {

   graphics_info_t g;
   int imol = -1;

   int idealised_flag = true;
   mmdb::Manager *mol = g.Geom_p()->mol_from_dictionary(dict_idx, imol_enc, idealised_flag);
   if (mol) {
      imol = graphics_info_t::create_molecule();
      atom_selection_container_t asc = make_asc(mol);
      std::string name;
      int imod = 1;
      mmdb::Model *model_p = mol->GetModel(imod);
      if (! model_p) {
	 std::cout << "Null model" << std::endl;
      } else { 
	 mmdb::Chain *chain_p;
	 int n_chains = model_p->GetNumberOfChains();
	 for (int ichain=0; ichain<n_chains; ichain++) {
	    chain_p = model_p->GetChain(ichain);
	    if (! chain_p) {
	       std::cout << "Null chain" << std::endl;
	    } else { 
	       int nres = chain_p->GetNumberOfResidues();
	       mmdb::Residue *residue_p;
	       for (int ires=0; ires<nres; ires++) { 
		  residue_p = chain_p->GetResidue(ires);
		  name = residue_p->GetResName();
		  break;
	       }
	    }
	    if (! name.empty())
	       break;
	 }
      }
      
      name += "_from_dict";
      graphics_info_t::molecules[imol].install_model(imol, asc, g.Geom_p(), name, 1);
      move_molecule_to_screen_centre_internal(imol);
      graphics_draw();
   }

   return imol;

}
