#ifndef DEVICE_H
#define DEVICE_H

#include <vdr/plugin.h>
#include <vdr/thread.h>
#include <vdr/device.h>
#include "libdvb_parts.h"
#include "helpfunc.h"


#undef  DBG // PTS/DTS debugging

// TS:
#define TRANSPORT_ERROR     0x80
#define PAYLOAD_START	    0x40 // PUSI: Payload unit start indicator
#define PID_MASK_HI	    0x1F
#define CONTINUITY_MASK	    0x0F 
#define ADAPTION_FIELD	    0x20
#define PAYLOAD		    0x10
#define PCR_FLAG       	    0x10
#define OPCR_FLAG      	    0x08

// PES: 
#define GROUP_START_CODE    0xB8
#define PROG_STREAM_MAP     0xBC
#define PRIVATE_STREAM1     0xBD
#define PADDING_STREAM      0xBE
#define PRIVATE_STREAM2     0xBF
#define AUDIO_STREAM_S      0xC0
#define AUDIO_STREAM_E      0xDF
#define VIDEO_STREAM_S      0xE0
#define VIDEO_STREAM_E      0xEF
#define ECM_STREAM          0xF0
#define EMM_STREAM          0xF1
#define DSM_CC_STREAM       0xF2
#define ISO13522_STREAM     0xF3
#define PROG_STREAM_DIR     0xFF

#define PTS_DTS_FLAGS       0xC0
#define PTS_ONLY            0x80
#define PTS_DTS             0xC0

#define MAX_PTS             (0x0000000200000000ULL)
#define MAXWARN             5
#define MAXRETRIES          3
#define SOURCE              "P"
#define TS                  0x47 // Synchronisation Byte
#define TYPE_VPID           5
#define TYPE_APID           6
#define TYPE_TPID           7

class cEncoder;

typedef struct
  {
  int   videodev;
  int   fd;
  char  name[64];
  int   index;
  int   composite0;
  int   composite1;
  int   composite2;
  int   composite3;
  int   composite4;  
  int   svideo0;
  int   svideo1;
  int   svideo2;
  int   tuner0;
  int   hasRadio;
  int   Apid;
  int   Vpid;
  int   Ppid;
  int   Tpid;
  } CARDS; 

class cPTV_Device : public cDevice
  {
  private:
    cEncoder    *encoder;
    CARDS       card;
    long long unsigned int bytestotal;
    int         skipped;
    int         dup; 
    int         af, vf, pf, sf;
    int         TSerrors, CCErrors, LastCC;
    bool        dropAudio, droppedAudio; 
    bool        bufferFilled; 
    uchar       buf[TS_SIZE];
    cTSBuffer   *tsBuffer;
    uint64_t    pts[3], last_pts[3];
    uint64_t    dts[3], last_dts[3];
    uint64_t    firstVideoPts;
    uint8_t     LastPts[3][5];
    uint8_t     pcr[6], opcr[6];
    int         dropPES;
    int         vdr_ready;       // vdr accepts TS packets
    int         pid;
    p2p         p2t;
    unsigned char *rbuffer[NUM_BUFS];
    unsigned int buf_read;
    unsigned int buf_write;
    unsigned int buf_read_pos;
    unsigned int buf_write_pos;
    int full_buffers;
    int buffered_bytes;
    int max_used;
    int mutex;
    int ts, pes;
    long long unsigned int sts, spes;
    int ringbuffer_warned, ready_warned, read_warned;
  protected:
    virtual bool     SetPid                     (cPidHandle *Handle, int Type, bool On);
    virtual bool     OpenDvr                    (void);
    virtual void     CloseDvr                   (void);
    virtual uint64_t transPtsDts                (uint8_t *pts);
    virtual bool     getPES                     (uchar *b, int l);
    virtual unsigned short int getPid           (void);
    virtual bool     validPid                   (unsigned short int pid);
    virtual bool     getTS                      (void);
    virtual bool     GetTSPacket                (uchar *&Data);
            void     report                     (int rmax, int dropped);
            int      ReadBuffer                 (unsigned char *data, int len);
            void     WriteBuffer                (unsigned char *data, int len, void *p);
            void     init_p2p                   (p2p *p, int repack);
            void     pes_in_ts                  (p2p *p);
            int      write_ts_header            (uint16_t pid, uint8_t *counter, \
                                                int pes_start, uint8_t *buf, uint8_t length);
            void     get_pes                    (uint8_t *buf, int count, p2p *p);
            bool     setup                      (int freq, const char *name);
  
  public:
    /* *******************device related ****************************** */
                     cPTV_Device                (int index, int dev);
    virtual          ~cPTV_Device               ();
    virtual bool     ProvidesSource             (int Source) const;
    virtual bool     ProvidesTransponder        (const cChannel *Channel) const;
    virtual bool     ProvidesChannel            (const cChannel *Channel, int Priority = -1, \
                                                 bool *NeedsSetChannel = NULL) const;
    virtual int      ProvidesCa                 (const cChannel *Channel) const;
    virtual bool     SetChannelDevice           (const cChannel *Channel, bool LiveView);
            int      ReadPVR                    (int fd, int Apid, int Vpid);
            int      DeviceApid                 (void) {return card.Apid;};
            int      DeviceVpid                 (void) {return card.Vpid;};
            int      getCardHandle              (void) {return card.fd;};
            void     StopEncoder                ();
    virtual bool     HasEncoder                 (void);
    virtual bool     EncoderIsRunning           (void);
    virtual bool     EncoderIsNeeded            (void);
  };

class cEncoder : public cThread
  {
  private:
    int         ReaderLoopRunning;
    int         EncoderRunning;
    int         EncoderNeeded; // true. if watching/recording on this device
    cPTV_Device* parent;
    virtual void Action(void);
  public:
    cEncoder(cPTV_Device* Parent);
    ~cEncoder(void);
    bool     IsNeeded           (void)      {return (EncoderNeeded)? true:false;};
    bool     IsRunning          (void)      {return (EncoderRunning)?true:false;};
    void     Needed             (int state) {EncoderNeeded = state;}; 
    bool     ReaderLoop         (void)      {return (ReaderLoopRunning)?true:false;};
    void     SetReaderLoop      (int state) {ReaderLoopRunning = state;};         
  };



#endif // DEVICE_H
