/*

    Bist: a chemical drawing tool
    Copyright (C) 2008 Valerio Benfante

    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/>.
*/
#include <global.hpp>
#include <cstdio>


#include <cairo/cairo.h>
#include <cairo-ps.h>
#include <pango/pangocairo.h>
#include <glib.h>
#include <cairo_t_singleton.hpp>


#include <string_tokenizer.hpp>

#include <FL/Fl_Pixmap.H>
#include <FL/fl_draw.H>
#include <FL/fl_ask.H>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>

#include <interfacce.hpp>
#include <legame.hpp>
#include <etichetta.hpp>
#include <multiline_label.hpp>
#include <multifont_label.hpp>
#include <paragraph_text.hpp>
#include <atomo.hpp>

#include <procedura.hpp>
#include <gruppo.hpp>

#include <immagine.hpp>
#include <bist_plugin.hpp>
#include <mol_canvas.hpp>
#include <finestra_pr.hpp>

#include <atom_prop.hpp>
#include <bond_prop.hpp>
#include <arrow_prop.hpp>
#include <bezier_prop.hpp>
#include <arc_prop.hpp>
#include <util.hpp>

#include <prefs.hpp>

#include <expat.h>

#include <immagine_mol.hpp>

extern finestra_pr* MainWindow;

extern Preferences  __pref;





immagine_mol::immagine_mol()
  :immagine(),
   _atoms_number(0)

{

}


immagine_mol::immagine_mol(string path)
  :immagine(path),
   _atoms_number(0)
{

}

immagine_mol::immagine_mol(const immagine_mol& altra)
  :immagine::immagine(altra)
{

}

immagine_mol::~immagine_mol(){


}


void immagine_mol::filebist(string path){
  struct stat attrb;
  stat(path.c_str(),&attrb);
  if(!S_ISDIR(attrb.st_mode)){
    reset_all();
    _filebist=path;

    if(path==""){
      return;
    }

    ifstream inf(_filebist.c_str(),ios::in);

    if(!inf){
      _has_error=true;
      string error="Error: file " +  _filebist + " do not exist.";
      _error.push_back(error);
    }else{
      while(!inf.eof()){
	string linea;
	getline(inf,linea,'\n');
	_molecule+=linea + std::string("\n");
      }

    }

    inf.close();

  }
}


void immagine_mol::start(){
  setlocale(LC_ALL,"C");

  parse_file_mol();
  
#ifdef DEBUG
  dump_tmp_atom();
#endif
  
  setlocale(LC_ALL,"");
}



void immagine_mol::start_from_string(std::string mol){
  _molecule=mol;
  start();

}


void immagine_mol::parse_file_mol(){
  skip_header_lines();
  
  atoms_number();
  
  gruppo the_g;
  _gruppi.push_back(the_g);

  atom_block(_gruppi.back());

  connection_table(_gruppi.back());

  m_charge(_gruppi.back());


  gruppo* bk=&_gruppi.back();

  vector<atomo>::iterator ina=bk->iniz_atom();
  vector<atomo>::iterator enda=bk->fin_atom();

  while(ina!=enda){
    atomo* atm=bk->find_atomo_id(ina->id());
    atm->costruisci_arrivati();
    ina++;
  }


  //std::cerr << "HAS ERROR: " << _has_error << std::endl;
  
}

void immagine_mol::skip_header_lines(){
  skip_junk_from_line();
  
  skip_junk_from_line();
  skip_junk_from_line();
  

}


void immagine_mol::skip_junk_from_line(){
  //std::cerr << "SKIP" << std::endl;
  string::size_type i=_punt_mol;

  try{
    while(_molecule.at(i)!='\n'){
      //std::cerr <<  _molecule.at(i);
      i++;
    }
  }catch (out_of_range e){
    _punt_mol=i+1;
    _has_error=true;
    //std::cerr << "EX"<< std::endl; 
  }

  //std::cerr << "<-" << std::endl;
  _punt_mol=i+1;
  
  
  
}

void immagine_mol::connection_table(gruppo& the_group){
  try{
    while(_molecule.at(_punt_mol)!=IMMAGINE_MOL_MOD_IDENTIFIER){
      bond_block(the_group);
    }

  }catch (out_of_range e){
    _has_error=true;
    //std::cerr << "EX"<< std::endl; 
  }

}

void immagine_mol::atoms_number(){
  std::string num;

  while(isspace(_molecule[_punt_mol])){
    _punt_mol++;
  }

  while(_punt_mol< _molecule.size() &&
	isdigit(_molecule[_punt_mol]) ){

    num+=_molecule[_punt_mol];
    _punt_mol++;
  }

  //std::cerr << "NUM " << num << std::endl;
  char* tail;
  int atoms=strtol(num.c_str(),&tail,0);
  _punt_mol+=num.size();
  

  if((*tail)!='\0'){
    _has_error=true;
    gen_err_miss_number(_punt_mol, num.size());
  }else{
    _atoms_number=atoms;
  }

  skip_junk_from_line();

#ifdef DEBUG
  std::cerr << __FUNCTION__ << " "
	    << "atoms number " 
	    << _atoms_number<< std::endl;
#endif
  

}
void immagine_mol::atom_block(gruppo& the_group){
  for(int i=0;i<_atoms_number;i++){
    float x=coordinate();
    float y=coordinate();
    float z=coordinate();
    std::string symbol=periodic_table_entry();
    skip_junk_from_line();
    
    atomo tmp_atom;
    etichetta tmp_etichetta;
    if(symbol=="C" || symbol=="c"){
      tmp_etichetta.aggiungi(NO_ETIC,ET_STR);
    }else{
      tmp_etichetta.aggiungi(symbol, ET_STR);
    }
    tmp_atom.id(i);
    tmp_atom.pos_x(x);
    tmp_atom.pos_y(y);
    tmp_atom.pos_z(z);
    tmp_atom.etich(tmp_etichetta);
    
    tmp_atom.aggiungi_genitore(&the_group);
    the_group.add_atomo(tmp_atom);

  }

}
float immagine_mol::coordinate(){
  char* tail;
  const char* mol_c=_molecule.c_str();
  
  float coord=strtod(mol_c + _punt_mol,&tail);
  _punt_mol+=tail - (mol_c + _punt_mol);
  

  if((*tail)!=' '){
    _has_error=true;
    gen_err_miss_number(_punt_mol , (tail - (mol_c + _punt_mol)));
    //std::cerr << "ERRORE" << std::endl;
  }else{

  }

  return coord;

}



void immagine_mol::bond_block(gruppo& the_group){
  int atom_f=node_entry()-1;
  int atom_s=node_entry()-1;
  int order=node_entry();
  atomo* atm_f=the_group.find_atomo_id(atom_f);
  atomo* atm_s=the_group.find_atomo_id(atom_s);
  
  int type_b=LEGAME_SINGOLO;

  switch(order){
  case 1:
    type_b=LEGAME_SINGOLO;
    break;

  case 2:
    type_b=LEGAME_DOPPIO;
    break;

  case 3:
    type_b=LEGAME_TRIPLO;
    break;
  default:
    type_b=LEGAME_SINGOLO;
    break;
    
  }

  if( atm_f !=0 && atm_s!=0){
    atm_f->aggiungi_legame(atm_s->id(),type_b,0,0,0);
    atm_s->aggiungi_legame(atm_f->id(),type_b,0,0,0);
  }
  skip_junk_from_line();  
}


int immagine_mol::node_entry(){
  char* tail;
  const char* mol_c=_molecule.c_str();
  
  int res=strtol(mol_c + _punt_mol,&tail,0);
  _punt_mol+=tail - (mol_c + _punt_mol);
  

  if((*tail)!=' '){
    _has_error=true;
    gen_err_miss_number(_punt_mol , (tail - (mol_c + _punt_mol)));
    //std::cerr << "ERRORE" << std::endl;
  }else{

  }

  return res;

}


int immagine_mol::numeric_entry_int(){

  char* tail;
  const char* mol_c=_molecule.c_str();
  
  int res=strtol(mol_c + _punt_mol,&tail,0);
  _punt_mol+=tail - (mol_c + _punt_mol);
  

  if((*tail)!=' ' &&
     (*tail)!='\n'
     ){
    _has_error=true;
    gen_err_miss_number(_punt_mol , (tail - (mol_c + _punt_mol)));
  }else{

  }

  return res;

}
 

int immagine_mol::numeric_entry_float(){

  char* tail;
  const char* mol_c=_molecule.c_str();
  
  float res=strtod(mol_c + _punt_mol,&tail);
  _punt_mol+=tail - (mol_c + _punt_mol);
  

  if((*tail)!=' ' &&
     (*tail)!='\n'){
    _has_error=true;
    gen_err_miss_number(_punt_mol , (tail - (mol_c + _punt_mol)));
    //std::cerr << "ERRORE" << std::endl;
  }else{

  }

  return res;

}


std::string immagine_mol::periodic_table_entry(){
  std::string res="";
  try{
    string::size_type i=_punt_mol;
    while(isspace(_molecule.at(i))){
      i++;
    }
    _punt_mol=i;
    while(!isspace(_molecule.at(i))){
      res+=_molecule.at(i);
      i++;
    }
    _punt_mol=i;
  }catch (out_of_range e){
    _has_error=true;
  }

  //std::cerr << "res: " << res << std::endl;

  return res;

}



void immagine_mol::m_charge(gruppo& the_group){
  /*
    #define IMMAGINE_MOL_M_END           "M  END"
    #define IMMAGINE_MOL_M_CHG           "M  CHG" 
    #define IMMAGINE_MOL_MOD_IDENTIFIER  'M'

   */

  std::string::size_type m_end_size=std::string(IMMAGINE_MOL_M_END).size();
  std::string::size_type m_chg_size=std::string(IMMAGINE_MOL_M_CHG).size();
  
  try{
    std::string mod="";
    std::string::size_type i=0;
    for(;i<m_chg_size;i++){
      mod+=_molecule.at(_punt_mol+i);
    }

    if(mod==std::string(IMMAGINE_MOL_M_CHG)){
      _punt_mol+=i;

      while(_molecule.at(_punt_mol)!='\n' && !_has_error){
	int atm_id=numeric_entry_int()-1;
	int atm_chg=numeric_entry_int();
	atomo* atm=the_group.find_atomo_id(atm_id);
	if(atm!=0){
	  atm->cariche(atm_chg);
	}
      }
      m_charge(the_group);
    }else{
      std::string mod_end="";
      std::string::size_type i=0;
      for(;i<m_end_size;i++){
	mod_end+=_molecule.at(_punt_mol+i);
      }
      if(mod==std::string(IMMAGINE_MOL_M_END)){
	//std::cerr << "end of parsing" << std::endl;
	
	_punt_mol+=i; //end_of_parsing;
      }else{
	skip_junk_from_line();
	m_charge(the_group);
      }
    }
  }catch (out_of_range e){
    _has_error=true;
  }

  
}
