#include <array>
#include <cstdlib>
#include <iostream>
#include <vector>

#include <GL/glut.h>
#include <Magick++.h>

using Color = std::array<double, 3>;
std::vector<Color> pixels;
int width, height, center_x, center_y, radius;

double alpha = 0.5;

void display() {
  glClearColor(0.0, 0.0, 0.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT);
  glBegin(GL_POINTS);
  for (int x = 0; x < width; ++x)
    for (int y = 0; y < height; ++y) {
      int image_x = x, image_y = y;
      double dx = x - center_x, dy = y - center_y;
      double r = std::sqrt(dx * dx + dy * dy);
      if (r < radius) {
        double scale = alpha * radius / (radius - r);
        int px = std::round(center_x + dx * scale);
        int py = std::round(center_y + dy * scale);
        if (px >= 0 && px < width && py >= 0 && py < height) {
          image_x = px;
          image_y = py;
        }
      }
      glColor3dv(&pixels[image_x * height + image_y][0]);
      glVertex2i(x, y);
    }
  glEnd();
  glutSwapBuffers();
}

void keyboard(unsigned char key, int, int) {
  switch (key) {
  case 'd' : alpha /= 1.1; glutPostRedisplay(); break;
  case 'f' : alpha *= 1.1; glutPostRedisplay(); break;
  case 'j' : if (radius > 10) { radius /= 1.1; glutPostRedisplay(); } break;
  case 'k' : radius *= 1.1; glutPostRedisplay(); break;
  case 'q' : std::exit(0);
  }
}

void mouse(int x, int y) {
  center_x = x;
  center_y = y;
  glutPostRedisplay();
}

int main(int argc, char *argv[]) {
  if (argc != 2) {
    std::cerr << "Usage: " << argv[0] << " <image file>" << std::endl;
    return 1;
  }

  Magick::InitializeMagick(nullptr);
  Magick::Image image;
  image.read(argv[1]);
  width = image.columns();
  height = image.rows();
  center_x = width / 2;
  center_y = height / 2;
  radius = std::min(width, height) / 3;
  pixels.reserve(width * height);
  for (int x = 0; x < width; ++x)
    for (int y = 0; y < height; ++y) {
      Magick::ColorRGB color = image.pixelColor(x, y);
      pixels.push_back({ color.red(), color.green(), color.blue() });
    }

  glutInit(&argc, argv);
  glutInitWindowSize(width, height);
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
  glutCreateWindow("Magnifying Glass");
  glutDisplayFunc(display);
  glutKeyboardFunc(keyboard);
  glutPassiveMotionFunc(mouse);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(0, width, height, 0);

  glutMainLoop();
}