// -*-c++-*-

/*
* Copyright (C) 2004 Stephan Huber http://digitalmind.de
*
*
* The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for 
* real-time rendering of large 3D photo-realistic models. 
* The OSG homepage is http://www.openscenegraph.org/
* 
* This software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "QuicktimeImageStream.h"
#include <osg/Notify>
#include <osg/Timer>
#include <osg/Math>

#include <OpenThreads/ScopedLock>
#include <OpenThreads/Thread>

#include "QTUtils.h"
#include "MovieData.h"



#define IDLE_TIMEOUT 150000L

int QuicktimeImageStream::_qtInstanceCount = 0;

// Constructor: setup and start thread
QuicktimeImageStream::QuicktimeImageStream(std::string fileName) : ImageStream()
{
    setOrigin(osg::Image::TOP_LEFT);

    _len = 0;
    _movieData = new MovieData();

    for (int i = 0; i < NUM_CMD_INDEX; i++)
      _cmd[i] = THREAD_IDLE;
    _wrIndex = _rdIndex = 0;

    load(fileName);

    if (!fileName.empty())
      setFileName(fileName);

    // ricky
    _status = ImageStream::PAUSED;
}


// Deconstructor: stop and terminate thread
QuicktimeImageStream::~QuicktimeImageStream()
{
   if( isRunning() )
   {
      quit(true);
   }

   
   // clean up quicktime movies.
   delete _movieData;
   
}


// Set command
void QuicktimeImageStream::setCmd(ThreadCommand cmd, float rate)
{
   OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);

   _cmd[_wrIndex] = cmd;
   _rates[_wrIndex] = rate;
   _wrIndex = (_wrIndex + 1) % NUM_CMD_INDEX;
}


// Get command
QuicktimeImageStream::ThreadCommand QuicktimeImageStream::getCmd()
{
   ThreadCommand cmd = THREAD_IDLE;

   OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);

   if (_rdIndex != _wrIndex) {
      cmd = _cmd[_rdIndex];
      _currentRate = _rates[_rdIndex];
      _rdIndex = (_rdIndex + 1) % NUM_CMD_INDEX;
   }

   return cmd;
}


void QuicktimeImageStream::load(std::string fileName)
{
   osg::notify(osg::DEBUG_INFO) << "QT-ImageStream: loading quicktime movie from " << fileName << std::endl;

   _movieData->load(this, fileName);

   _len = _movieData->getMovieDuration();
   _current = 0;
}

void QuicktimeImageStream::quit(bool wiatForThreadToExit)
{
   osg::notify(osg::DEBUG_INFO)<<"Sending quit"<<this<<std::endl;
   setCmd(THREAD_QUIT);

   if (wiatForThreadToExit)
   {
      while(isRunning())
      {
         osg::notify(osg::DEBUG_INFO)<<"Waiting for QuicktimeImageStream to quit"<<this<<std::endl;
         OpenThreads::Thread::YieldCurrentThread();
      }
      osg::notify(osg::DEBUG_INFO)<<"QuicktimeImageStream has quit"<<this<<std::endl;
   }
}

void QuicktimeImageStream::setVolume(float volume)
{
    _movieData->setVolume(osg::minimum(osg::maximum(volume,0.0f),1.0f));
}

// Get and Set the playback volume of the stream.
float QuicktimeImageStream::getVolume() const
{
    return _movieData->getVolume();
}

void QuicktimeImageStream::run()
{

   bool playing = false;
   bool done = false;

   while (!done) {


      ThreadCommand cmd = getCmd();
      osg::notify(osg::DEBUG_INFO) << "movietime: " << _movieData->getMovieTime() << " rate: " << _movieData->getMovieRate() << " state " << cmd << " playing: " << playing << " done " << done << "  " << _wrIndex << "/" << _rdIndex << std::endl;        
      // Handle commands               
      {
         if (cmd != THREAD_IDLE) {
            osg::notify(osg::DEBUG_INFO) << "new cmd: " << cmd << std::endl;
            switch (cmd) {
                    case THREAD_START: // Start or continue stream
                       applyLoopingMode();    
                       _movieData->setMovieRate(1.0f);

                       playing = true;
                       break;

                    case THREAD_STOP:
                       _movieData->setMovieRate(0);
                       osg::notify(osg::INFO) << "QT-ImageStream: stop at "<< std::endl;
                       playing = false;
                       break;

                    case THREAD_REWIND:
                       SetMovieRate(_movieData->getMovie(),0);
                       GoToBeginningOfMovie(_movieData->getMovie());
                       break;

                    case THREAD_FORWARD:
                       SetMovieRate(_movieData->getMovie(),0);
                       GoToEndOfMovie(_movieData->getMovie());
                       break;

                    case THREAD_SEEK:
                       _movieData->setMovieTime(_currentRate);
                       playing = true;
                       break;

                    case THREAD_SETRATE:
                       _movieData->setMovieRate(_currentRate);
                       playing = (_currentRate != 0.0f);
                       break;

                    case THREAD_CLOSE:
                       _movieData->setMovieRate(0);
                       break;

                    case THREAD_QUIT: // TODO
                       _movieData->setMovieRate(0);
                       osg::notify(osg::INFO) << "QT-ImageStream: quit" << std::endl;
                       //playing = false;
                       done = true;
                       break;
                    default:
                       osg::notify(osg::WARN) << "QT-ImageStream: Unknown command " << cmd << std::endl;
                       break;
            }
         }

         MoviesTask(_movieData->getMovie(),0);
         _current = _movieData->getMovieTime();
      }


      if (_lastUpdate!= _current) 
      {
         // force the texture to update the image
         dirty();
         // update internal time and take care of looping
         _lastUpdate = _current;
      }

      if (playing)
      {
         OpenThreads::Thread::microSleep(16000);
      }
      else if (!done)
      {
         OpenThreads::Thread::microSleep(IDLE_TIMEOUT);
      }
   }


}


void QuicktimeImageStream::applyLoopingMode()
{
    osg::notify(osg::INFO) << "applying loop mode " << getLoopingMode() << std::endl;
    _movieData->setLooping(getLoopingMode() == osg::ImageStream::LOOPING);
}
