#include "mosaic.hpp"


void Mosaics::add(Image* im, unsigned dim) {
    assert(dim == im->buf.w && dim == im->buf.h);
    const rgb_t im_med = im->med();
    const rgb_t im_avg = im->avg();
    const unsigned char im_avg_dist = im_avg.dist(im_med); // smoothness
    const unsigned char im_med_dist = im->dist(im_med); // average distance

    #if 0
        for (std::vector<entry_t>::iterator it = entries.begin(); it != entries.end();) {
            if (it->dim == dim && it->im_med == im_med) {
                if (im_med_dist > it->im_med_dist || (im_med_dist == it->im_med_dist && im_avg_dist > it->im_avg_dist)) {
                    // there are already better choices
                    LOG("not adding %p", im);
                    delete im;
                    return;
                } else if (im_med_dist < it->im_med_dist || (im_med_dist == it->im_med_dist && im_avg_dist < it->im_avg_dist)) {
                    // we would be a better choice
                    LOG("purging %p", it->im);
                    delete it->im;
                    it = entries.erase(it);
                    continue;
                }
            }
            ++it;
        }
    #endif

    entries.push_back((entry_t){
        dim,
        im,
        im_avg,
        im_med,
        im_avg_dist,
        im_med_dist,
    });
}


Mosaics::~Mosaics() {
    for (std::vector<entry_t>::iterator it = entries.begin(); it != entries.end(); ++it) {
        delete it->im;
    }
}


bool Mosaics::add(const Image* im) {
    bool rv = false;
    Image* i = im->box(config.mosaic_dim);
    if (i) {
        add(i, config.mosaic_dim);
        rv = true;
    }
    i = im->box(config.mosaic_dim_half);
    if (i) {
        add(i, config.mosaic_dim_half);
        rv = true;
    }
    return rv;
}


const Image* Mosaics::find_best(unsigned dim, rgb_t c, unsigned char& dist) const {
    Image* im = NULL;
    unsigned char im_avg_dist = -1;
    dist = -1;
    for (std::vector<entry_t>::const_iterator it = entries.begin(); it != entries.end(); ++it) {
        if (it->dim != dim) continue;
        unsigned char d = it->im_med.dist(c);
        if ((d < dist) || (d == dist && (it->im_avg_dist < im_avg_dist || (it->im_avg_dist == im_avg_dist && rand()%2)))) {
            dist = d;
            im_avg_dist = it->im_avg_dist;
            im = it->im;
        }
    }
    return im;
}


const Image* Mosaics::find_best(const Image* im, double& dist) const {
    std::vector<entry_t>::const_iterator best = entries.end();
    for (std::vector<entry_t>::const_iterator it = entries.begin(); it != entries.end(); ++it) {
        if (it->dim != im->buf.w || it->dim != im->buf.h) continue;
        double cand_dist = im->fdist(it->im);
        if (best == entries.end() || cand_dist < dist || (cand_dist == dist && rand() % 2)) {
            dist = cand_dist;
            best = it;
        }
    }
    assert(best != entries.end());
    return best->im;
}