// Copyright (c) 2023, ETH Zurich and UNC Chapel Hill.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)

#include "ui/qt_utils.h"

#include "base/camera_models.h"
#include "util/misc.h"

namespace colmap {

Eigen::Matrix4f QMatrixToEigen(const QMatrix4x4& matrix) {
  Eigen::Matrix4f eigen;
  for (size_t r = 0; r < 4; ++r) {
    for (size_t c = 0; c < 4; ++c) {
      eigen(r, c) = matrix(r, c);
    }
  }
  return eigen;
}

QMatrix4x4 EigenToQMatrix(const Eigen::Matrix4f& matrix) {
  QMatrix4x4 qt;
  for (size_t r = 0; r < 4; ++r) {
    for (size_t c = 0; c < 4; ++c) {
      qt(r, c) = matrix(r, c);
    }
  }
  return qt;
}

QImage BitmapToQImageRGB(const Bitmap& bitmap) {
  QImage image(bitmap.Width(), bitmap.Height(), QImage::Format_RGB32);
  for (int y = 0; y < image.height(); ++y) {
    QRgb* image_line = (QRgb*)image.scanLine(y);
    for (int x = 0; x < image.width(); ++x) {
      BitmapColor<uint8_t> color;
      if (bitmap.GetPixel(x, y, &color)) {
        image_line[x] = qRgba(color.r, color.g, color.b, 255);
      }
    }
  }
  return image;
}

QPixmap ShowImagesSideBySide(const QPixmap& image1, const QPixmap& image2) {
  QPixmap image = QPixmap(QSize(image1.width() + image2.width(),
                                std::max(image1.height(), image2.height())));

  image.fill(Qt::black);

  QPainter painter(&image);
  painter.drawImage(0, 0, image1.toImage());
  painter.drawImage(image1.width(), 0, image2.toImage());

  return image;
}

void DrawKeypoints(QPixmap* pixmap, const FeatureKeypoints& points,
                   const QColor& color) {
  if (pixmap->isNull()) {
    return;
  }

  const int pen_width = std::max(pixmap->width(), pixmap->height()) / 2048 + 1;
  const int radius = 3 * pen_width + (3 * pen_width) % 2;
  const float radius2 = radius / 2.0f;

  QPainter painter(pixmap);
  painter.setRenderHint(QPainter::Antialiasing);

  QPen pen;
  pen.setWidth(pen_width);
  pen.setColor(color);
  painter.setPen(pen);

  for (const auto& point : points) {
    painter.drawEllipse(point.x - radius2, point.y - radius2, radius, radius);
  }
}

QPixmap DrawMatches(const QPixmap& image1, const QPixmap& image2,
                    const FeatureKeypoints& points1,
                    const FeatureKeypoints& points2,
                    const FeatureMatches& matches,
                    const QColor& keypoints_color) {
  QPixmap image = ShowImagesSideBySide(image1, image2);

  QPainter painter(&image);
  painter.setRenderHint(QPainter::Antialiasing);

  // Draw keypoints

  const int pen_width = std::max(image.width(), image.height()) / 2048 + 1;
  const int radius = 3 * pen_width + (3 * pen_width) % 2;
  const float radius2 = radius / 2.0f;

  QPen pen;
  pen.setWidth(pen_width);
  pen.setColor(keypoints_color);
  painter.setPen(pen);

  for (const auto& point : points1) {
    painter.drawEllipse(point.x - radius2, point.y - radius2, radius, radius);
  }
  for (const auto& point : points2) {
    painter.drawEllipse(image1.width() + point.x - radius2, point.y - radius2,
                        radius, radius);
  }

  // Draw matches

  pen.setWidth(std::max(pen_width / 2, 1));

  for (const auto& match : matches) {
    const point2D_t idx1 = match.point2D_idx1;
    const point2D_t idx2 = match.point2D_idx2;
    pen.setColor(QColor(0, 255, 0));
    painter.setPen(pen);
    painter.drawLine(QPoint(points1[idx1].x, points1[idx1].y),
                     QPoint(image1.width() + points2[idx2].x, points2[idx2].y));
  }

  return image;
}

}  // namespace colmap
