/*
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * Author:
 *         Roman Krenicky
 *
 * $Id: controller.h,v 1.16 2005/11/23 17:51:35 kutzner Exp $
 *
 */

/*
  controller.h
  created: 1.12.2004
  last change: 29.7.2005
*/
/*! @file
    the logic stuff is done here (i.e. decisions, when to do what, how to react
    on events, ...)
*/

#ifndef _CONTROLLER_H
#define _CONTROLLER_H


#include "globalheader.h"

#include <list>
#include <set>
#include <thread.h>
#include "receiver.h"
#include "epgtypes.h"
#include "epgigor_interface.h"
#include "vdrepg_interface.h"


//! number of retransmissions for requests which haven't been satisfied
#define  REQUEST_BACKOFF_DEPTH   6



/*! the @c controller is the acting part of the plugin, communicating with the
    @c epg_interface and the @c epgigor_interface

    on the one hand it occupies a separate thread, sleeping most of its time
    and sending out data from time to time, on the other hand it provides
    callback functions to react on events (e.g. incoming messages)
*/
class controller : public cThread, public receiver
{
  public:
   //! a type for internal requests to the controller (sent by other threads)
   enum ir_type { ir_none,       //!< no request
                  ir_scan,       //!< get missing EPG data from the net
                  ir_reconnect   //!< reconnect to IGOR (if disconnected)
                };

  private:
   //! global vdr EPG data is accessed via @c epg_access
   vdrepg_interface &epg_access;
   //! IGOR communication is done via @c igor_access
   epgigor_interface &igor_access;

   /*! EPG blocks we requested for

       the blocks in @c requested_blocks[i] have been requested for i times,
       i.e. the requests have been retransmitted i-1 times (blocks in
       requested_blocks[0] are still waiting for their first transmission,
       the ones in requested_blocks[<MAX>] should not be retransmitted any more

       no block shall be contained twice
   */
   set<blockentry> requested_blocks[REQUEST_BACKOFF_DEPTH+2];
   //! mutex for @c requested_blocks
   cMutex requested_blocks_mutex;

   //! EPG blocks to send before picking a random one
   list<blockentry> block_send_queue;
   //! mutex for @c block_send_queue
   cMutex block_send_queue_mutex;

   //! internal requests to the controller are notified by this object
   cCondVar internal_request_cond;
   //! the (last) internal request to the controller
   ir_type internal_request;
   //! mutex for internal request notifications (i.e. for
   //! @c internal_request_cond and @c internal_request)
      // INTERNAL NOTE: don't lock this mutex for long, so that
      //                controller::controller_request() can return fast !
   cMutex internal_request_mutex;

   //! requests received in the current period (= some time window)
   unsigned int incoming_request_count;
   //! start time of the current period for incoming request
   time_t request_period_start;
   //! data messages received in the current period (= some time window)
   unsigned int incoming_data_count;
   //! start time of the current period for incoming data messages
   time_t data_period_start;

   //! time, when unsatisfied future requests have been reset for
   //! retransmission the last time
   time_t last_request_reset;

   //! summarised size of response messages for request-answer-size (ras)
   //! calculation in a certain period
   unsigned long ras_answer_bytes;
   //! number of requests sent in a certain period; for request-answer-size
   //! (ras) calculation
   unsigned int ras_request_count;
   //! bytes of answer messages per request (sliding average, including
   //! non-responded requests)
   size_t request_answer_size;
   //! summarised size of update response messages for update-answer-size (uas)
   //! calculation in a certain period
   unsigned long uas_answer_bytes;
   //! number of update requests sent in a certain period;
   //! for update-answer-size (uas) calculation
   unsigned int uas_request_count;
   //! bytes of update answer messages per request (sliding average, including
   //! non-responded requests)
   size_t update_answer_size;


  private:
   unsigned int epglinger2past_blocks (int epglinger);
   void enter_incomplete_blocks (unsigned int past_blocks,
                                 unsigned int future_blocks);
   void remove_obsolete_incomplete_blocks (unsigned int past_blocks,
                                           unsigned int future_blocks);
   void remove_unsatisfied_blocks (unsigned int past_blocks);
   void transmit_requests (unsigned int depth);
   void send_a_block (blockentry *seed);
   void update (unsigned int past_blocks, unsigned int future_blocks);
   float next_request_rate (float data_rate, float bandwidth,
                            float current_request_rate, bool regular);


  protected:
   virtual void Action (void);

  public:
   controller (vdrepg_interface &init_epg_access,
               epgigor_interface &init_igor_access);
   virtual ~controller (void);
   void controller_request (ir_type request);
   void Stop (void);
   virtual void receive_data (const epgchannel &channel, time_t timeblock,
                              bool update, const epgdata &data, bool final);
   virtual void receive_req (const epgchannel &channel, time_t timeblock,
                             bool update, const sa_igor &requestor, bool final);
}; // controller


#endif // _CONTROLLER_H
