/*
#  $Id$
# -----------------------------------------------------------------------------
#  The part of 'IP cameras retrival' project
# -----------------------------------------------------------------------------
#  Author: Petrov Maxim, 12/04/2009
#  Edited by:
#  QA by:
#  Copyright: videoNEXT LLC
# -----------------------------------------------------------------------------
*/

#include "VideoFrameRateController.h"

namespace videonext { namespace media {

/*static*/VideoFrameRateController *VideoFrameRateController::instance()
{
   static VideoFrameRateController *singleton = 0; 
   static Mutex singleton_mutex;

   // Perform the Double-Check pattern...
   if (singleton == 0)
   {
      MutexGuard guard(singleton_mutex);
      
      if (singleton == 0)
      {
         singleton = new VideoFrameRateController;
      }
   }

   return singleton;
}

VideoFrameRateController::VideoFrameRateController()
   : fMaxFrameRate(120.0f), fCurrentFrameRate(0.0f)
   , fFrameRatePerConsumer(fMaxFrameRate), fLastFrameRateMeasureTime(time(0) + 1)
{
}

VideoFrameRateController::~VideoFrameRateController()
{
}

void VideoFrameRateController::setMaxFrameRate(float value)
{
   if (value != fMaxFrameRate)
   {
      MutexGuard g(fConsumerMutex);
      if (value != fMaxFrameRate)
      {
#ifdef DEBUG
         fprintf(stderr, "VideoFrameRateController::setMaxFrameRate(%f)\n", value);
#endif
         fMaxFrameRate = value;
         if (fConsumerTimers.size()) resetTimers();
      }      
   }
}

void VideoFrameRateController::addConsumer(VideoFrameConsumer *consumer)
{
   MutexGuard g(fConsumerMutex);
   
   fConsumerTimers.insert(std::make_pair(consumer, videonext::media::Timeval()));

   resetTimers();
#ifdef DEBUG
   fprintf(stderr, "VideoFrameRateController::addConsumer() Count: %d\n", consumerTimers_.size());
#endif
}

void VideoFrameRateController::removeConsumer(VideoFrameConsumer *consumer)
{
   MutexGuard g(fConsumerMutex);

   fConsumerTimers.erase(consumer);

   if (fConsumerTimers.size()) resetTimers();
#ifdef DEBUG
   fprintf(stderr, "VideoFrameRateController::removeConsumer() Count: %d\n", consumerTimers_.size());
#endif
}

bool VideoFrameRateController::commitFrame(VideoFrameConsumer *consumer, CPMediaFrame&)
{   
   bool result = true;
   MutexGuard g(fConsumerMutex);

   ConsumerTimers::iterator iter = fConsumerTimers.find(consumer);
   if (iter == fConsumerTimers.end()) // should not happen
   {
#ifdef DEBUG
      fprintf(stderr, "VideoFrameRateController::check(): Consumer NOT FOUND\n");
#endif
      result = false;
   }
   else
   {
      videonext::media::Timeval tv;
      if (tv < iter->second)
      {
//         fprintf(stderr, "VideoFrameRateController::check(0x%08lx) result false(%ld.%ld < %ld.%ld)\n", 
//                 consumer, tv.seconds(), tv.useconds(), iter->second.seconds(), iter->second.useconds());
       
         result = false;
      }
      else
      {
         // Set next expected time
         iter->second = iter->second + videonext::media::DelayInterval(0, videonext::media::MILLION/fFrameRatePerConsumer);
         fCurrentFrameRate += 1.0f;
         result = true;
      }
   }

   
   if (fLastFrameRateMeasureTime < time(0))
   {
#ifdef DEBUG
      fprintf(stderr, "Current framerate: %f\n", fCurrentFrameRate);
#endif  
      fLastFrameRateMeasureTime = time(0);
      fCurrentFrameRate = 0.0f;
   }

   return result;
  
}

void VideoFrameRateController::resetTimers()
{
   float newFrameRatePerConsumer = fMaxFrameRate / fConsumerTimers.size();
   
   float diff = fFrameRatePerConsumer - newFrameRatePerConsumer;

   fFrameRatePerConsumer = newFrameRatePerConsumer;
#ifdef DEBUG
   fprintf(stderr, "VideoFrameRateController::resetTimers() frameRatePerConsumer_: %f, diff: %f \n", frameRatePerConsumer_, diff);
#endif
   if (diff > 0.0f)
   {
      ConsumerTimers::iterator pos = fConsumerTimers.begin();
      ConsumerTimers::iterator end = fConsumerTimers.end();

      for (; pos != end; ++pos)
      {
//         pos->second += videonext::media::DelayInterval(0, videonext::media::MILLION/diff);
         pos->second = videonext::media::Timeval();
      }
   }

}

}}
