/**
 @file ImageAnalogy_IO.cpp

  Implementation of the input and output routines for ImageAnalogy.

 */
#include "ImageAnalogy.h"
#include "imageutils.h"
#include "SearchTree.h"

const Vector2int16 ImageAnalogy::UNINITIALIZED(-1, -1);

ImageAnalogy::ImageAnalogy(const Settings& s) : settings(s), searchTree(NULL), searchTreeLevel(-1) {
    // Set to true when debugging ANN code
    settings.exhaustiveSearch = false;
}

ImageAnalogy::~ImageAnalogy() {
    delete searchTree;
}

static std::string htmlImage(const std::string& url) {
    return "<a href=\"" + url + "\"><img src=\"" + url + "\" width=200></a>";
}

void ImageAnalogy::saveData() {
    std::string dir = "out";

#   ifdef G3D_WIN32
        mkdir(dir.c_str());
#   else
        mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#   endif

        
    TextOutput t(dir + "/out.html");
    t.printf("<html><head><title>" + settings.title + "</title></head><body>");
    t.printf("<h2>" + settings.title + "</h2>\n");
    t.printf("<table><tr align=center><td>level</td><td><b>A</b><td>:</td></td><td><b>A'</b>"
             "</td><td>::</td><td><b>B</b></td><td>:</td><td><b>B'</b></td><td></td><td>(debug)</td></tr>\n");
    for (int L = 0; L < levelArray.size(); ++L) {
        const Level& level = levelArray[L];

        std::string A1filename = format("A1-%02d.png", L);
        std::string A2filename = format("A2-%02d.png", L);
        std::string B1filename = format("B1-%02d.png", L);
        std::string B2filename = format("B2-%02d.png", L);
        std::string Xfilename  = format("X-%02d.png", L);

        reconstructImage(level.A )->save(dir + "/" + A1filename);
        reconstructImage(level.A2)->save(dir + "/" + A2filename);
        reconstructImage(level.B )->save(dir + "/" + B1filename);
        reconstructImage(level.B2)->save(dir + "/" + B2filename);
        level.debug->save(dir + "/" + Xfilename);

        t.printf("<tr align=center><td>%d</td><td>%s</td><td>:</td><td>%s</td><td>::</td><td>%s</td><td>:</td><td>%s</td><td></td><td>%s</td></tr>\n",
                 L, htmlImage(A1filename).c_str(), htmlImage(A2filename).c_str(), 
                 htmlImage(B1filename).c_str(), htmlImage(B2filename).c_str(), htmlImage(Xfilename).c_str());
    }

    t.printf("</table>* The (debug) column shows debugging output; it is currently displaying "
             "white for approximate match pixels and gray for coherence match pixels.</html>\n");
    t.commit();
}

void ImageAnalogy::createImagePyramid(int N, Image3Ref iA, Image3Ref iA2, Image3Ref iB) {

    printf("Creating image pyramid...");

    levelArray.resize(N);

    // We index on capital L because lower-case L is hard to read in code; N is our num levels
    for (int L = N - 1; L >= 0; --L) {
        Level& level = levelArray[L];

        // Extract features for this level
        level.A     = extractSourceFeatures(iA);
        level.A2    = extractTargetFeatures(iA2);
        level.B     = extractSourceFeatures(iB);
        level.B2    = TargetImage::create(iB->width(), iB->height(), iB->wrapMode());
        level.s     = Map2D<Vector2int16>::create(iB->width(), iB->height());
        level.debug = Image1uint8::createEmpty(iB->width(), iB->height());
        // Mark all s pixels as uninitalized, which will prevent the feature distance and
        // search from looking ahead of the generator.
        level.s->setAll(UNINITIALIZED);
        level.shift = N - 1 - L;

        // Prepare for the next smaller level
        if (L > 0) {
            iA = shrink(iA);
            iA2 = shrink(iA2);
            iB = shrink(iB);
        }
    }

    printf("done.\n");
}


Image3Ref ImageAnalogy::reconstructImage(const TargetImage::Ref& F) {
    int w = F->width();
    int h = F->height();
    Image3Ref B2 = Image3::createEmpty(w, h, F->wrapMode());

    for (int y = 0; y < h; ++y) {
        for (int x = 0; x < w; ++x) {
            B2->set(x, y, F->get(x, y).color.toColor3());
        }
    }

    return B2;
}


SourceImage::Ref ImageAnalogy::extractSourceFeatures(const Image3Ref& A) {
    int w = A->width();
    int h = A->height();
    SourceImage::Ref F = SourceImage::create(w, h, A->wrapMode());

    for (int y = 0; y < h; ++y) {
        for (int x = 0; x < w; ++x) {
                
            // Compute horizontal and vertical derivatives
            Vector2 d;
            d.x = SourceFeature::computeLuminance(A->get(x+1, y, WRAP_CLAMP)) - 
                SourceFeature::computeLuminance(A->get(x-1, y, WRAP_CLAMP));

            d.y = SourceFeature::computeLuminance(A->get(x, y+1, WRAP_CLAMP)) - 
                SourceFeature::computeLuminance(A->get(x, y-1, WRAP_CLAMP));

            F->set(x, y, SourceFeature(A->get(x, y), d));
        }
    }

    return F;
}


TargetImage::Ref ImageAnalogy::extractTargetFeatures(const Image3Ref& A2) {
    // Source and target are currently the same!
    return extractSourceFeatures(A2);
}


void ImageAnalogy::Settings::deserialize(TextInput& t) {
    t.readSymbols("title", "=");
    title = t.readString();

    t.readSymbols("coherencePreference", "=");
    coherencePreference = (float)t.readNumber();

    t.readSymbols("luminanceOnly", "=");
    luminanceOnly = t.readBoolean();

    t.readSymbols("colorSource", "=");
    if (t.readSymbol() == "A") {
        colorSource = SOURCE_A;
    } else {
        colorSource = SOURCE_B;
    }
}
