#include <stdio.h>
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL.h>
#include <vector>

using namespace std;

struct Point { double x, y, z; };
struct Triangle { int ind[3]; };

class Model {
  public:
  void LoadFromFile(FILE *f) {
    int point_count;
    fscanf(f, "%d", &point_count);
    points_.resize(point_count);
    for (int i = 0; i < point_count; i++) {
      fscanf(f, "%lf %lf %lf", &points_[i].x, &points_[i].y, &points_[i].z);
    }
    int triangle_count;
    fscanf(f, "%d", &triangle_count);
    triangles_.resize(triangle_count);
    for (int i = 0; i < triangle_count; i++) {
      fscanf(f, "%d %d %d", &triangles_[i].ind[0], &triangles_[i].ind[1], &triangles_[i].ind[2]);
    }
  }

  void Draw() {
    glBegin(GL_TRIANGLES);
    for (int i = 0; i < triangles_.size(); i++) {
      for (int j = 0; j < 3; j++)
        glVertex3f(points_[triangles_[i].ind[j]].x, 
                   points_[triangles_[i].ind[j]].y,
                   points_[triangles_[i].ind[j]].z);
    }
    glEnd();
  }
  vector<Point> points_;
  vector<Triangle> triangles_;
};

vector<Model> models;

class Object {
 public:
  void LoadFromFile(FILE *f) {
    fscanf(f, "%d %lf %lf %lf %lf %lf %lf %lf", &model_, &rot_axis_.x, &rot_axis_.y, &rot_axis_.z, &rot_angle_, &position_.x, &position_.y, &position_.z);
    color_g_ = (rand()%256)/256.0;
    color_b_ = (rand()%256)/256.0;
  }

  void Draw() {
    glPushMatrix();
    glTranslated(position_.x, position_.y, position_.z);
    glRotated(rot_angle_, rot_axis_.x, rot_axis_.y, rot_axis_.z);
    glColor3f(1,color_g_,color_b_);
    models[model_].Draw();
    glPopMatrix();
  }
  int model_;
  Point rot_axis_;
  double rot_angle_;
  Point position_;
  double color_g_, color_b_;
};

vector<Object> objects;

class CameraAndClick {
 public:
  void LoadFromFile(FILE *f) {
    fscanf(f, "%lf %lf %lf", &position_.x, &position_.y, &position_.z);
    fscanf(f, "%lf %lf %lf", &direction_.x, &direction_.y, &direction_.z);
    fscanf(f, "%lf %lf %lf", &up_.x, &up_.y, &up_.z);
    fscanf(f, "%d %d", &clickx_, &clicky_);
  }

  void Setup() {
    glLoadIdentity();
    gluLookAt(position_.x, position_.y, position_.z, position_.x+direction_.x, position_.y+direction_.y, position_.z+direction_.z, up_.x, up_.y, up_.z);
  }

  void DrawClick() {
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0,500,500,0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(clickx_, clicky_, 0.1);
    glColor3f(1,0,0);
    glBegin(GL_QUADS);
    glVertex3f(-2,-2,0);
    glVertex3f(2,-2,0);
    glVertex3f(2,2,0);
    glVertex3f(-2,2,0);
    glEnd();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
  }

  void PreparePick() {
    int viewport[4];

    glGetIntegerv(GL_VIEWPORT, viewport);
    glSelectBuffer(512, pick_buffer);
    glRenderMode(GL_SELECT);

    glInitNames();
    glPushName(0);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluPickMatrix(clickx_, viewport[3]-clicky_, 1, 1, viewport);
    gluPerspective(90, (GLfloat) (viewport[2]-viewport[0])/(GLfloat) (viewport[3]-viewport[1]), 0.1f, 10000.0f); // 90 is the perspective angle
    glMatrixMode(GL_MODELVIEW);
    Setup();
  }

  int GetPick() {
    int hits;
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    hits = glRenderMode(GL_RENDER);
    if (hits == 0) return -1;
    int hit = pick_buffer[3];
    uint depth = pick_buffer[1];
    for (int i = 0; i < hits; i++) {
      if (pick_buffer[4*i+1] < depth) {
        depth = pick_buffer[4*i+1];
        hit = pick_buffer[4*i+3];
      }
    }
    return hit;
  }

  unsigned int pick_buffer[512];
  Point position_, direction_, up_;
  int clickx_, clicky_;
};

vector<CameraAndClick> clicks;
int cur_click = 0;

void LoadInput(char *inputfile) {
  FILE *f = fopen(inputfile, "r");
  int model_count;
  fscanf(f, "%d", &model_count);
  models.resize(model_count);
  for (int i = 0; i < model_count; i++) models[i].LoadFromFile(f);
  int object_count;
  fscanf(f, "%d", &object_count);
  objects.resize(object_count);
  for (int i = 0; i < object_count; i++) objects[i].LoadFromFile(f);
  int click_count;
  fscanf(f, "%d", &click_count);
  clicks.resize(click_count);
  for (int i = 0; i < click_count; i++) clicks[i].LoadFromFile(f);
  fclose(f);
}

int drawGLScene() {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  clicks[cur_click].PreparePick();
  for (int i = 0; i < objects.size(); i++) { glLoadName(i); objects[i].Draw(); }
  printf("%d\n", clicks[cur_click].GetPick());
  SDL_GL_SwapBuffers();
  return 1;
}

SDL_Surface *surface;

void Quit(int returnCode) { SDL_Quit(); exit(returnCode); }
int resizeWindow(int width, int height) {
  GLfloat ratio = (GLfloat) width / (GLfloat) height;
  glViewport(0, 0, (GLsizei) width, (GLsizei) height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(PR_ANGLE, 1, 0.1f, 10000.0f);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  return 1;
}

void handleKeyPress(SDL_keysym *keysym) {
  switch (keysym->sym) {
  case SDLK_ESCAPE: Quit(0); break;
  case SDLK_n: cur_click++; cur_click %= clicks.size(); break;
  default: break;
  }
  return;
}

int initGL() {
  glShadeModel(GL_SMOOTH);
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClearDepth(1.0f);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
  glDisable(GL_CULL_FACE);
  return 1;
}



int main(int argc, char **argv) {
  if (argc < 2) return 0;
  LoadInput(argv[1]);
  int videoFlags, done=0, isActive=1;
  SDL_Event event;
  const SDL_VideoInfo *videoInfo;

  if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "Video initialization failed: %s\n", SDL_GetError()); Quit(1); }
  videoInfo = SDL_GetVideoInfo();
  if (!videoInfo) { fprintf(stderr, "Video query failed: %s\n", SDL_GetError()); Quit(1); }

  videoFlags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE | SDL_RESIZABLE;
  if (videoInfo->hw_available) videoFlags |= SDL_HWSURFACE; else videoFlags |= SDL_SWSURFACE;
  if (videoInfo->blit_hw) videoFlags |= SDL_HWACCEL;
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  surface = SDL_SetVideoMode(500,500,16,videoFlags);
  if (!surface) { fprintf(stderr, "Video mode set failed: %s\n", SDL_GetError()); Quit(1); }
  initGL();
  resizeWindow(500,500);
  for (int i = 0; i < clicks.size(); i++) { cur_click = i; drawGLScene(); }
  Quit(0);
  return 0;
}
