/* 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_DVBREADNODE_HPP
#define NMM_DVBREADNODE_HPP

#include "nmm/nmm_prefix.hpp"

#include "nmm/multimedia/GenericSourceNode.hpp"
#include "nmm/multimedia/sync/StreamTimer.hpp"
#include "nmm/plugins/video/tv/interfaces/ITVCardImpl.hpp"
#include "nmm/plugins/video/tv/interfaces/IEPGImpl.hpp"
#include "nmm/plugins/video/tv/interfaces/tvguide/ITVGuideProducerImpl.hpp"
#include "nmm/plugins/video/tv/interfaces/IChannelInfoImpl.hpp"
#include "nmm/utils/Singleton.hpp"

#include <linux/videodev.h>
#include <queue>
#include <string>

namespace VDR {
  class cRecordBuffer;
  class cDvbApi;
  class cSchedules;
  class cEITScanner;
}

namespace NMM {

  //! This node receives digital-video-broadcasts from satellite via a DVB-compatible PCI-Cards
  /*!
   * Two different output-formats are supported:
   *   - raw YV12 planar (video only)
   *   - mpeg-pes stream
   * You can also run multiple instances of this node parallel
   * (for example to write mpeg to disk and watch a harware-decoded picture).
   * It uses a channel-conf-file for channel configuration and the file
   * setup.conf for primary LNB-setup (only LnbSLOF,LnbFrequLo and LnbFrequHi are used)
   */
  class DVBReadNode 
    : public GenericSourceNode,
      public ITVCardImpl,
      public IEPGImpl,
      public ITVGuideProducerImpl,
      public IChannelInfoImpl
  {

  protected:

    bool format_mpeg;                //!< true if output-format is mpeg
    bool format_raw;                 //!< true if output-format is yuv
    bool running;
    bool is_producing_mpeg_buffers;  //!< true if mpeg-buffers are enqueued by the dvbapi
    bool send_format;
    bool dvb_is_initialized;  //!< needed to init/deinit dvbapi
    bool dvb_raw_is_initialized;     //!< needed to init/deinit video-4-linux device

    int  node_count;          //!< count the number of instances of this node

    bool sync_event_sent;
    int frame;

    //! buffers from the dvbapi are collected in this queue and sent by the produceBuffer-method
    std::queue<Buffer*> buffer_queue;

    //! clear the buffer_queue
    void clearQueue();

    // YUV capture capabilities
    int v_fd;	//!< file-descriptor to framegrabber-device (to get the decoded pictures)
    int height; //!< the height of the decoded picture
    int width;  //!< the width of the decoded picture
    int depth;  //!< bitdepth of the decoded picture (YUV422)
    int format;
    int size;
    int maxframes;
    unsigned char *cp_buffer;
    struct video_window vidwin;
    struct video_picture vidpict;
    struct video_mbuf vidmbuf;
    struct video_mmap vidmmap;
    string device;

    string __directory;
    
    /**
     * converts packed422 YUV-video-data from the DVB-card to
     * planar411 needed by the X-display
     */
    void packed422_to_planar411(char *dest, char *src, int width_, int heigth_);

    Message* processBuffer(Buffer *);      //!< calls produceYUVBuffer or produceMPEGBuffer depening on choosen format
    Message* produceMPEGBuffer();  //!< sends buffers produced by the dvbapi
    Message* produceYUVBuffer();   //!< sends captured YUV-frames (using video-4-linux)

    bool switched_channel;  //!< indicates that a channel-switch occured
    bool send_channel_name_yuv;
    int buffers_sent;             //!< buffer-counter (only used to discard first buffer after channel-switch)
    StreamTimer stream_timer;

    /**
     * start/stop producing mpeg-data.
     * this calls VDR's StartRecord without a filename and thus disables output to disk.
     */
    void produceMPEG();

    // do-method implementations from GenericNode
    Result doInit();
    Result doInitOutput();
    Result doActivate();
    Result doDeactivate(); //! stop dvbapi
    Result doDeinit(); //! close v4l device
    Result doFlush(); //! clear the buffer_queue


    //! sends picture width and height as 'resolution_changed'-event (YUV only)
    void sendFormat();

    //! save last active channel in file 'setup.conf'
    void save();


    //! enqueue a mpeg-buffer
    /*! this is only used by the dvbapi.
     *  every enqueued buffer should start with a I-frame to keep it independent playable.
     */
    void enqueue(Buffer* buffer) {
      setProducingFlag(true);
      buffer_queue_mutex.lock();
      buffer_queue.push(buffer);
      buffer_queue_mutex.unlock();
    }


    //ITVCard interface
    virtual void setDevice(const string&) throw(const DeviceNotFoundException&); /* limited(INode::CONSTRUCTED) */
    
    virtual string getDevice() const;
    
    virtual void setResolution(const nmm_int32& , const nmm_int32& ) /* limited(INode::CONSTRUCTED) */ /* limited(INode::INITIALIZED) */ { }

    virtual void getResolution(nmm_int32& x, nmm_int32& y) { x = 768; y = 576; }

    virtual Result setChannel(const nmm_int32&) /* limited(INode::INITIALIZED) */ /* limited(INode::ACTIVATED) */ /* limited(INode::STARTED) */ /* limited(INode::OUTPUT_INITIALIZED) */;

    virtual Result getChannel(nmm_int32&, string&);

    //IEPG Interface
    virtual string getCurrentShow();
    virtual string getFollowingShow();
    virtual string getDetailedDescription();
    virtual void setEPGDirectory(const string& directory);
    virtual void getActivity();
    virtual void scan();

    //ITVGuideProducer Interface
    virtual void registerTVGuide(const ITVGuide* itvgs);
    virtual void unregisterTVGuide(const ITVGuide* itvgs);

    ThreadMutex tuner_mutex; // this mutex prevents from channel-switching while a frame is captured and vice versa
    ThreadMutex buffer_queue_mutex;
    ThreadMutex init_mutex;   //!< manages DVB-initialization
    ThreadMutex eit_mutex;

    VDR::cEITScanner*         eitScanner; 


  public:

    DVBReadNode(const char* name = "DVBReadNode",
		StreamQueue::Mode = StreamQueue::MODE_SUSPEND,
		int = StreamQueue::DEFAULT_SIZE);
    ~DVBReadNode();

    ITVGuide* itvg;


    friend class VDR::cRecordBuffer;  // for buffer enqueueing via DVBReadNode::enqueue() from vdr
    friend class VDR::cDvbApi;        // for flushing DVBReadNode from vdr
    
    //IChannelInfo interface
    virtual Result hasChannel(nmm_int32& , const string& ){return FAILURE;}
    virtual TVChannelList getChannelList();
    virtual void setChannelConfigFile(const string &filename);

  private:
    TVChannelList channels;
    string channelConfigFile;
    void loadChannelconfig();

  };
}

#include "nmm/nmm_suffix.hpp"

#endif //NMM_DVBREADNODE_HPP
