/* OpenSceneGraph example, osgdrawinstanced.
*
*  Permission is hereby granted, free of charge, to any person obtaining a copy
*  of this software and associated documentation files (the "Software"), to deal
*  in the Software without restriction, including without limitation the rights
*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the Software is
*  furnished to do so, subject to the following conditions:
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*  THE SOFTWARE.
*/
//
// This code is copyright (c) 2008 Skew Matrix Software LLC. You may use
// the code under the licensing terms described above.
//

#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osg/Geometry>
#include <osg/Texture2D>
#include <osg/Texture2DArray>
#include <osg/BlendFunc>
#include <osg/BlendEquation>

#include <osgDB/WriteFile>

#include <iostream>
#include <iomanip>
#include <sstream>

#include <osgViewer/ViewerEventHandlers>


osg::Node* createTwoQuads()
{
   osg::Vec3 corner(-0.5, 0.f, -0.5);
   osg::Vec3 width(1.f, 0.f, 0.f);
   osg::Vec3 height(0.f, 0.f, 1.f);   

   osg::Geode* pGeode = new osg::Geode;

   // first quad 
   {
      osg::Geometry* pGeometry = osg::createTexturedQuadGeometry(corner, width, height);

      
      pGeode->addDrawable(pGeometry);

      // add the texture idx for the first quad
      osg::Uniform* pIdxUniform = new osg::Uniform(osg::Uniform::INT, "texIdx");
      pIdxUniform->set(0);
      pGeometry->getOrCreateStateSet()->addUniform(pIdxUniform);
   }

   
   // secod quad 
   {
      float offset = 2.f;

      osg::Geometry* pGeometry = osg::createTexturedQuadGeometry(corner + osg::Vec3(offset, 0, 0), width, height);   
      pGeode->addDrawable(pGeometry);

      osg::Uniform* pIdxUniform = new osg::Uniform(osg::Uniform::INT, "texIdx");
      pIdxUniform->set(1);
      pGeometry->getOrCreateStateSet()->addUniform(pIdxUniform);
   }

   osg::Texture2DArray* pTex2DArray = new osg::Texture2DArray;
   pTex2DArray->setTextureDepth(2);
   pTex2DArray->setImage(0, osgDB::readImageFile("osg256.png"));
   pTex2DArray->setImage(1, osgDB::readImageFile("nose.tga"));

   pTex2DArray->setFilter(osg::Texture2DArray::MIN_FILTER, osg::Texture2DArray::NEAREST);
   pTex2DArray->setFilter(osg::Texture2DArray::MAG_FILTER, osg::Texture2DArray::NEAREST);

   unsigned int texUnit = 0;
   osg::StateSet* pSS = pGeode->getOrCreateStateSet();
   pSS->setTextureAttributeAndModes(texUnit, pTex2DArray);

   // set the uniform
   osg::Uniform* pTex2DU = new osg::Uniform(osg::Uniform::SAMPLER_2D_ARRAY, "textureArray");
   pTex2DU->set(0);

   // add the shaders

   std::string vertSource = 
      "varying vec2 texCoords; \n"
      " \n"
      "void main() \n"
      "{ \n"
      "	// pass the tex coords to frag program \n"
      "texCoords = gl_MultiTexCoord0.xy; \n"
      "	 \n"
      "	gl_Position = ftransform();	 \n"
      "}";

   osg::Shader* pVertShader = new osg::Shader(osg::Shader::VERTEX, vertSource);

   osg::Program* pProgram = new osg::Program;
   pProgram->addShader(pVertShader);

   std::string fragSource = 
      "uniform sampler2DArray textureArray; \n"
      "uniform int texIdx; \n"
      " \n"
      "varying vec2 texCoords; \n"
      " \n"
      "void main() \n"
      "{ \n"
      "	vec4 color = texture2DArray(textureArray, vec3(texCoords, texIdx));	 \n"      
      "gl_FragColor = color; \n"
      "}";

   osg::Shader* pFragShader = new osg::Shader(osg::Shader::FRAGMENT, fragSource);
   pProgram->addShader(pFragShader);

   pSS->setAttributeAndModes(pProgram);

   return pGeode;
}



int main( int argc, char **argv )
{
    osg::ArgumentParser arguments(&argc, argv);

    // set up the usage document, in case we need to print out how to use this program.    
    arguments.getApplicationUsage()->addCommandLineOption("-h or --help", "Display this information");
   

    osgViewer::Viewer viewer(arguments);

    // add the state manipulator
    //viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );    
    // add the thread model handler
    viewer.addEventHandler(new osgViewer::ThreadingHandler);
    // add the window size toggle handler
    viewer.addEventHandler(new osgViewer::WindowSizeHandler);        
    // add the stats handler
    viewer.addEventHandler(new osgViewer::StatsHandler);
    // add the help handler
    viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));

    viewer.setSceneData( createTwoQuads());

    return viewer.run();
}
