/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *  
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *  
 * This file is part of NMM. 
 * 
 * NMM 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; only version 2 of the License.      
 * 
 * NMM 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 NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */


#ifndef _NMM_FILEBUFFER_HPP_
#define _NMM_FILEBUFFER_HPP_

#include "nmm/nmm_prefix.hpp"

#include "nmm/utils/thread/ThreadMutex.hpp"
#include "nmm/utils/time/Time.hpp"

#include <string>
#include <list>
#include <fstream>

//! This class implements a extended FIFO-buffer.
/*!
 *  A file, which is divided in blocks of fixed size (block_size),
 *  is used to buffer data. If a block is read (by calling popData())
 *  and the file contains more than keep_min blocks the block is
 *  marked free and reused by a following pushData(). It is also
 *  possible to skip data-blocks in forward or backward direction.
 */
class FileBuffer {
  size_t block_size;   //!< the size of each data-block
  size_t block_num;    //!< absolute number of blocks in file. Used to compute EOF
  NMM::Time start_time;
  NMM::Time end_time;
  size_t file_block_size; //!< the size of a block including the header
  unsigned int keep_min;  //!< keep that much blocks before reusing one

  /*!
   * this structure represents a data-block
   */
  struct Block {
    size_t id;           //!< defines the position where the block can be found in the file
    size_t size;         //!< the size of the block
    bool already_read;   //!< if true the block might be reused
    bool is_event;       //!< indicates that the block contains an event (and no data)
    NMM::Time timestamp;
  };

  bool skiperror;      //!< flag indicating that a skip reached EOF

  //        list<size_t>  free_blocks;   //!< contains id's of already read blocks
  std::list<Block>        used_blocks;   //!< contains blocks that can be read
  std::list<Block>::iterator read_pos;   //!< read_position in used_blocks list
  bool keep_data;                   //!< if true blocks will not be reused
  
  NMM::ThreadMutex buffer_mutex;
  
  std::fstream*  o_file;  //!< used for i/o
  //fstream*  d_file;  //!< used for debug-output
  std::string filename;   //!< the name of the file used for buffering

  char* ctemp;   //!< needed to convert a long to a char[sizeof(long)]

public:
  /*!
   * create a buffer containing blocks of data.
   * \param size the size of each block
   * \param name the name of the file used for buffering
   * \param keep keep that much blocks before the oldest is freed
   */
  FileBuffer(size_t size, std::string name = "tsbuffer", unsigned int keep = 0);

  FileBuffer(size_t size, unsigned int keep);

  //! destroy the buffer (and delete the associated file if keep_data is false)
  ~FileBuffer();

  /*!
   * the two self-explanatory positions BEGINNING and END
   */
  enum Position_t {BEGINNING,END};

  //bool debug;

  //! \return the number of data-blocks currently in use
  size_t getUsed() const {return used_blocks.size();}

  //! \return the number of data-blocks currently in use
  //size_t getFree() const {return free_blocks.size();}

  //! \return the absolute number of data-blocks in file
  size_t getSize() const {return block_num;}

  //! \return amount of time in the buffer
  NMM::Interval getIntervalSize() const;

  //! \return the (absolute) time, at which the buffer starts
  NMM::Time getStartTime() const;

  //! push size bytes in the buffer
  void pushData(const char* data,size_t size,bool is_event, NMM::Time timestamp);

  /**
   * pop data out of the buffer.
   * \param data a pointer to a memory-segment to store the data
   * \param skip_blocks discard blocks after reading
   * \param is_event true if returned data is an event
   * \param timestamp timecode of the block
   * \return the number of bytes that have been read
   */
  size_t popData(char* data, bool& is_event, NMM::Time& timestamp,int skip_blocks = 0);

  /**
   * \param is_event true if next block is an event
   * \return the number of bytes needed to read the next block
   */
  size_t getNextBlockSize(bool& is_event);

  /**
   * \param amount: skip that much blocks
   * \return false if EOF discovered
   */
  bool skipBlocks(int amount);

  /**
   * set the read-position to the first/last valid block
   * \return false if there is no valid block which is older than the actual read-position
   */
  bool setReadPosition(Position_t pos = BEGINNING);

  //! set status of keep_data-flag (if true blocks are never freed after reading)
  void setKeepData(bool keep = true);

  //! get status of keep_data-flag
  const bool getKeepData();

  //! save all data currently in the buffer-file correct ordered to a new file
  bool saveToFile(std::string filename);

  void printUsed(); //!< print debug output (list of used blocks)

};

#include "nmm/nmm_suffix.hpp"

#endif
