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

#include "Group.hxx"
#include "GroupIterator.hxx"

#include <AP_TreeSet.hxx>

using namespace std;

static AP_tree *find_node_with_groupdata(AP_tree *subtree, GBDATA *gb_group) {
    // brute-force impl
    // @@@ instead use group id (as stored in node) as hint to find group
    if (subtree->is_leaf()) return NULp;
    if (subtree->gb_node == gb_group) return subtree;

    AP_tree *found    = find_node_with_groupdata(subtree->get_leftson(), gb_group);
    if (!found) found = find_node_with_groupdata(subtree->get_rightson(), gb_group);
    return found;
}

bool Group::locate(AP_tree *subtree) const {
    td_assert(is_valid());

    if (!is_located()) {
        node = find_node_with_groupdata(subtree, gb_group);
        td_assert(node); // wrong subtree specified!

        TreeNode *keeledToSon = node->keelTarget();
        if (keeledToSon) {
            node = DOWNCAST(AP_tree*, keeledToSon);
        }
    }

    td_assert(implicated(node, at_node(node)));
    return node;
}

// --------------------------------------------------------------------------------

#ifdef UNIT_TESTS
#ifndef TEST_UNIT_H
#include <test_unit.h>
#endif

void TEST_groups() {
    GB_shell  shell;
    GBDATA   *gb_main = GB_open("../../demo.arb", "r");

    SmartPtr<AP_tree_root> treeRoot = new AP_tree_root(new AliView(gb_main), NULp, false, NULp);

    {
        GB_transaction ta(gb_main);
        TEST_EXPECT_NO_ERROR(treeRoot->loadFromDB("tree_test"));
    }

    AP_tree     *rootNode = treeRoot->get_root_node();
    AP_tree_set  existingGroups;

    const int GROUP_COUNT = 8;

    collect_contained_groups(rootNode, existingGroups);
    TEST_EXPECT_EQUAL(existingGroups.size(), GROUP_COUNT);

    {
        Group gunknown;
        TEST_EXPECT(!gunknown.is_valid());
        TEST_EXPECT_NULL(gunknown.get_group_data());

        for (AP_tree_set_iter i = existingGroups.begin(); i != existingGroups.end(); ++i) {
            AP_tree *node = *i;

            Group gfound(node);
            Group gexisting(node->gb_node);

            TEST_EXPECT(gfound.is_located());
            TEST_EXPECT(!gexisting.is_located());

            TEST_REJECT_NULL(gfound.get_group_data());
            TEST_REJECT_NULL(gexisting.get_group_data());

            TEST_EXPECT(gexisting.locate(rootNode));
            TEST_EXPECT(gexisting.is_located());
            TEST_EXPECT_EQUAL(gfound.get_node(), gexisting.get_node());

            gexisting.dislocate();
            TEST_EXPECT(!gexisting.is_located());

            // for all groupnodes test whether group 'gexisting' is at_node
            int seen_exist = 0;
            int seen_found = 0;
            for (AP_tree_set_iter j = existingGroups.begin(); j != existingGroups.end(); ++j) {
                AP_tree *testnode = *j;
                if (gexisting.at_node(testnode)) seen_exist++;
                if (gfound.at_node(testnode)) seen_found++;
            }

            TEST_EXPECT_EQUAL(seen_found, 1);
            TEST_EXPECT_EQUAL(seen_exist, 1);
            TEST_EXPECT(gexisting.is_located());
            TEST_EXPECT_EQUAL(gfound.get_node(), gexisting.get_node());

            { // compare node name
                GB_transaction ta(gb_main);
                TEST_EXPECT_EQUAL(gfound.get_name(), gfound.get_node()->name);
            }
        }
    }

    // test GroupIterator
    {
        GroupIterator iter(rootNode);
        GroupIterator reverse(rootNode, false);

        TEST_EXPECT(iter.valid());
        TEST_EXPECT(reverse.valid());

        AP_tree *start    = iter.node();
        int      count    = 0;
        int      differed = 0;

        AP_tree *at = start;
        do {
            TEST_ANNOTATE(GBS_global_string("count=%i", count));

            AP_tree *rat = reverse.node();

            TEST_EXPECT(at->is_normal_group());
            TEST_EXPECT(rat->is_normal_group());

            fprintf(stderr, "iter=%s reverse=%s\n", at->name, rat->name);

            // Note: groupname 'outer' and 'test' are each used at two different groups!
            // Counting leafs below ensures the iterator point to the right one of these duplicates!

            switch (count) {
                case 0:
                    TEST_EXPECT_EQUAL(at->name, "outer"); TEST_EXPECT_EQUAL(at->count_leafs(), 15);
                    TEST_EXPECT_EQUAL(rat->name, "last");
                    TEST_EXPECT_EQUAL(iter.get_clade_level(), 1);
                    TEST_EXPECT_EQUAL(reverse.get_clade_level(), 1);
                    break;
                case 2:
                    TEST_EXPECT_EQUAL(at->name, "test"); TEST_EXPECT_EQUAL(at->count_leafs(), 4);
                    TEST_EXPECT_EQUAL(rat->name, "inner");
                    TEST_EXPECT_EQUAL(iter.get_clade_level(), 2);
                    TEST_EXPECT_EQUAL(reverse.get_clade_level(), 2);
                    break;
                case GROUP_COUNT-1:
                    TEST_EXPECT_EQUAL(at->name, "last");
                    TEST_EXPECT_EQUAL(rat->name, "outer"); TEST_EXPECT_EQUAL(rat->count_leafs(), 15);
                    TEST_EXPECT_EQUAL(iter.get_clade_level(), 1);
                    TEST_EXPECT_EQUAL(reverse.get_clade_level(), 1);
                    break;
            }

            count++;
            if (at != rat) ++differed;

            {
                GroupIterator dup(iter);
                TEST_EXPECT(dup.node() == iter.node());

                GroupIterator rdup(dup.next().node(), false);
                TEST_EXPECT(dup.node() == rdup.node());

                dup.previous();
                rdup.next();
                TEST_EXPECT(dup.node() == iter.node());
                TEST_EXPECT(dup.node() == rdup.node());

                dup.next();
                rdup.previous();
                TEST_EXPECT(dup.node() == rdup.node());
            }

            at = iter.next().node();
            reverse.next();
        }
        while (at != start);

        TEST_EXPECT_EQUAL(count, GROUP_COUNT);
        TEST_EXPECT_EQUAL(differed, GROUP_COUNT%2 ? GROUP_COUNT-1 : GROUP_COUNT);
    }



    GB_close(gb_main);
}

#endif // UNIT_TESTS

// --------------------------------------------------------------------------------

