// ============================================================= //
//                                                               //
//   File      : Group.hxx                                       //
//   Purpose   : Handles for taxonomic groups                    //
//                                                               //
//   Coded by Ralf Westram (coder@reallysoft.de) in March 2017   //
//   http://www.arb-home.de/                                     //
//                                                               //
// ============================================================= //

#ifndef GROUP_HXX
#define GROUP_HXX

#ifndef AP_TREE_HXX
#include <AP_Tree.hxx>
#endif

#define td_assert(cond) arb_assert(cond)

class Group {
    RefPtr<GBDATA>          gb_group; // NULp = disappeared/invalid group; otherwise points to "/tree_data/tree_XXXX/node"
    mutable RefPtr<AP_tree> node;     // NULp = unknown or not searched yet or tree not loaded; otherwise points to corresponding node in loaded tree

public:
    Group() : gb_group(NULp), node(NULp) {}
    Group(GBDATA *gb_group_) :
        gb_group(gb_group_),
        node(NULp)
    {
        td_assert(gb_group_);
    }

    Group(AP_tree *node_) :
        gb_group(NULp),
        node(node_)
    {
        if (node->is_normal_group()) {
            gb_group = node->gb_node;
        }
        else { // only point to keeled group if no "normal" group is at node
            td_assert(node->is_keeled_group());
            gb_group = node->get_father()->gb_node;
        }

        td_assert(at_node(node));
    }

    bool is_valid() const { return gb_group; }
    bool is_located() const {
        td_assert(is_valid());
        return node;
    }

    AP_tree *get_node() const { return node; }
    GBDATA *get_group_data() const { return gb_group; } // returns NULp when !is_valid()
    const char *get_name() const {
        td_assert(is_valid());
        GBDATA *gb_name = GB_entry(gb_group, "group_name");
        td_assert(gb_name);
        return GB_read_char_pntr(gb_name);
    }

    bool locate(AP_tree *subtree) const;
    void dislocate() const { node = NULp; }

    bool at_node(AP_tree *test) const {
        if (!gb_group) return false;

        bool at = (test->gb_node == gb_group) && test->is_normal_group();
        if (!at) { // check for group at father keeled down to 'test'
            AP_tree *parent = test->get_father();
            at = (parent->gb_node == gb_group) && parent->keelsDownGroup(test);
        }
        if (at) {
            if (!node) node = test; // locate on-the-fly
#if defined(ASSERTION_USED)
            else td_assert(node == test); // detected mislocation
#endif
        }

        return at;
    }

    bool operator == (const Group& other) const { return gb_group == other.gb_group; }
    bool operator != (const Group& other) const { return !operator == (other); }
};


#else
#error Group.hxx included twice
#endif // GROUP_HXX
