#include "BilinearInterpolation.h"

#include "ReadOnlyMapImage.h"

#include <cmath>

BilinearInterpolation::BilinearInterpolation(ReadOnlyMapImage *const mapImage)
    : InterpolationMethod(mapImage)
{
}

QRgb BilinearInterpolation::interpolate(double const x, double const y)
{
    int const x1 = x;
    int const x2 = x1 + 1;
    int const y1 = y;
    int const y2 = y1 + 1;

    QRgb const lowerLeftPixel = m_mapImage->pixel(x1, y1);
    QRgb const lowerRightPixel = m_mapImage->pixel(x2, y1);
    QRgb const upperLeftPixel = m_mapImage->pixel(x1, y2);
    QRgb const upperRightPixel = m_mapImage->pixel(x2, y2);

    // interpolate horizontically
    //
    // x2 - x    x2 - x
    // ------- = ------ = x1 + 1 - x = 1 - fractionX
    // x2 - x1      1
    //
    // x - x1    x - x1
    // ------- = ------ = fractionX
    // x2 - x1     1

    double const fractionX = x - x1;
    double const lowerMidRed = (1.0 - fractionX) * qRed(lowerLeftPixel) + fractionX * qRed(lowerRightPixel);
    double const lowerMidGreen = (1.0 - fractionX) * qGreen(lowerLeftPixel) + fractionX * qGreen(lowerRightPixel);
    double const lowerMidBlue = (1.0 - fractionX) * qBlue(lowerLeftPixel) + fractionX * qBlue(lowerRightPixel);
    double const lowerMidAlpha = (1.0 - fractionX) * qAlpha(lowerLeftPixel) + fractionX * qAlpha(lowerRightPixel);

    double const upperMidRed = (1.0 - fractionX) * qRed(upperLeftPixel) + fractionX * qRed(upperRightPixel);
    double const upperMidGreen = (1.0 - fractionX) * qGreen(upperLeftPixel) + fractionX * qGreen(upperRightPixel);
    double const upperMidBlue = (1.0 - fractionX) * qBlue(upperLeftPixel) + fractionX * qBlue(upperRightPixel);
    double const upperMidAlpha = (1.0 - fractionX) * qAlpha(upperLeftPixel) + fractionX * qAlpha(upperRightPixel);

    // interpolate vertically
    //
    // y2 - y    y2 - y
    // ------- = ------ = y1 + 1 - y = 1 - fractionY
    // y2 - y1      1
    //
    // y - y1    y - y1
    // ------- = ------ = fractionY
    // y2 - y1     1

    double const fractionY = y - y1;
    double const red = (1.0 - fractionY) * lowerMidRed + fractionY * upperMidRed;
    double const green = (1.0 - fractionY) * lowerMidGreen + fractionY * upperMidGreen;
    double const blue = (1.0 - fractionY) * lowerMidBlue + fractionY * upperMidBlue;
    double const alpha = (1.0 - fractionY) * lowerMidAlpha + fractionY * upperMidAlpha;

    return qRgba(round(red), round(green), round(blue), round(alpha));
}
