/*	SCCS Id: @(#)qt_clust.cpp	3.4	1999/11/19	*/
/* Copyright (c) Warwick Allison, 1999. */
/* NetHack may be freely redistributed.  See license for details. */
#include "qt_clust.h"

static void include(QRect& r, const QRect& rect)
{
    if (rect.left() < r.left()) {
        r.setLeft(rect.left());
    }
    if (rect.right() > r.right()) {
        r.setRight(rect.right());
    }
    if (rect.top() < r.top()) {
        r.setTop(rect.top());
    }
    if (rect.bottom() > r.bottom()) {
        r.setBottom(rect.bottom());
    }
}

/*
 * A Clusterizer groups rectangles (QRects) into non-overlapping
 * rectangles by a merging heuristic.
 */
Clusterizer::Clusterizer(int maxclusters) :
    cluster(new QRect[maxclusters]),
    count(0),
    max(maxclusters)
{
}

Clusterizer::~Clusterizer()
{
    delete [] cluster;
}

void Clusterizer::clear()
{
    count = 0;
}

void Clusterizer::add(int x, int y)
{
    add(QRect(x, y, 1, 1));
}

void Clusterizer::add(int x, int y, int w, int h)
{
    add(QRect(x, y, w, h));
}

void Clusterizer::add(const QRect& rect)
{
    QRect biggerrect(rect.x() - 1, rect.y() - 1,
                     rect.width() + 2, rect.height() + 2);

    //assert(rect.width() > 0 && rect.height() > 0);

    int cursor;

    for (cursor=0; cursor < count; cursor++) {
        if (cluster[cursor].contains(rect)) {
            // Wholly contained already.
            return;
        }
    }

    int lowestcost = 9999999;
    int cheapest = -1;
    for (cursor = 0; cursor < count; cursor++) {
        if (cluster[cursor].intersects(biggerrect)) {
            QRect larger=cluster[cursor];
            include(larger,rect);
            int cost = larger.width() * larger.height()
                     - cluster[cursor].width() * cluster[cursor].height();

            if (cost < lowestcost) {
                bool bad = false;
                for (int c = 0; c < count && !bad; c++) {
                    bad = cluster[c].intersects(larger) && c != cursor;
                }
                if (!bad) {
                    cheapest = cursor;
                    lowestcost = cost;
                }
            }
        }
    }
    if (cheapest >= 0) {
        include(cluster[cheapest], rect);
        return;
    }

    if (count < max) {
        cluster[count++] = rect;
        return;
    }

    // Do cheapest of:
    //  add to closest cluster
    //  do cheapest cluster merge, add to new cluster

    lowestcost = 9999999;
    cheapest = -1;
    for (cursor = 0; cursor < count; cursor++) {
        QRect larger = cluster[cursor];
        include(larger, rect);
        int cost = larger.width() * larger.height()
                 - cluster[cursor].width() * cluster[cursor].height();
        if (cost < lowestcost) {
            bool bad = false;
            for (int c = 0; c < count && !bad; c++) {
                bad = cluster[c].intersects(larger) && c != cursor;
            }
            if (!bad) {
                cheapest = cursor;
                lowestcost = cost;
            }
        }
    }

    // XXX could make an heuristic guess as to whether we
    // XXX need to bother looking for a cheap merge.

    int cheapestmerge1 = -1;
    int cheapestmerge2 = -1;

    for (int merge1 = 0; merge1 < count; merge1++) {
        for (int merge2 = 0; merge2 < count; merge2++) {
            if (merge1 != merge2) {
                QRect larger = cluster[merge1];
                include(larger, cluster[merge2]);
                int cost = larger.width() * larger.height()
                         - cluster[merge1].width() * cluster[merge1].height()
                         - cluster[merge2].width() * cluster[merge2].height();
                if (cost < lowestcost) {
                    bool bad = false;
                    for (int c = 0; c < count && !bad; c++) {
                        bad = cluster[c].intersects(larger) && c != cursor;
                    }
                    if (!bad) {
                        cheapestmerge1 = merge1;
                        cheapestmerge2 = merge2;
                        lowestcost = cost;
                    }
                }
            }
        }
    }

    if (cheapestmerge1 >= 0) {
        include(cluster[cheapestmerge1], cluster[cheapestmerge2]);
        cluster[cheapestmerge2] = cluster[count--];
    } else {
        // if (!cheapest) debugRectangles(rect);
        include(cluster[cheapest],rect);
    }

    // NB: clusters do not intersect (or intersection will
    //   overwrite).  This is a result of the above algorithm,
    //   given the assumption that (x,y) are ordered topleft
    //   to bottomright.
}

const QRect& Clusterizer::operator[](int i)
{
    return cluster[i];
}
