
/*

    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 <FL/Fl_Progress.H>



#define IMMAGINE_LEGAMI_SELECTED_SZ 3


/**
 *  atomo* root=reinterpret_cast<atomo*>(data);
 * float dx=*(reinterpret_cast<float*>(data2));
 *  float dy=*(reinterpret_cast<float*>(data3));
 */

void trasl_subgroup_as_subtree(gruppo* the_group, atomo* root, atomo* start, float dx, float dy);

int appartiene_legame(atomo* da , atomo* fino, void* data, void* data2 ,void* data3);

int quale_gruppo(atomo* da , atomo* fino, void* data, void* data2 ,void* data3);

int appartiene_legame_bb(atomo* da , atomo* fino, void* data, void* data2 ,void* data3);

int appartiene_legame_prop(atomo* da , atomo* fino, void* data, void* data2 ,void* data3);


/**
 *ordina in senso decrescente secondo l'id dell'elemento
 */

bool ordina_id_decr_elem_selected(pair < int, pair<int,int> >  uno , 
				  pair < int, pair<int,int> >  altro);

/**
 *Parsa  l'input  costruendo  una  rappresentazione  della  molecola  in
 *memoria utile ad essere disegnata o modificata.
 */

class immagine: public disegnabile{

  friend class bist_plugin;

public:

  /**
   *Costruttore di default
   */

  immagine(bool preview=false);


  /**
   *Costruttore di copia
   */

  immagine(const immagine& altra);
  immagine(const immagine* altra);

  immagine& operator=(const immagine& altra);
  immagine& operator=(const immagine* altra);

  /**
   *Costruisce, l'oggetto parsando il file passato come argomento.
   */

  immagine(string path);

  virtual ~immagine();

  /**
   *\return _has_error
   **/
  bool has_error();

  /**
   *\return l'ennesimo gruppo del vettore
   */
  gruppo& ritorna_gruppo(int i);


  /**
   *\return l'ultimo gruppo del vettore
   */
  gruppo& ritorna_ultimo_gruppo();


  /**
   *trova e ritorna un puntatore al gruppo identificato da id
   *\param id l'id del gruppo da cercare
   *\return l'ultimo gruppo del vettore
   */
  gruppo* find_group_id(int id);


  std::vector<gruppo>::iterator first_gruppo();

  std::vector<gruppo>::iterator end_gruppo();



  /**
   *incolla la selezione traslandola
   *
   *\param xt entita' della traslazione lungo l'asse x
   *\param yt entita' della traslazione lungo l'asse y
   *
   */

  void paste(int xt,int yt);



  /**
   *\return l'ennesima etichetta del vettore
   */
  etichetta* ritorna_etich(int i);

  
  /**
   *\return un puntatore all'ennesima etichetta del vettore
   */
  etichetta* ritorna_etich_pointer(int i);

  /**
   *Controlla se un atomo si trova sotto al puntatore
   *
   *L'atomo viene aggiunto al _elem_selected  e ris e' un puntatore allo
   *stesso.
   *
   *\return  l'id dell'atomo  sotto  al mouse  oppure  -1 se  non si  e'
   *cliccato su nessun atomo.
   *
   */

  int prova_click_atomo(atomo** ris,int x, int y);


  /**
   *Controlla quali elementi sono sotto al puntatore
   *
   *Gli elementi cliccati vengono aggiunti a _elem_selected.
   *I legami cliccati  vengono aggiunti a _legami_selected.
   * \return true if something has been hitted fale otherwise
   *\param exists true if hitted element (atom or procedure) already exists
   */

  bool prova_click(int x, int y, bool& exists, bool erase_previous=true);
  

  /**
   *check which labels  are under mouse pointer
   *if any they will be added to _elem_selected
   * \return true if something has been hitted false otherwise
   *\param exists true if hitted label already exists
   */
  bool check_click_etichetta(int x, int y , bool& exists, bool erase_previous);


  /**
   *check which bond  is under mouse pointer
   *if any it will be added to _elem_selected
   * \return true if something has been hitted false otherwise
   *\param exists true if hitted label already exists
   */
  bool check_click_bond(int x, int y , bool& exists, bool erase_previous);



  /**
   *check if there is an element under mouse pointer <b>and</b> if is in
   *current _elem_selected vector.
   *\return if element exists in current vector
   */

  bool check_exists_in_elem_selected(int x_m,int y_m,bool& exists);



  bool check_selected_group_atom_coincidence(atomo** selected, atomo** hook);



  bool check_selected_group_bond_coincidence(atomo** selected_1, atomo** selected_2,
                                             atomo** hook_1    , atomo** hook_2);

  /**
   *check ifthere  is a control point  of a procedura  under the mouse
   *pointer, if so set the procedura for edit control point mode.
   *\return true if control poin of any procedura has been hitted
   */

  bool check_control_point_procedura_under_mouse(int x_m,int y_m);


  void translate_control_points(float dx, float dy);

  void unset_selected_control_points();

  /**
   *Controlla quale gruppo e sotto il puntatore
   *
   *Gli elementi cliccati vengono aggiunti a _elem_selected.
   *I legami cliccati  vengono aggiunti a _legami_selected.
   *\return true if something has been added
   */
  
  bool click_gruppo(int x, int y, bool erase_previous=false);

  void remove_gruppo_id(int gid);
  
  /**
   *Controlla quali elementi sono ddentro la bounding box.
   */
  void aggiungi_se_dentro_bb(float x, float y, float w , float h);

  virtual void disegna();

  virtual void ruota();

  virtual void ruota(float xpiv, float ypiv,float angl);

  virtual void trasla(float dx, float dy);

  virtual void phys_translate_selected(int xn, int yn);

  virtual void scale(float sc);

  float w();

  float visual_w();
  
  float phys_w();

  float h();

  float visual_h();
  
  float phys_h();



  /**
   *Disegna  quattro  quadratini  ai  vertici delle  bounding_box  degli
   *elementi selezionati.
   */
  void highlight_selected();


  /**
   *Trasla gli elementi selezionati
   *
   *\param xn nuova ascissa
   *\param yn nuova_ordinata
   
   */

  void trasla_selected(int xn, int yn);

   /**
   *Ruota gli elementi selezionati
   *
   *\param xpivot ascissa dell'asse di rotazione (perp. allo schermo)
   *\param ypivot ordinata       "            "               "
   *\param angl angolo di rotazione (radianti?)
   */

  void ruota_selected(float angl,int xpivot,int ypivot);


  /**
   *Ruota in 3d e proietta nel piano
   *Gli assi sono orientati secondo la regola della mano sinisra (come nelle opengl)
   *
   *\param axis true=asse x , false= asse y
   *\param angle entità della rotazione
   */
  void rotate_selected_3d(bool axis, float angle);
  


  /**
   *Scala gli elementi selezionati
   *\param fatt il fattore di scala.
   */

  void scala_selected(float fat);


  /**
   *Scale only labels of selected atoms
   *\param fatt il fattore di scala.
   */


  void scale_label_of_atom_selected(float fat);



  /**
   *shift type of selected bond
   *
   */

  void shift_type_bond_selected();


  /**
   *flip della molecola
   *
   *\param x ascissa del puntatore del mouse
   *\param y ordinata del puntatore del mouse
   *\param hor true se il flipping deve essere orizzontale false se verticale
   */

  void flip(int x,int y, bool hor=true);


  void flip_group (int group_id, bool hor=true);

  void copy_flip(int x,int y, bool hor=true);

  void copy_flip_group(int id_group , bool hor=true);



  /**
   *Cancella tutta l'immagine ma senza deallocarla, non e' un distuttore.
   */

  void reset_all();

  /**
   *Aggiunge un template 
   *
   *\param templ_file il file di template
   */

  void aggiungi_template(string templ_file);


  /**
   *carica in memoria il file di input
   */
  virtual void filebist(string path);

  /**
   *Avvia il parsing
   */
  virtual void start();


  /**
   *Stampa su console gli errori.
   */
  void print_errors();

   /**
   *Stampa su console i warning.
   */
  void print_warn();


  /**
   *elimina gli errori
   */


  void elimina_err();

  /**
   *elimina i warning
   */

  void elimina_warn();


  /**
   *elimina i gruppi selezionati
   */

  void elimina_elem_selected();

  /**
   *Aggiunge un elemento selezionato 
   *\return false if element already exists
   */

  bool aggiungi_elem_selected(int tipo, int grp,int id);


  /**
   *elimina i legami selezionati
   */
  void elimina_legami_selected();

  /**
   *Aggiunge un legame selezionato
   */

  void aggiungi_legami_selected(int gruppo, int id_atomo_app, int id_legame);

  /**
   *Cambia colore agli elementi selezionati
   */

  void cambia_colore_sel(int r, int g, int b);

  /**
   *Cambia colore all'etichetta degli atomi selezionati
   */

  void  cambia_colore_sel_etich(int r, int g, int b);


  /**
   *Elimina dalla molecola gli elementi (e i legami) selezionati.
   */

  void cancella_elementi_selected();

  /**
   *Erase and free the memori associated with _stringhe[i]
   *
   */

  void delete_etichetta_at(unsigned int i);


  /**
   *Erase and free the memori associated with _stringhe
   *\param i the iterator of the string to delete
   */

  void delete_etichetta_at(vector<etichetta*>::iterator instr);



  /**
   *Apre una finestra di dialogo e  cambia le proprieta del legame a cui
   *appertengono le coordinate date come argomento.
   */

  void cambia_prop_legami(int x, int y);

  /**
   *Apre una finestra di dialogo e  cambia le proprieta della freccia a cui
   *appertengono le coordinate date come argomento.
   */
  bool  cambia_prop_frecce(int x , int y);

  /**
   *Apre una finestra di dialogo e  cambia le proprieta della curva a cui
   *appertengono le coordinate date come argomento.
   *
   *\return true se il mouse e' sopra una curva di bezier false altrimenti
   */

  bool cambia_prop_bezier(int x , int y);

 /**
   *Apre una  finestra di dialogo  e cambia le proprieta  dell'ellisse a
   *cui appertengono le coordinate date come argomento.
   *
   *\return true se il mouse e' sopra una curva di bezier false altrimenti
   */


  bool cambia_prop_arc(int x , int y);


  bool change_prop_box(int x , int y);

  /**
   *Apre una finestra di dialogo e  cambia le proprieta dell'atomo a cui
   *appertengono le coordinate date come argomento.
   *
   *\return true se si e' cliccato su un atomo false altrimenti
   */

  bool cambia_prop_atomo(int x, int y);


  /**
   *Apre una finestra di dialogo e  cambia le proprieta dell'etichetta a cui
   *appertengono le coordinate date come argomento.
   *
   *\return true se si e' cliccato su un etichetta false altrimenti
   */

  bool cambia_prop_etichetta(int x, int y);



  /**
   *Crea ed aggiunge un nuovo gruppo vuoto e losffiunge al vettore
   *
   *\return id del nuovo gruppo creato
   */

  int create_empty_group();

  /**
   *Crea  ed aggiunge a _gruppi un  nuovo gruppo  con un  legame standard  con gli
   *atomi nelle posizioni specificate
   *
   */
    
  atomo* crea_legame_nuovo(float x1,float y1,float x2, float y2, int tipo= LEGAME_SINGOLO );


  /**
   * Crea un nuovo gruppo contenente la freccia specificata
   *
   *\param dax ascissa del punto di inizio della freccia
   *\param day ordinata del punto di inizio della freccia
   *\param ax ascissa del punto di arrivo della freccia
   *\param ay ordinata del punto di arrivo della freccia
   *
   */

  void crea_freccia_nuova(int dax, int day, int ax, int ay);


  /**
   *Crea un nuovo gruppo contenente una curva di bezier
   *
   *\param punti un  vettore di coppie ascisse ordinate,  ovvero i punti
   *di controllo della curva
   */

  void crea_bezier_nuova(vector< pair<float,float> > punti);


  /**
   *Crea una nuova ellisse partendo dall'angolo in alto a sinistra e altezza
   */


  void crea_ellisse_nuova(int xc,int yc, int w, int h);


  /**
   *create a new box
   */

  void create_new_box(int ld_x, int ld_y, int ru_x, int ru_y);


  /**
   *Aggiunge al  gruppo cui appartiene gen  un nuovo atomo  legato a gen
   *con un legame specificato da tipo e di colore standard.
   *
   *\param gen puntatore all atomo a cui aggiungere il nuovo atomo
   *\param x ascissa del nuovo atomo
   *\param y ordinata del nuovo atomo
   *\param tipo il tipo di legame
   *   
   *\return il puntatore all'atomo creato dal legame.
   *
   */

  atomo* crea_legame_nuovo(atomo* gen, float x, float y, int tipo);

  
  /**
   *Aggiunge un legame tra l'atomo st e arr che <b>devono</b> appartenere al medesimo gruppo.
   */

  void crea_legame_nuovo(atomo* st, atomo* arr, int tipo);

  /**
   *dato  un identificativo  di  gruppo  e di  atomo  valido ritorna  un
   *puntatore all'atomo identificato
   *
   */

  atomo* ritorna_atomo_ident(int gr_id, int atm_id);



  /**
   *Salva l'immagine nel formato nativo
   *
   *\param path il file da salvare
   */

  void w_immagine(string path);

  /**
   *Salva un gruppo nel formato nativo
   */
  void w_gruppo(int pos,std::ofstream& out);
 

  /**
   *Salva un'etichetta nel formato nativo
   */
  
  void w_etichetta(etichetta& sy,ofstream& out);



  /**
   *Salva un'etichetta nel formato nativo
   */
  
  void w_etichetta(etichetta* sy,ofstream& out);

  void w_multiline_label(multiline_label* sy,ofstream& out);

  void w_multifont_label(multifont_label* sy,ofstream& out);

  void w_paragraph_text(paragraph_text* sy,ofstream& out);
  /**
   *Salva un atomo nel formato nativo
   */

  void w_atomo(atomo& atm,ofstream& out);

  /**
   *Ritorna  un   puntatore  all'etichetta  sel   vettore  _stringhe  in
   *posizione at.
   */

  etichetta* ritorna_punt_etich(int at);

  /**
   *Aggiunge una nova etichetta in coda al vettore _stringhe
   */

  void aggiungi_etich(etichetta nuova);


  void aggiungi_etich(etichetta* n);


  /**
   *Add a pointer to an  etichetta in _stringhe
   */

  void add_etich(etichetta* n);



  /**
   *Add an etichetta to _stringhe
   */

  void add_etich(etichetta n);




  /**
   *Ritorna il numero di etichette nel vettore stringa
   */


  int numero_etichetta();

  /**
   *size of _string vector
   */

  int size_etich_vector();

  /**
   *size of _gruppi vector
   */
  
  int size_gruppi_vector();
  

  /**
   *Semplicemente aggiunge in coda a _grupp il gruppo added
   */

  void aggiungi_gruppo(gruppo& added);


  /**
   *\return the size of _elem_selected
   */

  unsigned int elem_selected_size();

  void PROVA_ISOMORPH(){
    std::cerr << "isomorph!!" << std::endl;
    _gruppi[0].substructure_match(_gruppi[1]);
  }
  
  void aabb_elem_selected(pair<float,float>& ld,
                          pair<float,float>& ru);



  /**
   *Aggiunge il gruppo added co ìn  tutti i suoi legami e procedure agli
   *elementi selezionati
   */

  void aggiungi_tutto_selected(gruppo added);


protected:


  /**
   *Spezza un  gruppo composto compostao da piu'  componenti connesse in
   *piu'  gruppi   contenenti  una  singola   componenete  connessa.  Le
   *procedure vengono attribuite al primo gruppo quindi attenzione!
   */

  void spezza_gruppo();



  /**
   *Cerca una procedura con lo stesso id.
   *
   *\param proc la procedura da controllare
   *\return true se trovata false altrimenti
   */

  bool find_proc(procedura* proc);


public:
  /**
   *Fa il dump su standard output di tmp_atom, utile per il debug.
   */

  void dump_tmp_atom();

protected:
  /**
   *Parsa i legami della lista di adiacenza che rappresenta i legami
   */

  void p_legami();


   /**
   *Parsa  la lista di adiacenza che rappresenta i legami
   */

  void p_lista();

  /**
   *Parsa un atomo.
   */

  void p_atomo();

  /**
   *Parsa un gruppo.
   */

  void p_gruppo();

  /**
   *Parsa le procedure
   */

  void p_procedure();


  /**
   *Parsa una stringa (ovvero una frase che non e' appartenente a nessun
   *atomo).
   */


  void p_stringhe();


  /**
   *Parsa  un'etichetta
   *\return  l'etichetta  parsata  (<b>  attenzione
   *controllare il  valore di immagine::_has_error, se  si e' verificato
   *un errore la funzione ritorna un etichetta contenente spazzatura.
   */

  etichetta p_etichetta();

  paragraph_text p_paragraph_text();
  
  multifont_label p_multifont_label();

  multiline_label p_multiline_label();

  /**
   *Parsa l'elemento dell'atomo
   */

  void p_elemento();


  /**
   *parsa la rotazione del gruppo
   */

  void p_grot(gruppo& grp);



  /**
   *Parsa  un intero.Se  il parsing  non va  a buon  fine setta  true la
   *variabile  _has_error.
   *
   *\param  num  la stringa  che rappresenta  il
   *numero.
   */

  int p_int(string num);

  /**
   *Parsa il valore di un etichetta
   *\param etic l'etichetta nella quale consevare i valori
   */
  void p_valore(etichetta& etic);


  /**
   *Trova un intero delimitato
   *
   * <p><b>controllare  sempre  la  variabile  _has_error dopo  aver  usato
   * questa funzione</b></p>
   *
   *\param  conc il delimitatore
   *
   *\return l'intero parsato
   */

  int trova_campo_etic_int(const char* conc);


 /**
   *Trova un float delimitato
   *
   * <p><b>controllare  sempre  la  variabile  _has_error dopo  aver  usato
   * questa funzione</b></p>
   *
   *\param  conc il delimitatore
   *
   *\return l'intero parsato
   */

  float trova_campo_etic_float(const char* conc);


  /**
   *Controlla la  effettiva chiusura  di un blocco:  genera un  errore e
   *setta _has_error=true  se il blocco  non e' chiuso.  (<b> Attenzione
   *questo metodo fa  avanzare il cursore nella stringa  da parsare fino
   *ad escludere il delimitatore del blocco.
   */

  void chk_chiusura_blocco();


  /**
   *Genera  un  messaggio d'errore  dovuto  al prematura  raggiungimento
   *della fine del file.
   */

  string gen_err_eof();



  /**
   *Genera  un  messaggio  d'errore   del  tipo:
   *
   *<pre>
   *
   *brano del file incriminato "  In posizione: " _punt_mol " era atteso"
   *+ missing + " trovato: " + + _molecule.substr(start,length);
   *
   *</pre>
   */

  string gen_err_miss_string(string missing,int start, int length);


  /**
   *Genera  un  messaggio  d'errore   del  tipo:
   *
   *<pre>
   *
   *brano del file incriminato "  In posizione: " _punt_mol " era atteso
   *un numero" " trovato: " + _molecule.substr(start,length);
   *
   *</pre>
   */

  string gen_err_miss_number(int start, int length);



  /**
   *Genera  un messaggio  di avvertimento  se l'elemento  parsato  ha un
   *conflitto di id con un altro.
   *
   *\param elem l'elemento che confligge
   *\param id l'ide dell'elemento che confligge
   */

  string gen_warn_id_conflict(string elem, int id);


  /**
   *In caso di errore setta a  true _has_error e aggiunge una stringa di
   *errore in _error.
   *
   *\return   la    posizione   eventuale   della    graffa   aperta   o
   *string::size_typenpos.
   */

  string::size_type trova_apri();


  /**
   *In caso di errore setta a  true _has_error e aggiunge una stringa di
   *errore in _error.
   *
   *\return   la    posizione   eventuale   della    graffa   chiusa   o
   *string::size_typenpos.
   */

  string::size_type trova_chiudi();

 /**
   *In caso di errore setta a  true _has_error e aggiunge una stringa di
   *errore in _error.
   *
   *\return     la    posizione     eventuale     della    virgola     o
   *string::size_typenpos.
   */

  string::size_type trova_sep();


  
  /**
   *check the  string to be parsed for escaped code and increment the pointer 
   *to the former
   *
   *\return the escaped shar or "" if no escape code "\\" has been found
   */
  string check_escape_char();

  /**
   *add slash for esapable chars
   *
   *the  char   that  need  to   be  esacaped  have  bee   defined  in
   *include/global.hpp ad are the keyword of the bist file
   *
   *\param the string w/o escaped code
   *\return the escaped string
   */

  string add_slash_to_escape(string to_escape);

  /**
   *clean molecule string from null chars as defined in include/global.hpp
   */
  void clean_molecule_from_nullchars();

  void create_progressw();

  /**
   *temporaneamente gli atomi parsati vanno qui
   */

  vector<atomo> _tmp_atom;



  bool _trovato_apice;
  bool _trovato_pedice;

  bool _has_error;
  unsigned int _punt_mol;

  vector <string> _error;
  vector <string> _warning;


  string _filebist;
  string _molecule;

  vector <etichetta*> _stringhe;
  //vector <atomo>  _atomi;
  vector <gruppo> _gruppi;
  
  /**
   *Contiene  coppie <tipo, < gruppo, id > > selezionati, se  si e'
   *selezionato una etichetta gruppo e' uguale a NO_VALID_GROUP
   */

  vector< pair < int, pair<int,int> > > _elem_selected;


  /**
   *Contiene  triplette: gruppo, atomo di appartenenza e id dei legami selezionati
   */
  
  vector<int*> _legami_selected;

  /**
   *val true se l'immaggine e' una preview false altrimenti
   */

  bool _is_preview;


  Fl_Window*   _export_progressw;
  Fl_Progress* _export_progressw_bar;

};


int glue_find_first_near(atomo* from , atomo* to,
                         void* dist, void* atom_other, void* res);

/**
 *\param res_sel_ std::vector< std::pair< std::pair<atomo*,atomo*>, std::pair<float,float> > >
 */

int glue_find_line_near_sel(atomo* from , atomo* to,
                            void* res, void* d, void* d1);

/**
 *\param res_sel_ std::vector< std::pair< std::pair<atomo*,atomo*>, std::pair<float,float> > >
 *\param res std::vector<unsigned int> where res_sel[res[0]] are pointers to atom in selected group for bond
 * and res[1] , res[2] are id of atoms in the other group
 */

int glue_find_line_near_hook(atomo* from , atomo* to,
                             void* res_sel, void* res, void* found);


void export_image_to_vector_file(immagine imm,std::string output_file, bool graphics=true);


void export_image_to_EPS_file_cairo(immagine imm,std::string output_file);

void export_image_to_SVG_file_cairo(immagine imm,std::string output_file);

void export_image_to_PNG_file_cairo(immagine imm,std::string output_file);



/*
Il parser parsa il file riempiendo:

- un vector < atomo >  descrivente il grafo molecolare, ogni elemento
  del vettore contiene le caratteristiche specificate dal file e un
  vector <legami> contenente l'id del legame l' id dell'atomo a cui e'
  legato e il tipo (sigolo, doppio, triplo, verso di noi ,
  allontanantesi)

-ogni volta che si incontra un gruppo il parser riempie il vettore del
 grafo con gli atomi che lo costituiscono e riempie un vector< gruppi>
 con gli elementi che contengono l'id del gruppo e un vector < int >
 contenenti gli id degli atomi che compongono i gruppi


Ogni volta che si aggiunge un atomo si conserva questo (oltre che nel
vettore principale del grafo) anche in un vettore temporaneo e si
controlla questo per vedere se questo sottografo corrisponde a qualche
gruppo particolare (fenile sistemi coniugati carichi e poi?) se si ha
una corrispondenza si modifica il vettore dei gruppi aggiungendo quello
nuovo  e si azzera il vettore temporaneo.
*/

