//----------------------------------------------------------------------------
// High-res OpenGL snapshot code
// (C) 1999,2004   Bill Baxter
//----------------------------------------------------------------------------
// Some parts of the code refer to the GLVU class library, which is available
// here:  http://www.cs.unc.edu/~walk/software/glvu
//----------------------------------------------------------------------------
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without
// fee, provided that the above copyright notice appear in all copies
// and that both that copyright notice and this permission notice
// appear in supporting documentation.  Binaries may be compiled with
// this software without any royalties or restrictions.
//
// The University of North Carolina at Chapel Hill makes no representations 
// about the suitability of this software for any purpose. It is provided 
// "as is" without express or implied warranty.
//-----------------------------------------------------------------------------

#include "hiressnap.h"
#include <camera.hpp>
#include <GL/glut.h>
#include <string.h>
#include <glbuffers.hpp>
#include <tga.hpp>


// Take a super-high-resolution snapshot by tiling the screen and
// rendering N times.

HiresSnapper::HiresSnapper()
  : m_filename("hires"),
    m_gridW(3),
    m_gridH(3),
    m_tileNumX(0),
    m_tileNumY(0),
    m_snapshotCallback( HiresSnapper::grabSnapshot )
{
}


HiresSnapper::HiresSnapper(const Camera& cam, int tileXSize, int tileYSize, bool doAlpha)
  : m_cam(cam),
    m_filename("hires"),
    m_gridW(tileXSize),
    m_gridH(tileYSize),
    m_tileNumX(0),
    m_tileNumY(0)
{
  m_snapshotCallback = (doAlpha) ? grabSnapshotAlpha : grabSnapshot;
}





void HiresSnapper::grabSnapshot(const char *file)
{
  unsigned char *buf = 0;
  int w, h;
  SaveColorBuffer(buf, w, h, GL_FRONT);
  WriteTGA(file, buf, w, h, 3);

  delete [] buf;
}
void HiresSnapper::grabSnapshotAlpha(const char *file)
{
  unsigned char *buf = 0;
  int w, h;
  SaveColorAndAlphaBuffer(buf, w, h, GL_FRONT);
  WriteTGA(file, buf, w, h, 4);

  delete [] buf;
}



// Actually performs GL_PROJECTION setup
void HiresSnapper::cameraSetup( )
{
  float    tLeft,tRight,tBottom,tTop,tNear,tFar;

  int VP[4];
  glGetIntegerv(GL_VIEWPORT, VP);
  int WinW = VP[2];
  int WinH = VP[3];

  fprintf(stderr,"Rendering %i,%i of %ix%i tiles (each of %ix%i pixels)\n",
          m_tileNumX, m_tileNumY, m_gridW, m_gridH, WinW, WinH);

  tLeft  = m_cam.wL + (float)m_tileNumX*((m_cam.wR - m_cam.wL) / (float)m_gridW);
  tRight = tLeft +          ((m_cam.wR - m_cam.wL) / (float)m_gridW);

  tBottom = m_cam.wB + (float)m_tileNumY*((m_cam.wT - m_cam.wB) / (float)m_gridH);
  tTop    = tBottom +          ((m_cam.wT - m_cam.wB) / (float)m_gridH);
    
  tNear = m_cam.Near;
  tFar  = m_cam.Far; 

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(tLeft, tRight, tBottom, tTop, tNear, tFar);
  glMatrixMode(GL_MODELVIEW);
   
}

// Modifies 'cam', do your own GL_PROJECTION setup
void HiresSnapper::cameraSetup( Camera &cam )
{
  float    tLeft,tRight,tBottom,tTop;

  int VP[4];
  glGetIntegerv(GL_VIEWPORT, VP);
  int WinW = VP[2];
  int WinH = VP[3];

  fprintf(stderr,"Rendering %i,%i of %ix%i tiles (each of %ix%i pixels)\n",
          m_tileNumX, m_tileNumY, m_gridW, m_gridH, WinW, WinH);

  tLeft  = m_cam.wL + (float)m_tileNumX*((m_cam.wR - m_cam.wL) / (float)m_gridW);
  tRight = tLeft +          ((m_cam.wR - m_cam.wL) / (float)m_gridW);

  tBottom = m_cam.wB + (float)m_tileNumY*((m_cam.wT - m_cam.wB) / (float)m_gridH);
  tTop    = tBottom +          ((m_cam.wT - m_cam.wB) / (float)m_gridH);
    
  cam.wL = tLeft;
  cam.wR = tRight;
  cam.wT = tTop;
  cam.wB = tBottom;
  cam.Near = m_cam.Near;
  cam.Far = m_cam.Far;
   
}


void HiresSnapper::snap()
{
  char tileName[256];
  sprintf(tileName,"%s_%ix%i.tga", m_filename.c_str(), m_gridH-m_tileNumY-1, m_tileNumX);
  
  glutSwapBuffers();
  m_snapshotCallback(tileName);
}

bool HiresSnapper::nextTile()
{
  if (++m_tileNumX < m_gridW) { 
    return true;  // KEEP_GOING
  }

  m_tileNumX = 0;
  if (++m_tileNumY < m_gridH ) {
    return true; // KEEP GOING
  }

  return false; // all tiles done
}

void HiresSnapper::setCam(const Camera& cam)
{
  m_cam = cam;
}
void HiresSnapper::getCam(Camera& cam) const
{
  cam = m_cam;
}

void HiresSnapper::setSnapshotCallback( void (*snapper)(const char* file) )
{
  m_snapshotCallback = snapper;
}




