#include <osg/ComputeBoundsVisitor>
#include <osg/io_utils>
#include <osg/Notify>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include "ReaderWriterPOV.h"
#include "POVWriterNodeVisitor.h"

using namespace std;
using namespace osg;


// Register with Registry to instantiate the Povray writer.
REGISTER_OSGPLUGIN( Povray, ReaderWriterPOV )


/**
 * Constructor.
 * Initializes the ReaderWriterPOV.
 */
ReaderWriterPOV::ReaderWriterPOV()
{
    // Set supported extensions and options
    supportsExtension( "pov", "POV-Ray format" );
}


static osgDB::ReaderWriter::WriteResult
writeNodeImplementation( const Node& node, ostream& fout,
                         const osgDB::ReaderWriter::Options* options )
{
   // get camera on the top of scene graph
   const Camera *camera = dynamic_cast< const Camera* >( &node );

   Vec3d eye, center, up, right;
   double fovy, aspectRatio, tmp;
   if( camera ) {

      // view matrix
      camera->getViewMatrixAsLookAt( eye, center, up );
      up = Vec3d( 0.,0.,1. );
      right = Vec3d( 1.,0.,0. );
      //up.normalize();
      //right = (center-eye) ^ up;
      //right.normalize();

      // projection matrix
      camera->getProjectionMatrixAsPerspective( fovy, aspectRatio, tmp, tmp );
      right *= aspectRatio;

   } else {

      // get POV-Ray camera setup from scene bounding sphere
      // constructed from bounding box
      // (bounding box computes model center more precisely than bounding sphere)
      ComputeBoundsVisitor cbVisitor;
      const_cast< Node& >( node ).accept( cbVisitor );
      BoundingBox &bb = cbVisitor.getBoundingBox();
      BoundingSphere bs( bb );

      // set
      eye = bs.center() + Vec3( 0., -3.0 * bs.radius(), 0. );
      center = bs.center();
      up = Vec3d( 0.,1.,0. );
      right = Vec3d( 4./3.,0.,0. );

   }

   // camera
   fout << "camera { // following POV-Ray, x is right, y is up, and z is to the scene" << endl
        << "   location <" << eye[0] << ", " << eye[2] << ", " << eye[1] << ">" << endl
        << "   up <" << up[0] << ", " << up[2] << ", " << up[1] << ">" << endl
        << "   right <" << right[0] << ", " << right[2] << ", " << right[1] << ">" << endl
        << "   look_at <" << center[0] << ", " << center[2] << ", " << center[1] << ">" << endl
        << "}" << endl
        << endl;

   POVWriterNodeVisitor povWriter( fout, node.getBound() );
   if( camera )

      for( int i=0, c=camera->getNumChildren(); i<c; i++ )
         const_cast< Camera* >( camera )->getChild( i )->accept( povWriter );

   else

      const_cast< Node* >( &node )->accept( povWriter );

   notify( NOTICE ) << "ReaderWriterPOV::writeNode() Done. ("
                    << povWriter.getNumProducedTriangles()
                    << " triangles written)" << endl;

   return osgDB::ReaderWriter::WriteResult::FILE_SAVED;
}


osgDB::ReaderWriter::WriteResult
ReaderWriterPOV::writeNode( const Node& node, const string& fileName,
                            const osgDB::ReaderWriter::Options* options ) const
{
   // accept extension
   string ext = osgDB::getLowerCaseFileExtension( fileName );
   if( !acceptsExtension( ext ) )  return WriteResult::FILE_NOT_HANDLED;

   notify( NOTICE ) << "ReaderWriterPOV::writeNode() Writing file " << fileName << endl;

   osgDB::ofstream fout( fileName.c_str(), ios::out | ios::trunc );
   if( !fout )
      return WriteResult::ERROR_IN_WRITING_FILE;
   else
      return writeNodeImplementation( node, fout, options );
}


osgDB::ReaderWriter::WriteResult
ReaderWriterPOV::writeNode( const Node& node, ostream& fout,
                            const osgDB::ReaderWriter::Options* options ) const
{
   notify( osg::NOTICE ) << "ReaderWriterPOV::writeNode() Writing to "
                         << "stream" << endl;

   return writeNodeImplementation( node, fout, options );
}
