#include <cstdlib>
#include <climits>
#include <GL/glut.h>
#include "CImageGL.h"

static const int DEFAULT_PATTERN_WIDTH  = 50;
static const int DEFAULT_PATTERN_HEIGHT = 50;
static inline float getRandomColorComponent()
{
  return ( rand() / (float) RAND_MAX );
}
static const float MAX_S = 30.0f;

////////////////////
//PUBLIC FUNCTIONS//
////////////////////

CImageGL::CImageGL() :
  CImage() ,
  m_pDepths(NULL) ,
  m_pattern()
{
}

CImageGL::CImageGL(int w, int h) :
  CImage(w,h) ,
  m_pDepths(new float[w*h]) ,
  m_pattern()
{
}

// Allocate memory for an image
void CImageGL::SetDimension(int w, int h)
{
  int newSize = w*h;
  if (getSize() < newSize)
    m_pDepths.reset(new float[newSize]);

  Allocate(w, h);
}

// Set the pattern that will be used for the stereogram
void CImageGL::SetPattern(const std::string& strName)
{
  bool doRandom = true;

  if (strName != "")
    {
      if (0 == m_pattern.LoadBMP(strName))
        doRandom = false;
    }

  if (doRandom)
    {
      m_pattern.Allocate(DEFAULT_PATTERN_WIDTH,
                         DEFAULT_PATTERN_HEIGHT);

      for (int i = 0; i < m_pattern.width(); ++i)
        for (int j = 0; j < m_pattern.height(); ++j)
          {
            m_pattern(i,j) = CColor(getRandomColorComponent(),
                                    getRandomColorComponent(),
                                    getRandomColorComponent());
          }
    }
}

// The function to call before the buffer swapping
void CImageGL::GenerateStereogram()
{
  CreateFromZBuffer();
  BuildStereogramFromDepth();
  WriteToFrameBuffer();
}




/////////////////////
//PRIVATE FUNCTIONS//
/////////////////////

// Create the image from the z buffer data
void CImageGL::CreateFromZBuffer(bool fullcopy)
{
  glReadPixels(0,                  // x
               0,                  // y,
               width(),            // width
               height(),           // height
               GL_DEPTH_COMPONENT, // format
               GL_FLOAT,           // type
               m_pDepths.get());   // pixels

  if (fullcopy)
    {
      for (int i = 0; i < width(); ++i)
        for (int j = 0; j < height(); ++j)
          (*this)(i, j) = CColor(getDepth(i,j), getDepth(i,j), getDepth(i,j));
    }
}

// Actually calculate the stereogram
void CImageGL::BuildStereogramFromDepth()
{
  int i, j;

  // Here is what we do:
  //
  // WE NEEd
  // a) Pattern to repeat
  // b) Buffer with "s" (depths)
  //
  // 1) Draw repeatidly the pattern on the left of the surface
  // 2) Scan the image line by line
  // 3) With a displacement, we check the preceding "deformed pattern"
  //    with a specific displacement "s"
  for (i = 0; i < m_pattern.width(); ++i)
    for (j = 0; j < height(); ++j)
      (*this)(i,j) = m_pattern(i,
                               j % m_pattern.height());

  for (j = 0; j < height(); ++j)
    for (i = m_pattern.width(); i < width(); ++i)
      {
        float pos    = i - m_pattern.width()+MAX_S*(1-getDepth(i,j));
        float frac   = pos - (int) pos;

        (*this)(i,j) =
          (1-frac) * (*this)(  (int)pos, j) +
          (frac)   * (*this)(1+(int)pos, j);
      }
  /*
  for (int i = 0; i < width(); ++i)
    for (int j = 0; j < height(); ++j)
      {
        (*this)(i,j).red(getDepth(i,j));
        (*this)(i,j).green(getDepth(i,j));
        (*this)(i,j).blue(getDepth(i,j));
      }
  */
}

// Write the image to the frame buffer
void CImageGL::WriteToFrameBuffer(bool depths) const
{
  if (depths)
    {
      glDrawPixels(width(),
                   height(),
                   GL_LUMINANCE,
                   GL_FLOAT,
                   m_pDepths.get());
    }
  else
    {
      glDrawPixels(width(),
                   height(),
                   GL_RGB,
                   GL_FLOAT,
                   &(*this)(0,0));
    }
}
