#include <iostream>
#include <memory>
#include <sstream>
#include "CImage.hpp"
#include "CShaderGenerator.hpp"
#include "CFileStream.hpp"
#include "CRendererLexer.hpp"
#include "CScanner.hpp"
#include "CScene.hpp"
#include "stereogram.hpp"
#include "CGLUTContext.hpp"
#include "CGLUTShaderContext.hpp"
using std::auto_ptr;
using std::cout;
using std::cerr;
using std::endl;
using std::ostringstream;


int main(int argc, char** argv)
{
  if (argc < 2)
    {
      printf("Usage: %s input.dat\n", argv[0]);
      return 1;
    }

  CFileStream *pStream = new CFileStream(argv[1]);
  if (pStream->failed())
    {
      printf("Error opening input file.\n");
      return 1;
    }

  CRendererLexer *pLexer = new CRendererLexer(pStream);
  CScanner scanner(pLexer);

  auto_ptr<CScene> pScene(scanner.parse());

  if (pScene.get())
    {
      int modes = pScene->m_renderingModes;

      if (modes != CScene::ModeNone)
        pScene->init();

      // These pointers must be declared in this scope or the image
      // objects will be destroyed too soon.
      auto_ptr<CImage> pImage(NULL);
      auto_ptr<CImage> pDepthImage(NULL);
      auto_ptr<CImage> pStereogramImage(NULL);

      if ( (modes & CScene::ModeRaytrace) ||
           (modes & CScene::ModeDepth)    ||
           (modes & CScene::ModeStereogram) )
        {
          // Raytracing is needed
          if (modes & CScene::ModeRaytrace)
            pImage.reset(new CImage);

          if ( (modes & CScene::ModeDepth) ||
               (modes & CScene::ModeStereogram) )
            pDepthImage.reset(new CImage);

          // Do the raytracing
          pScene->rayTrace(pImage.get(), pDepthImage.get());

          if (modes & CScene::ModeRaytrace)
            pImage->show(false);

          if ( (modes & CScene::ModeDepth) ||
               (modes & CScene::ModeStereogram) )
            {
              // Re-map the depth in the depth image
              MapDepthTo0_1(pScene.get(), *pDepthImage);

              if (modes & CScene::ModeDepth)
                pDepthImage->show(false);

              if (modes & CScene::ModeStereogram)
                {
                  pStereogramImage.reset(new CImage);
                  GenerateStereogram(pScene.get(),
                                     *pDepthImage,
                                     *pStereogramImage);
                  pStereogramImage->show(false);
                }
            }
        }

      if (modes & CScene::ModeOpenGL)
        {
          // OpenGL display
          CGLUTContext::getInstance()->init(&argc, argv, *pScene);
          CGLUTContext::getInstance()->show();
        }

      if ( (modes & CScene::ModeShaderText) || 
           (modes & CScene::ModeShaderRender) )
        {
          // Shader generation
          ostringstream shaderStream;

          CShaderGenerator *pGenerator = CShaderGenerator::getInstance();
          pGenerator->generateShader(shaderStream, pScene.get());

          if (modes & CScene::ModeShaderText)
            cout << shaderStream.str() << endl;

          if (modes & CScene::ModeShaderRender) {
            bool initSuccess =  CGLUTShaderContext::getInstance()->init(&argc,
                                                                        argv,
                                                                        *pScene,
                                                                        shaderStream.str());
            if (initSuccess) {
              CGLUTShaderContext::getInstance()->show();
            }
            else {
              cerr << "Unable to display shader." << endl;
            }
          }
        }
    }

  return 0;
}
