/*

    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_cml.hpp>

extern finestra_pr* MainWindow;

extern Preferences  __pref;





immagine_cml::immagine_cml()
  :immagine()

{

}


immagine_cml::immagine_cml(string path)
  :immagine(path)
{

}

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

}

immagine_cml::~immagine_cml(){


}


void immagine_cml::filebist(string path){
  reset_all();
  _filebist=path;

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

}


void immagine_cml::start(){
  setlocale(LC_ALL,"C");
  FILE* inp=fopen(_filebist.c_str(),"r");
  const int buffsize=8192;
  char buff[buffsize];
  
  XML_Parser p = XML_ParserCreate(NULL);
  if (! p) {
    fl_alert( _("Couldn't allocate memory for parser"));
    XML_ParserFree(p);
    return;
  }

  XML_SetElementHandler(p, start_tags, end_tags);
  XML_SetCharacterDataHandler(p,text_hand);


  for (;;) {
    int done;
    int len;

    len = fread(buff, 1, buffsize, inp);
    if (ferror(inp)) {
      fl_alert( _("Read error") );
      return;
    }
    done = feof(inp);

    if (XML_Parse(p, buff, len, done) == XML_STATUS_ERROR) {
      ostringstream eout;
      eout <<"cml: parse error at line" 
	   << XML_GetCurrentLineNumber(p) 
	   << "\n" 
	   << XML_ErrorString(XML_GetErrorCode(p));
      _error.push_back(eout.str());
      return;
    }

    if (done)
      break;
  }
  XML_ParserFree(p);
  
  
  for(unsigned int i=0;i<_gruppi_static.size();i++){
    _gruppi.push_back(_gruppi_static[i]);
  }
#ifdef DEBUG
  dump_tmp_atom();
#endif

  _gruppi_static.clear();

  //  _tag_static.clear();

  _genitore_attrb_cml_static.clear();

  _atomref_static.first=-1;
  _atomref_static.second=-1;
  _bnd_order_static=-1;
  _id_leg_fallback=0;





  setlocale(LC_ALL,"");
}


void immagine_cml::start_tags(void *userData,
			 const XML_Char *name,
			 const XML_Char **atts){

  
  map<string,string> mapa=costruct_map_attrb(atts);

  if(string(MOLECULE_CML)==name){
    int id_gruppo= create_hash(mapa[ID_CML]);
    gruppo tmp;
    tmp.id_gruppo(id_gruppo);
    _gruppi_static.push_back(tmp);
  }else if(string(ATOM_CML)==name){
    _genitore_cml_static=(_tag_static.top()).first;
    _genitore_attrb_cml_static=(_tag_static.top()).second;
    atomo tmp;
    int id_atomo= create_hash(mapa[ID_CML]);
    tmp.id(id_atomo);
    (_gruppi_static.back()).add_atomo(tmp);

  }else if(string(STRING_ARRAY)==name){

  }else if(string(FLOAT_ARRAY)==name){
    
  }else if(string(STRING_CML)==name){
    _genitore_cml_static=(_tag_static.top()).first;
    _genitore_attrb_cml_static=(_tag_static.top()).second;
  }else if(string(COORDINATE2_CML)==name) {
    //<coordinate2 builtin="xy2">245.301 289.5</coordinate2>
  }else if(name==string(BOND_CML)){
    //int leg_id=create_hash(mapa[ID_CML]);
    //string delim=" ";
    if(mapa.count(ATOMREFS_CML)){
      /*
      string_tokenizer tok(mapa[ATOMREFS_CML],delim);
      int atomid1=create_hash(tok.next_token());
      int atomid2=create_hash(tok.next_token());
      atomo* adding1=(_gruppi_static.back()).find_atomo_id(atomid1);
      atomo* adding2=(_gruppi_static.back()).find_atomo_id(atomid2);
      
// 	void atomo::aggiungi_legame(int	nw_leg_id_atomo, int nw_leg_id_legame,
// 			    int nw_leg_tipo_legame,int nwcr,
// 			    int nwcg , int nwcb){
      
      adding1->aggiungi_legame(atomid2,leg_id,0,0,0,0);
      adding1->costruisci_arrivati();
      adding2->aggiungi_legame(atomid1,leg_id,0,0,0,0);
      adding2->costruisci_arrivati();
      */
    }
  }


  pair< string, map<string,string> > tmp;
  tmp.first=string(name);
  tmp.second=mapa;
  _tag_static.push(tmp);

}


void immagine_cml::end_tags(void *userData,
			const XML_Char *s){

  map<string,string> mapa=_tag_static.top().second;
  string name=_tag_static.top().first;

  if(s==string(STRING_CML)){

  }else if(string(COORDINATE2_CML)==s){


  }else if(s==string(INTEGER_CML)){


  }else if(s==string(FLOAT_CML)){
   
  }else if(s==string(BOND_CML)){
    int leg_id=_id_leg_fallback;
    if(mapa.count(ID_CML)){
      int leg_id=create_hash(mapa[ID_CML]);
      _id_leg_fallback=leg_id;
    }
    //superbug!!! leg_id e' sempre lo stesso!!!
    cout << "xxxx " << leg_id << endl;

    atomo* adding1=(_gruppi_static.back()).find_atomo_id(_atomref_static.first);
    atomo* adding2=(_gruppi_static.back()).find_atomo_id(_atomref_static.second);
   
    //cout << "order: " << adding1<< " " << adding2 << " " << _bnd_order_static <<endl;
    /*
      void atomo::aggiungi_legame(int	nw_leg_id_atomo, int nw_leg_id_legame,
      int nw_leg_tipo_legame,int nwcr,
      int nwcg , int nwcb){
    */
    adding1->aggiungi_legame(adding2->id(),leg_id,_bnd_order_static,0,0,0);
    adding1->costruisci_arrivati();
    adding2->aggiungi_legame(adding1->id(),leg_id,_bnd_order_static,0,0,0);
    adding2->costruisci_arrivati();

    /*
    for(unsigned int ctatm=0;ctatm<2;ctatm++){
      vector<legame>::iterator primo=att_atm[ctatm]->primo_leg();
      vector<legame>::iterator ultimo=att_atm[ctatm]->ultimo_leg();
      while(primo!=ultimo){
	if((*primo).id_atomo()==att_atm[(ctatm+1)%2]->id()){
	  att_atm[ctatm]->modifica_legame((*primo).id_atomo(),
					  (*primo).id_atomo(),
					  (*primo).id_legame(),
					  _bnd_order_static,
					  (*primo).cr(), 
					  (*primo).cg(), 
					  (*primo).cb());
	      
	}

	primo++;
      }
    }
    */

    _id_leg_fallback++;
    _atomref_static.first=-1;
    _atomref_static.second=-1;
    _bnd_order_static=-1;
    

    _tag_static.pop();
  }else if(s==string(STRING_ARRAY)){
    _tag_static.pop();
  }else if(s==string(FLOAT_ARRAY)){
    _tag_static.pop();
  }else{
    _tag_static.pop();
  }
}


void immagine_cml::text_hand(void *userData,
			 const XML_Char *s,
			 int len){
  string text;
  for(unsigned int i=0;i<static_cast<unsigned int>(len);i++){
    text+=s[i];
  }
  string name=(_tag_static.top()).first;
  map<string,string> mapa = (_tag_static.top()).second;

  if(name==string(STRING_CML)){
    //string non puo' avere figli
    _tag_static.pop();
    string name_p = (_tag_static.top()).first;

    map<string,string> mapa_p = (_tag_static.top()).second;

    if(name_p==string(ATOM_CML)){
      //il genitore e' un atomo
      atomo* no_etic=(_gruppi_static.back()).
	find_atomo_id(
		      create_hash(
				  mapa_p[ID_CML])
		      );
      if(mapa[BUILTIN_CML]==ELEMENT_TYPE_CML){
	etichetta tmp;
	if(text=="" || 
	   text==" " || 
	   text=="c" || 
	   text=="C"){
	  text="#";
	}
	tmp.aggiungi(text,ET_STR);
	
	no_etic->etich(tmp);

      }else {
	//altri stringbuiltin residue ecc... da fare
      }
    }else if(name_p==string(BOND_CML)){
      //il genitore e' un legame
      /*
      map<string,string>::iterator primo=mapa.begin();
      map<string,string>::iterator fin=mapa.end();
      
      while(primo!=fin){
	cout << (*primo).first << " " << (*primo).second << " " << mapa_p.count(ATOMREF_CML) << endl;
	primo++;
      }
      */
      if(mapa_p.count(ATOMREFS_CML)){
	//cout << "atomref" <<endl;
	string delim=" ";
	string_tokenizer tok(mapa_p[ATOMREFS_CML],delim);
	int atomid1=create_hash(tok.next_token());
	int atomid2=create_hash(tok.next_token());
	_atomref_static.first=atomid1;
	_atomref_static.second=atomid2;

	
      }else{
	//non c'e' atomrefs
	if(mapa[BUILTIN_CML]==ATOMREF_CML){
	  //primo atomref figlio di un aggiungere
	  if(_atomref_static.first==-1){
	    _atomref_static.first=create_hash(text);
	  }else{
	    _atomref_static.second=create_hash(text);
	  }

	}

	
      }
      

      

      if(mapa[BUILTIN_CML]==(ORDER_CML)){
	_bnd_order_static=conv_order_cml(text);
      }
            
    }else{
     
      // altri tipi builtin delle stringhe non figli di atomo o di bond
    }

  }else if(string(COORDINATE2_CML)==name){
    //COORDINATE2_CML non puo' avere figli
    _tag_static.pop();
    string name_p = (_tag_static.top()).first;
    map<string,string> mapa_p = (_tag_static.top()).second;
    if(name_p==string(ATOM_CML)){
      //il genitore e' un atomo
      atomo* no_etic=(_gruppi_static.back()).
	find_atomo_id(
		      create_hash(
				  mapa_p[ID_CML])
		      );
      if(mapa[BUILTIN_CML]==XY2_CML){
	string delim=" ";
	string_tokenizer tok(text,delim);
	float pos=strtof(tok.next_token().c_str(),NULL);
	no_etic->pos_x(pos);
	pos=strtof(tok.next_token().c_str(),NULL);
	no_etic->pos_y(pos);
      }
    }
  }else if(name==string(INTEGER_CML)){
    _tag_static.pop();
    string name_p = (_tag_static.top()).first;
    map<string,string> mapa_p = (_tag_static.top()).second;
    if(name_p==string(ATOM_CML)){
      //il genitore e' un atomo
      atomo* no_etic=(_gruppi_static.back()).
	find_atomo_id(
		      create_hash(
				  mapa_p[ID_CML])
		      );
      if(mapa[BUILTIN_CML]==FORMAL_CHARGE_CML){
	float charge=strtof(text.c_str(),NULL);
	no_etic->cariche(static_cast<int>(charge));
      }
    }
  }else if(name==string(FLOAT_CML)){
    _tag_static.pop();
    string name_p = (_tag_static.top()).first;
    //    cout << "textfloat"<< text <<" " << name_p <<endl;
    map<string,string> mapa_p = (_tag_static.top()).second;
    if(name_p==string(ATOM_CML)){
      //il genitore e' un atomo
      atomo* no_etic=(_gruppi_static.back()).
	find_atomo_id(
		      create_hash(
				  mapa_p[ID_CML])
		      );
      
      if(mapa[BUILTIN_CML]==X2_CML){
	//cout << "x2 " << text <<endl;
	float pos=strtof(text.c_str(),NULL);
	no_etic->pos_x(pos);
      }else if(mapa[BUILTIN_CML]==Y2_CML){
	//cout << "y2 " << text <<endl;
	float pos=strtof(text.c_str(),NULL);
	no_etic->pos_y(pos);
      }
    }

  }else  if(name==string(STRING_ARRAY)){
    string name_p = (_tag_static.top()).first;
    map<string,string> mapa_p = (_tag_static.top()).second;
    if(mapa[BUILTIN_CML]==ATOMARRAY_ID_CML){
      //cout << "string_array"<< text <<" " << name_p <<endl;
      string delim=" ";
      string_tokenizer tok(text,delim);
      while(!tok){
	atomo* tmp=new atomo;
	string id_atm=tok.next_token();
	int id_atomo= create_hash(id_atm);
	//cout << "rrrr " << "creo id " << id_atm << " " <<  id_atomo << endl;
	tmp->id(id_atomo);
	//cout << "rrrrr" << tmp->id() << endl;
	(_gruppi_static.back()).add_atomo(*tmp,false);
	delete tmp;
      }
    }else if(mapa[BUILTIN_CML]==ELEMENT_TYPE_CML){
      vector<atomo>::iterator primo=(_gruppi_static.back()).iniz_atom();
      vector<atomo>::iterator ultimo=(_gruppi_static.back()).fin_atom();
      string delim=" ";
      string_tokenizer tok(text,delim);
      while(!tok && primo!=ultimo){
	etichetta tmp;
	string resid=tok.next_token();
	if(resid=="" || 
	   resid==" " || 
	   resid=="c" || 
	   resid=="C"){
	  text="#";
	}
	tmp.aggiungi(resid,ET_STR);
	//cout << "rrrr aggiungo etichetta:" << resid << " " << (*primo).id() << endl;
	(*primo).etich(tmp);
	primo++;
      }
    }else if(mapa[BUILTIN_CML]==ORDER_CML){
      if(_atomref_array_1_static!="" &&
	 _atomref_array_2_static!=""){
	/*
	cout << "rrr" << _atomref_array_1_static << "----" << _atomref_array_2_static << endl;
	cout << "rrrr" << text << endl;
	*/
      	string delim=" ";
	string_tokenizer tok_1(_atomref_array_1_static,delim);
	string_tokenizer tok_2(_atomref_array_2_static,delim);
	string_tokenizer tok_order(text,delim);
	if(tok_1.count()==tok_2.count()){
	  while(!tok_1){ 
	    /*
	    time_t t_now=time(0);
	    struct tm* ora=localtime(&t_now);
	    */
	    int leg_id=_id_leg_fallback;//create_hash(asctime(ora));

	    string id_1=tok_1.next_token();
	    string id_2=tok_2.next_token();
	    int order_bond=conv_order_cml(tok_order.next_token());
	    atomo* adding1=(_gruppi_static.back()).find_atomo_id(create_hash(id_1));
	    atomo* adding2=(_gruppi_static.back()).find_atomo_id(create_hash(id_2));
	    /*
	    cout << "rrrrr" << "creo legame " << id_1 << "(" << create_hash(id_1) << ")" 
		 << "->" << id_2 << "(" << create_hash(id_2) << ")" << endl;
	    */
	    /*
	      void atomo::aggiungi_legame(int	nw_leg_id_atomo, int nw_leg_id_legame,
	      int nw_leg_tipo_legame,int nwcr,
	      int nwcg , int nwcb){
	    */
	    
	    
	    adding1->aggiungi_legame(adding2->id(),leg_id,order_bond,0,0,0);
	    adding1->costruisci_arrivati();
	    adding2->aggiungi_legame(adding1->id(),leg_id,order_bond,0,0,0);
	    adding2->costruisci_arrivati();
	    _id_leg_fallback++;
	  }
	  
	}
	
	_atomref_array_1_static="";
	_atomref_array_2_static="";
	(_gruppi_static.back()).ordina();
      }


    }else if(mapa[BUILTIN_CML]==ATOMREF_CML){
      if(_atomref_array_1_static==""){
	_atomref_array_1_static=text;
      }else{
	_atomref_array_2_static=text;
      }
    }


  }else if(name==string(FLOAT_ARRAY)){

    if(mapa[BUILTIN_CML]==X2_CML ||
       mapa[BUILTIN_CML]==Y2_CML){

      vector<atomo>::iterator primo=(_gruppi_static.back()).iniz_atom();
      vector<atomo>::iterator ultimo=(_gruppi_static.back()).fin_atom();
      string delim=" ";
      string_tokenizer tok(text,delim);
      while(!tok && primo!=ultimo){
	string pos_str=tok.next_token();
	float pos=strtof(pos_str.c_str(),NULL);

	if(mapa[BUILTIN_CML]==X2_CML){
	  (*primo).pos_x(pos);
	}else{
	  (*primo).pos_y(pos);
	}
	primo++;
      }
    }
  }





//   if(_genitore_cml_static==string(ATOM_CML)){
//     //il genitore e' un atomo
//     atomo* no_etic=(_gruppi_static.back()).
//       find_atomo_id(
// 		    create_hash(
// 				_genitore_attrb_cml_static[ID_CML])
// 		    );
//       if(string(STRING_CML)==name){
// 	if(mapa[BUILTIN_CML]==ELEMENT_TYPE_CML){
// 	  etichetta tmp;
// 	  tmp.aggiungi(text,ET_STR);
	  
// 	  no_etic->etich(tmp);
// 	}else {
// 	  //altri stringbuiltin residue ecc... da fare
// 	}
//       }else if(string(COORDINATE2_CML)==name){
// 	if(mapa[BUILTIN_CML]==XY2_CML){
// 	  string delim=" ";
// 	  string_tokenizer tok(text,delim);
// 	  float pos=strtof(tok.next_token().c_str(),NULL);
// 	  no_etic->pos_x(pos);
// 	  pos=strtof(tok.next_token().c_str(),NULL);
// 	  no_etic->pos_y(pos);
// 	}
//       }
//   }
  

}


vector<gruppo> immagine_cml::_gruppi_static;


map<string,string> immagine_cml::costruct_map_attrb(const XML_Char **atts){
  map<string, string> risul;
  unsigned int i=0;
  while(atts[i]!=NULL){
    pair<string,string> p;
    p.first=string(atts[i]);
    p.second=string(atts[i+1]);
    risul.insert(p);
    i+=2;
  }
  return risul;

}


int immagine_cml::conv_order_cml(string order_cml){
  int order=static_cast<int>(strtof(order_cml.c_str(),NULL));  

  switch (order){
  case 1:
  case 2:
  case 3:
    return order - 1;
  default:
    return 1;
  }


}



stack< pair< string, map<string,string> > > immagine_cml::_tag_static;

string immagine_cml::_genitore_cml_static;

map<string,string> immagine_cml::_genitore_attrb_cml_static;

pair<int,int> immagine_cml::_atomref_static(-1,-1);

int         immagine_cml::_bnd_order_static;

string  immagine_cml::_atomref_array_1_static;
string  immagine_cml::_atomref_array_2_static;


int immagine_cml::_id_leg_fallback=0;
