/////////////////////////////////////////////////////////////////////////
//                                                                     //
// This file contains the implementation of the stereogram generating  //
// function.                                                           //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
#include "CImage.hpp"
#include "CScene.hpp"
#include "stereogram.hpp"




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




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// Function : GenerateStereogram()                                     //
//                                                                     //
// Comments : Generate the stereogram from the depth image.            //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
void GenerateStereogram(const CScene *pScene,
                        const CImage& depthImage,
                        CImage& stereogram)
{
  int i, j;
  CImage pattern;

  bool doRandom = true;


  ////////////////////////////////////////////
  //                                        //
  //  Load the stereogram repeting pattern  //
  //                                        //
  ////////////////////////////////////////////
  if (pScene->m_PropPatternFile != "")
    {
      if (0 == pattern.loadBMP(pScene->m_PropPatternFile))
        doRandom = false;
    }

  if (doRandom)
    {
      pattern.allocate(pScene->m_PropRandomPatternWidth,
                       pScene->m_PropRandomPatternWidth);

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


  ///////////////////////////////
  //                           //
  //  Generate the stereogram  //
  //                           //
  ///////////////////////////////

  // 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"

  stereogram.allocate(depthImage.width()+pattern.width(),
                      depthImage.height());

  for (i = 0; i < pattern.width(); ++i)
    for (j = 0; j < depthImage.height(); ++j)
      stereogram(i,j) = pattern(i, j % pattern.height());

  for (j = 0; j < depthImage.height(); ++j)
    for (i = 0; i < depthImage.width(); ++i)
      {
        real_t pos    = i + MAX_S*(1-depthImage(i,j).red());
        real_t frac   = pos - (int) pos;

        stereogram(i+pattern.width(),j) =
          (1-frac) * stereogram(  (int)pos, j) +
          (frac)   * stereogram(1+(int)pos, j);
      }
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// Function : MapDepthTo0_1()                                          //
//                                                                     //
// Comments : Remap the depth values so that they are between 0 and 1. //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
void MapDepthTo0_1(const CScene* pScene,
                   CImage& depthImage)
{
  real_t minDepth = pScene->m_PropNearestDepth;
  real_t maxDepth = pScene->m_PropFarthestDepth;

  if (minDepth == 0 && maxDepth == 0)
    {
      minDepth = infinity();
      maxDepth = 0;

      for (int j = 0; j < depthImage.height(); ++j)
        for (int i = 0; i < depthImage.width(); ++i)
          {
            real_t t = depthImage(i,j).red();

            if (t != infinity())
              {
                if (t < minDepth)
                  minDepth = t;
                else if (t > maxDepth)
                  maxDepth = t;
              }
          }
    }


  for (int j = 0; j < depthImage.height(); ++j)
    for (int i = 0; i < depthImage.width(); ++i)
      {
        real_t oldT = depthImage(i,j).red();
        real_t newT;

        if (oldT > maxDepth)
          {
            newT = 1;
          }
        else if (oldT < minDepth)
          {
            newT = 0;
          }
        else
          {
            newT = (oldT - minDepth) / (maxDepth - minDepth);
            newT = newT * (pScene->m_PropFarthestMapsTo -
                           pScene->m_PropNearestMapsTo)    +
                   pScene->m_PropNearestMapsTo;
          }

        depthImage(i,j) = CColor(newT, newT, newT);
      }
}
