namespace substructure_search {
  enum action {
    NONE,
    DESCEND_NORMAL, 
    DESCEND_TERMINAL,
    DESCEND_NORMAL_CYCLE,
    DESCEND_TERMINAL_CYCLE,
    DESCEND_SET_BLACK_PARENT_CYCLE,
    DESCEND_SET_BLACK_PARENT,
    DESCEND_FORCE_SET_BLACK_PARENT,
    DESCEND_FORCE_SET_BLACK_PARENT_CYCLE,
    SET_BLACK_CURR_CYCLE,
    SET_BLACK_CURR,
    FORCE_BLACK_CURRENT,
    FORCE_BLACK_PARENT,
    FORCE_SHIFT_CHILD,
    POP_CURRENT_ATOM,
    POP_CURRENT_ATOM_LEAVE_BLACK,
    DELETE_TERMINAL};


  enum colornode {WHITE,
                  GRAY,
                  BLACK
  };

    
  class node_status {

  public:
    node_status()
      :_currcolor(WHITE),
       _lastcolor(WHITE),
       _action(NONE)
    {

    }
    atomo* _node;
    atomo* _parent_node;
    colornode _currcolor;
    colornode _lastcolor;
    action    _action;
  };

  class residual_DFS {

  public:

    residual_DFS();

    residual_DFS(gruppo& ref);

    virtual ~residual_DFS();

    virtual bool DFS_shift_descendant(bool leave_it_black=false);

    virtual bool DFS_simulate_shift_descendant(atomo** res);
    
    virtual bool DFS_simulate_shift_descendant_w_gparent(atomo** res, atomo* gparent,
                                                         atomo* parent, atomo* old_child);
    

    virtual bool DFS_simulate_shift_descendant_no_gparent(atomo** res, atomo* parent, 
                                                          atomo* old_child);

    virtual bool DFS_set_black_current(atomo* candidate);


    virtual void DFS_descend_force_set_black_parent_cycle(atomo* parent, 
                                                          atomo* child,
                                                          bool keep_track);

    virtual void DFS_descend_force_set_black_parent(atomo* parent, 
                                                    atomo* child,
                                                    bool keep_track);
    
    virtual bool DFS_find_descend_forced(atomo* parent, atomo* child);

    virtual void DFS_manage_node_no_white_nodes(bool had_children_not_black,
                                                bool is_cycle,
                                                atomo* parent, 
                                                atomo* child, 
                                                node_status& nw);

    virtual void DFS_find_and_descend(atomo* parent, node_status& nw);

    virtual void DFS_delete_terminal(atomo* at, node_status& nw,bool keep_in_hystory=true);

    virtual void DFS_step(int* start=NULL);

    virtual bool DFS_is_finished();


    virtual void DFS_force_black_current(bool keep_track=true);
    virtual void DFS_force_black_parent(bool keep_track=true);
    

    virtual bool DFS_pop_current_atom(bool leave_it_black=false,bool keep_in_history=true);

    bool  is_black(atomo* a=NULL);

    bool  is_gray(atomo* a=NULL);

    colornode get_color(atomo* atm);

    /**
     *\return true if we can go back
     */

    bool backstep();

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

    void dump();

    void dump_history();

    void dump_any_history(std::queue<node_status> tmp);

    void dump_blacks();

    atomo* get_starting_point();

    bool   is_starting_point(atomo* chkd);

    action curr_action();

    int bond_type();

    int no_children();

    atomo* curr_atom();
    atomo* parent_atom();

    void clean_to_start();

    gruppo get_copy_gruppo();
     
    gruppo* get_pointer_gruppo();

    bool  pop_back_history(node_status& popped);

    static std::string colornode2string(colornode c);

    static std::string action2string(action a);


  protected:


    gruppo _molecule;
  
    std::stack<atomo*> _stack_atoms;

    std::stack<atomo*> _parents;

    std::queue<node_status> _history;

    std::map<atomo*,bool>  _grays;

    std::map<atomo*,bool>  _blacks;

    std::stack<int> _bond_type;
    std::stack<int> _no_children;

    std::stack<action> _curr_action;
  
  };


  class molecule_matching {

  public:
    molecule_matching(gruppo& mol, gruppo& sub);

    virtual ~molecule_matching();

    bool match();


  protected:

    bool main_descend(bool use_reds);

    bool find_normal_candidate(bool use_reds);

    bool find_set_black_parent_cycle_candidate(bool use_reds);

    bool find_set_black_parent_candidate(bool use_reds);

    bool find_generic_candidate(std::vector<action> allowed,
                                bool (molecule_matching::*cond)(residual_DFS*,
                                                                atomo* , atomo*,
                                                                residual_DFS* , 
                                                                atomo* , atomo* ),
                                bool use_reds);

    bool find_alternative_path();

    void debug_print_all(residual_DFS s);

    void debug_print_DFS(residual_DFS s);

    bool TEST_DFS();

    bool TEST_DFS_forced();

    bool TEST_BFS();

    void clean();

    bool good_starting_point(atomo* ref, atomo* start);


    bool check_normal_candidates_atom(residual_DFS* dfs_mol,atomo* mol, atomo* parmol,
                                      residual_DFS* dfs_ref, atomo* sub, atomo* parsub);
  
    bool check_black_parent_cycle_candidate(residual_DFS* dfs_mol, 
                                            atomo* mol, atomo* parmol,
                                            residual_DFS* dfs_ref, 
                                            atomo* sub, atomo* parsub);
    
    bool equal_bond_type(gruppo* molecule_mol, atomo* mol, atomo* parmol, 
                         gruppo* molecule_ref, atomo* ref, atomo* parref);
    

    bool equal_raw_string(atomo* mol, atomo* ref);

    bool equal_no_children(atomo* mol, atomo* ref);

    bool is_direct_children(gruppo* mol,
                            atomo* child, atomo* parent, 
                            legame& res);


    bool go_back_to_parent(residual_DFS& molecule);

    /**
     *set atm red in _mol_reds, if atm is NULL set red _mol.curr_atom()
     *if stack is empty nothing is added to set and false is returned
     *\return true if is added false otherwise
     */
    bool block_path(atomo* atm);

    residual_DFS _sub;
    residual_DFS _mol;

    std::set<atomo*> _mol_reds;

  };

}
