/*

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


/**
 *Newer create an "hole"!
 *
 *Always keep LEGAME_STEREO_SCON latest!
 */

enum bond_type {

 LEGAME_SINGOLO                = 0,
 LEGAME_DOPPIO                 = 1,
 LEGAME_DOPPIO_1               = 2,
 LEGAME_DOPPIO_2               = 3,
 LEGAME_TRIPLO                 = 4,
 LEGAME_INTERNO                = 5,
 LEGAME_INTERNO_OPP            = -5,
 LEGAME_ESTERNO                = 6,
 LEGAME_ESTERNO_OPP            = -6,
 LEGAME_ISPESSITO              = 7,
 LEGAME_TRATTEGGIATO           = 8,
 LEGAME_DOPPIO_TRATTEGGIATO    = 9,
 LEGAME_DOPPIO_TRATTEGGIATO_1  = 10,
 LEGAME_DOPPIO_TRATTEGGIATO_2  = 11,
 LEGAME_STEREO_SCON            = 12 // keep always latest!!!!
};


#define LAST_BOND               LEGAME_STEREO_SCON

#define ATOM_MAX_POSITION_CHARGE      3
enum atomo_position_charge { R_TOP, R_BOTTOM, L_BOTTOM, L_TOP };


/**
 *Questa classe rappresenta un singolo atomo con i suoi legami.
 */

class atomo : public disegnabile, public genitore, 
	      public identificabile , public selezionabile{
 public:

  atomo();

  atomo(const atomo& altro);

  atomo& operator=(const atomo& altro);

  virtual  ~atomo();

  virtual bool dentro_bb(float x, float y, float w , float h);

  virtual int sotto_mouse(int posx_m, int posy_m);



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

  bool operator==(atomo altro);



  /*setta i valori*/

  void visitato(bool nw);
  void id(int);
  void tipo_atomo(int);
  void cariche(int);
  void position_charge(int nw_pos);
  void doppietti(int);
  void el_spaiati(int);
  void massa(int);
  void cr(int);
  void cg(int);
  void cb(int);
  void pos_x(float);
  void pos_y(float);
  void pos_z(float);

  void etich(etichetta nw);

  void aggiungi_genitore(genitore* gentr);

  /*recupera i valori*/
  bool visitato();
  virtual int id();
  int tipo_atomo();
  int cariche();
  int position_charge();
  int doppietti();
  int el_spaiati();
  int massa();
  int cr();
  int cg();
  int cb();
  float pos_x();
  float visual_pos_x();
  float phys_pos_x();
  float pos_y();
  float visual_pos_y();
  float phys_pos_y();
  float pos_z();
  float visual_pos_z();
  float phys_pos_z();

  etichetta etich();
  etichetta& etich_ref();
  etichetta* etich_punt();
  genitore* ritorna_genitore();

  bool is_son(atomo atm);

  int id_gruppo();



  /**
   *ritorna il primo lato libero dell'atomo 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 lato_libero();

  /**
   *Libera tutti i lati.
   */
  void libera_tutti();


  virtual void disegna();

  virtual void trasla(float dx, float dy);

  virtual void scale(float sc);

  virtual void phys_translate(float dx, float dy);

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

  /**
   * Aggiunge un nuovo legame a quest'atomo.
   *\param nw_leg_id_atomo id dell'atomo del nuovo legame
   *\param nw_leg_id_legame id del nuovo legame
   *\param nw_leg_tipo_legame tipo di legame
   *
   *\param nwcr componente rossa del colore del legame
   *\param nwcg componente verde del colore del legame
   *\param nwcb componente blu del colore del legame
   */
  void aggiungi_legame(int nw_leg_id_atomo,
		       int nw_leg_id_legame,
		       int nw_leg_tipo_legame,
		       int nwcr, int nwcg, int nwcb);


  /**
   * Aggiunge un nuovo legame a quest'atomo.
   *
   * L'id del legame e' quello dell'ultimo atomo del vettore _legami+1
   *
   *\param nw_leg_id_atomo id dell'atomo del nuovo legame
   *\param nw_leg_id_legame id del nuovo legame
   *
   *\param nwcr componente rossa del colore del legame
   *\param nwcg componente verde del colore del legame
   *\param nwcb componente blu del colore del legame
   */

  void aggiungi_legame(int nw_leg_id_atomo,
		       int nw_leg_tipo_legame,int nwcr,
		       int nwcg , int nwcb);



  /**
   *Modifica i parametri di un legame.
   *
   *\param id_target id dell'atomo a cui questo atomo e legato
   *
   *\param nw_leg_id_atomo id dell'atomo del nuovo legame
   *\param nw_leg_id_legame id del nuovo legame
   *\param nw_leg_tipo_legame tipo di legame
   *\param nwcr componente rossa del colore del legame
   *\param nwcg componente verde del colore del legame
   *\param nwcb componente blu del colore del legame
   *
   */

  void modifica_legame(int id_target,
		       int nw_leg_id_atomo,
		       int nw_leg_id_legame,
		       int nw_leg_tipo_legame,
		       int nwcr, int nwcg, int nwcb);



  void modifica_legame(int id_target,
		       int* nw_leg_id_atomo,
		       int* nw_leg_id_legame,
		       int* nw_leg_tipo_legame,
		       int* nwcr, int* nwcg, int* nwcb);




  legame* find_leg(int id);


  /**
   *\return  ritorna un  iterator  al primo  elemento del  vettore
   *contenente i legami.
   */

  vector<legame>::iterator primo_leg();

  vector<legame>::iterator iniz_leg(){
    return primo_leg();
  }


  /**
   *\return  ritorna un iterator  all'ultimo elemento  del vettore
   *contenente i legami.
   */


  vector<legame>::iterator ultimo_leg();

  vector<legame>::iterator fin_leg(){
    return ultimo_leg();
  }
  

  std::vector<atomo*> get_all_children();

  unsigned int bond_number();

  unsigned int bond_number_not_visited();
  


  /**
   *Elimina un legame.
   *\param id id dell'atomo del legame da eliminare
   *
   */
  void elimina_legame(int id);


  /**
   *Elimina <b>tutti</b> i legami.
   */

  void elimina_legami();


  /**
   *Setta a false tutte le vistite.
   */

  void reset_arrivati();


  /**
   *Setta a true le vistite in posizione pos.
   */

  void setta_arrivati(int pos);



  /**
   *ritorna le visite dell atomo id.
   */

  bool ritorna_arrivati(int id);


  /**
   *Costruisce il vettore delle visite;
   */

  void costruisci_arrivati();


  /**
   *Ritorna true se l'atomo  in posizione altro ha visitato quest'atomo,
   *false  altrimenti.
   */

  bool e_arrivato(int altro);


  bool* ritorna_lati_liberi();

  bool  can_attach();

  void  can_attach(bool nw);


protected:

  //  int _lato_libero;


  /*        lati[3]
	+-----------------+
	|                 |
 lati[2]|                 |lati[0]
	|                 |
	+-----------------+
	    lati[1]
  */


  bool _lati_liberi[4];

  bool _visitato;

  int _id;
  int _tipo_atomo;

  int _cr;
  int _cg;
  int _cb;

  int _massa;

  etichetta _etich;

  int _cariche;

  int _position_charge;
  int _doppietti;
  int _el_spaiati;

  float _pos_x;
  float _pos_y;
  float _pos_z;

  /**
   *Ogni elemento e' un vettore di tre elementi contenente, a sua volta,
   *tre interi corrispondenti: all'id dell'atomo al quale punta il nuovo
   *legame, all'id del legame e al tipo di legame.
   */
  vector<legame> _legami;

  /**
   *L'  elemento di  questo vettore  vale true  se l'atomo  nella stessa
   *posizione in legami ha visitato quest'atomo.
   */

  vector<bool>   _arrivati;



  /**
   *Vale  true se  quest'atomo  puo' accettare  un  altro legame,  false
   *altrimenti.
   *
   *Per esempio se  si attacca un frammento ad un'altro  gruppo e poi se
   *ne  vuole attaccare  un'altro in  un punto  preciso  di quest'ultimo
   *allora si puo' settare a true questa variabile.
   *
   *esempio (i numeri indicano gli id):
   *
   *       ----2    ci attacchiamo ---1--2---       
   *
   *	  il risultato sara' (tra parentesi gli id vecchi)
   *	  
   *	  ----2(2)---3(1)---4(2)--
   *
   *
   *ora  supponiamo che  si  voglia attaccare  un'altro frammento  nella
   *posizione che  era di 1...il fatto  e'che ora l'id 1  e' diventato 4
   *mentre addirittura  l'id 1 unon  esiste piu'.  Per ovviare  a questo
   *problema si puo' marcare  l'atomo che si intende agganciare settando
   *a true questo parametro. Ricordarsi poi di risettarlo a false!
   *  
   *
   */

  bool _can_attach;


};

bool ordina_atomo_id(atomo  uno, atomo  altro);


/**
 *Ordina i legami di un atomo per id atomico dgli atomi a cui e' legato.
 */
bool ordina_legame_id_atomo(legame uno, legame altro);


bool ordina_legame_id_legame(legame  uno, legame  altro);
