/*

    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/>.
*/

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

/**
 *Questa classe  rappresenta un gruppo Un gruppo  rappresenta un insieme
 *eventuale  di procedure e'  una eventuale  molecola<br> <b>Attenzione:
 *  Il programma  assume che  un  gruppo contenga  una sola  molecola
 *ovvero una sola componente connessa.</b>
 */


class gruppo : public disegnabile, public genitore{

public:

  /**
   *Costruttore per copia.
   */

  gruppo(const gruppo& altro);


  /**
   *Costruttore senza parametri
   */

  gruppo();


  /**
   *Distruttore
   */

  virtual  ~gruppo();



  /**
   *\return true se altro ha lo stesso id di quest'istanza
   **/

  bool operator==(gruppo altro);

  /**
   *operatire fi assegnamento overloaded
   */

  gruppo& operator=(const gruppo& altro);


  /**
   *setta  il valore  di visitato  per tutti  gli atomoi  appartenenti a
   *questo gruppo pari a false
   **/

  void cancel_visitato_all();


  /**
   *Ritorna un puntatore all'atomo di questo gruppo identificato dal suo
   *id.
   *
   *\param id l'id dell'atomo da cercare
   *\return un puntatore all'atomo trovato, se esiste, 0 altrimenti
   */
  

  atomo* find_atomo_id(int id);

   /**
   *Ritorna un puntatore alla procedura di questo gruppo identificata dal suo
   *id.
   *
   *\param id l'id della procedura da cercare
   *\return un puntatore alla procedura trovata, se esiste, 0 altrimenti
   */
  
  procedura* find_proc_id(int id);
  


  /**
   *Setta il valore dell 'id del gruppo
   * \param nx nuovo id
   */
  void id_gruppo(int nw);

  void id(int nw);



  /**
   *Setta il valore dell 'id del gruppo
   * \param nx nuovo tipo
   */
  void tipo_gruppo(int nw);


  void xpivot(float nw);

  void ypivot(float nw);

  void angolorot(float nw);



  float xpivot();

  float ypivot();

  float angolorot();


  float posx();
  float posy();

  float phys_posx();
  float phys_posy();

  float visual_posx();
  float visual_posy();

  


  /**
   *Allinea l'etichetta rispetto al legame
   */
  void allinea_etichetta(etichetta& sy,atomo& start);

  void visual_allinea_etichetta(etichetta& sy,atomo& start);


  /**
   *Ritorna l'id del gruppo
   */
  int id_gruppo();

  /**
   *Ritorna l'id del gruppo
   */
  
  virtual int id();


  /**
   *Ritorna il tipo di gruppo
   */

  int tipo_gruppo();


  /**
   *Ritorna un iteratore corrispondente all'inizio del vettore contenente
   *gli id degli atomi.
   */

  vector<atomo>::iterator iniz_atom(){
    return _atomi.begin();
  }


  /**
   *Ritorna un iteratore corrispondente alla fine del vettore contenente
   *gli id degli atomi.
   */

  vector<atomo>::iterator fin_atom(){
    return _atomi.end();
  }


  bool has_atoms();

  /**
   *Ritorna un iteratore corrispondente all'inizio del vettore contenente
   *le procedure.
   */

  vector<procedura*>::iterator iniz_procedure(){
    return _procedure.begin();
  }


  /**
   *Ritorna un iteratore corrispondente alla fine del vettore contenente
   *le procedure.
   */

  vector<procedura*>::iterator fin_procedure(){
    return _procedure.end();
  }

  /**
   *Aggiunge una procedura.
   *
   *\param proc la procedura da aggiungere
   *\return the new proc id
   */

  int aggiungi_procedura(procedura* proc);



  /**
   * Elimina una procedura
   *\param id id della procedura
   */

  void elimina_proc(int id){
    vector<procedura*>::iterator iniz=_procedure.begin();
    vector<procedura*>::iterator fin=_procedure.end();
    procedura* cancellare=0;
    while(iniz!=fin){
      if((*iniz)->id()==id){
	cancellare=(*iniz);
	_procedure.erase(iniz); 
	break;
      }

      iniz++;
    }

    delete cancellare;
  }


  /**
   *Elimina l'atomo.
   *\param id l'id dell'atomo da eliminare
   *
   *\return un iteratore al successivo elemento del vettore
   */

  vector<atomo>::iterator elimina_atomo(int id);

  /**
   *Elimina l'atomo  e i  legami che lo  referenziano negli  altri atomi
   *appartenenti al gruppo.
   *\param id l'id dell'atomo da eliminare
   *\return true se l'atomo e' stato eliminato, false altrimenti.
   */
  bool purge_atom(int id);


  /**
   *Aggiunge  un  atomo al  gruppo  e  eventualmente  li ordina  secondo
   *l'operatore "minore di".
   *
   *\param nw nuovo atomo
   *\param sorted ordina o meno gli elementi
   */

  void add_atomo(atomo nw, bool sorted=true);


  /**
   *Aggiunge un atomo al gruppo setta un id unico e ordina secondo l'operatore "minore di"
   *gli atomi.
   *
   *\param nw nuovo atomo
   *\return l'id assegnato
   */

  int add_atomo_id(atomo nw);


  float w();
  float h();

  float phys_w();
  float phys_h();

  float visual_w();
  float visual_h();


  virtual void disegna();


  virtual bool is_shown();

  /**
   *
   *Ruota il fruppo in senso orario.
   *
   *\param angl l'angolo di rotazione in radianti in senso orario
   *\param xpivot ascissa relativa all'asse di rotazione
   *\param ypivot ordinata relativa all'asse di rotazione
   *
   */


  virtual void ruota(float angl,float x_pivot,float y_pivot);

  virtual void scale(float sc);

  void scale_from_origin(float sc);

  virtual void trasla(float dx,float dy);

  void phys_translate(float dx,float dy);

  void ruota();



  void generic_depth_search(atomo* da, void* data, void* data2 ,void* data3,void* data4,
                            int (*map_atom)(atomo* da , atomo* fino,
                                            void* data, void* data2, void* data3),
                            bool(*ignore)(atomo* atm, void* data4));

  /**
   *Scandisce il grafo degli atomi  con una ricerca depth first; ai nodi
   *collegati a quello visitato viene applicata la funzione pf;
   *
   */

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



  /**
   *Scandisce il grafo degli atomi  con una ricerca depth first; al nodo
   *visitato viene applicato la funzione pf;
   *
   */
  void generic_depth_search_appl_popped(atomo* da, void* data, void* data2 ,void* data3,
					int (*pf)(atomo* da, void* data, 
						  void* data2, void* data3) );
  



  void generic_depth_search_appl_popped(atomo* da, void* data, void* data2 ,
                                        void* data3, void* data4,
                                        int (*pf)(atomo* da ,void* data, 
                                                  void* data2, void* data3) ,
                                        bool(*ignore)(atomo* atm, void* data4));



  /**
   *Crea un "seme" per questo gruppo ovvero due atomi di carbonio legati
   *tra di loro in una certa posizione e legati con un legame standard.
   */

  void genera_seme(float x1, float y1, 
		   float x2, float y2, int tipo= LEGAME_SINGOLO ) throw (range_error);



  /**
   *Calcola il numero di componenti connesse per questo gruppo
   *
   *\param no il numero di componenti connesse
   */

  void no_componenti_connesse(int& no);

  void rimappa_da(int nwid);

  int rimappa_da(int nwid, int id_vec);

  void ordina();




  bool substructure_match(gruppo substructure);


  
  virtual void get_bounding_box(std::pair<float,float>& ld,
                                std::pair<float,float>& ru);

  virtual void get_phys_bounding_box(std::pair<float,float>& ld,
                                     std::pair<float,float>& ru);

  virtual void get_visual_bounding_box(std::pair<float,float>& ld,
                                     std::pair<float,float>& ru);

  virtual void arrows_points(std::vector<float>& x,
                             std::vector<float>& y);



  /**
   *fa il clipping di un segmento
   *
   */
  int clip_leg(float xrect, float yrect, float w, float h,
	       atomo& da , atomo& fino,float& xrisul, float& yrisul);


protected:

  /**
   *Check if atom is bonded to another by bond_type bond
   *\return the first occurence of the bonded atom, 0 therwise
   */
  atomo* check_atom_son_by_bond_type(atomo* parent, int bond_type);

  
  /**
   *Check if atom is bonded to another by bond_type bond AND visitato()== visited_value
   *\return the first occurence of the bonded atom, 0 therwise
   */
  atomo* check_atom_son_by_bond_type(atomo* parent, int bond_type,
				     bool visited_value);

  atomo* check_atom_son_by_bond_type(gruppo* group, atomo* parent, int bond_type,
				     bool visited_value);
  
  


  void clip_lato_cuneo(float* risul, float xlegstart, float ylegstart,
		       float xlegend,float ylegend, float xcunstart, float ycunstart,
		       float xcunend, float ycunend);




  /**
   *Una depth first utile per trovare un ciclo
   *
   *\param  visitati   un  vettore  che  mantiene  il   conto  dei  nodi
   *completamente visitati (black)
   *
   *\param grigi un  vettore che mantiene il conto  dei nodi visitati ma
   *non completamente
   *
   *\param  genitori un  vettore  che  mantiene il  conto  dei nodi  dei
   *genitori (genitori[v]=q q e' genitore di v).
   *
   *\param ultimo atomo che chiude il ciclo
   *
   *\param da  primo atomo del legame doppio
   *
   *\param fino secondo atomo del legame doppio
   *
   *
   *\param risult vale true se si e' trovato un ciclo false altrimenti
   *
   *\param st atomo dal quale partire
   */
  

  void depth_first_ciclo(atomo* st,atomo* da,atomo* fino,
			 vector<int>& grigi, vector<int>& visitati, 
			 vector<int>& genitori, bool& risult,atomo** ultimo,
			 bool& primo_e_adiacente);



  /**
   *Ritorna la  posizione nell'array _atomi  dell'atomo indentificato da
   *id
   *
   *\param id l'id dell'atomo da ricercare
   *
   *\return  la posizione dell'atomo  nell'array -1  in caso  di ricerca
   *infruttuosa
   */

  int get_position_inarr(int id);
  



  /**
   *Renderizza gli elettroni spaiati.
   *
   *\param atm l'atomo da renderizzare
   *\param x l'ascissa della stringa
   *\param y l'ordinata della stringa
   *\param w la larghezza della stringa
   *\param h l'altezza del font
   *\param dopp true se si vole disegnare un doppietto
   */


  void render_el_spaiati(atomo& atm,float x, float y, float w, float h, bool dopp=false);


  std::vector<int> calculate_electron_pos_relative(atomo& atm);

  /**
   *Renderizza le cariche se esistono.
   *
   *\param atm l'atomo da renderizzare
   *\param x l'ascissa della stringa
   *\param y l'ordinata della stringa
   *\param w la larghezza della stringa
   *\param fon il tipo di font
   *\param dim le dimensioni del font
   *\return la posizione occupata dalla carica secondo il seguente schema:
   *<pre>
   *                     3 
   *	    +-------------------------+
   *	    |                         |
   *	  2 |                         | 0
   *	    |                         |
   *	    +-------------------------+
   *                     1
   *</pre>
   *
   * >3 indica nessun lato libero
   *
   *La box viene  scandita in senso orario a  partire dal lato verticale
   *destro.
   */

  int render_carica(atomo& atm,float x, float y, float w,int fon, int dim);



  /**
   *Renderizza un legame che si allontana da noi
   *
   *\param x1 ascissa del punto di partenza del legame
   *\param y1 ordinata del punto di partenza del legame
   *\param x2 ascissa del punto di arrivo del legame
   *\param y2 ordinata del punto di arrivo del legame
   */

  void render_stereo_down(float x1, float y1,
			  float x2, float y2,
                          int cr, int cg, int cb);

  /**
   *Renderizza un doppio legame
   *
   *\param da atomo da cui parte il legame
   *\param fino  atomo a cui il legame arriva
   *
   *\param x1 ascissa del punto di partenza del legame
   *\param y1 ordinata del punto di partenza del legame
   *\param x2 ascissa del punto di arrivo del legame
   *\param y2 ordinata del punto di arrivo del legame
   *\param  tratt  true  se  si  vuole che  uno  dei  legami  sia
   *tratteggiato
   */

  void render_doppio(atomo& da, atomo& fino,
                     float x1, float y1,
                     float x2, float y2, 
                     int cr, int cg,int cb,
                     bool tratt);


  void render_double_forced(atomo& da, atomo& fino,
                            float x1, float y1,
                            float x2, float y2, 
                            int  cr, int cg, int cb,
                            bool tratt,bool side);

  /**
   *Renderizza un legame
   *
   *\param da atomo da cui parte il legame
   *\param fino  atomo a cui il legame arriva
   *\param il_legame il legame da renderizzare
   *
   */


  void render(atomo& da, atomo& fino, legame& il_legame);

  /**
   *disegna un cuneo pieno da stretto a largo
   *param  da   atomo di partenza
   *\param fino atomo di arrivo
   *\param strettox ascissa del punto di partenza del legame
   *\param strettoy ordinata del punto di partenza del legame
   *\param largox ascissa del punto di arrivo del legame
   *\param largoy ordinata del punto di arrivo del legame
   */
  void renderizza_stereo_up(atomo &da, atomo& fino,float strettox, float strettoy,
			    float largox, float largoy,
                            int cr, int cg, int cb);




  /**
   *disegna un legame ispessito da (x1,y1) a (x2,y2)
   *\param x1 ascissa del punto di partenza del legame
   *\param y1 ordinata del punto di partenza del legame
   *\param x2 ascissa del punto di arrivo del legame
   *\param y2 ordinata del punto di arrivo del legame
   */
  void renderizza_ispessito(float x1, float y1,
			    float x2, float y2,
                            int cr, int cg, int cb);



  /**
   *Renderizza un legame a strereochimica sconosciuta
   *
   *\param x1 ascissa del punto di partenza del legame
   *\param y1 ordinata del punto di partenza del legame
   *\param x2 ascissa del punto di arrivo del legame
   *\param y2 ordinata del punto di arrivo del legame
   *
   */

  void renderizza_stereo_scon(float x1,float y1,float x2,float y2,
                              int cr, int cg, int cb);

  /**
   *Scandisce il grafo degli atomi con una ricerca depth first e lo disegna.
   *
   *\param start l'atomo da cui partire.
   **/

  void depth_search(atomo& start);




  /**
   *Ritorna true se  l'atomo da e fino fanno parte  di un ciclo, false
   *altrimenti.
   *
   *\param  ultimo  dopo aver  fatto  andare  la  funzione ultimo  porta
   *l'atomo legato a da che chiude il ciclo
   *
   *\return true se da e fino fanno parte di un ciclo false altrimenti.
   */

  bool trova_ciclo(atomo& da, atomo& fino, atomo** ultimo);

  int _id_gruppo;
  int _tipo_gruppo;
  vector<procedura*> _procedure;
  vector<atomo>      _atomi;
  float _angolorot;
  float _xpivot;
  float _ypivot;

};



/**
 *Ordina secondo l'id in ordine cresente.
 */

bool ordina_gruppo_id(gruppo uno, gruppo  altro);


class GruppoLink{
public:
  int    _first;
  int    _second;
  int    _bondtype;
};

/**
 *links two groups whithout id conflict checking
 */
gruppo link_groups(gruppo* fst, gruppo* snd, std::vector<GruppoLink> links);

etichetta scaled_charge_etichetta(atomo& atm,float f_size);
