/*
 * gstreamer.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

#include <signal.h>
#include <unistd.h>

#include <vdr/device.h>



class cGstreamerDevice : public cDevice
{
  int fd_audio;
  int fd_video;
  
  virtual bool HasDecoder(void) const
  {
    return true;
  }

  virtual bool CanReplay(void) const
  {
    return true;
  }

  virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);  
  virtual int PlayVideo(const uchar *Data, int Length);

  void write(int &fd, const char *fifoName, const uchar *data, int length);

  cGstreamerDevice()
    : cDevice()
    , fd_audio(-1)
    , fd_video(-1)
  {
  }

  ~cGstreamerDevice()
  {
    if (-1 != fd_audio)
      ::close(fd_audio);
    
    if (-1 != fd_video)
      ::close(fd_video);
  }
  
public:
  static bool Create()
  {
    return 0 != new cGstreamerDevice();
  }
};

bool cGstreamerDevice::Poll(cPoller &Poller, int TimeoutMs /* = 0 */)
{
  if (-1 == fd_audio
      && -1 == fd_video)
  {
    return true;
  }
      
  if (-1 != fd_audio)
    Poller.Add(fd_audio, true);

  if (-1 != fd_video)
    Poller.Add(fd_video, true);
  
  if (Poller.Poll(TimeoutMs))
    return true;

  ::fprintf(stderr, "P");

  return false;
}

int cGstreamerDevice::PlayVideo(const uchar *Data, int Length)
{
  if (Length >= 4)
  {
    if (0x00 == Data[ 0 ]
        && 0x00 == Data[ 1 ]
        && 0x01 == Data[ 2 ])
    {
      if (0xe0 == (0xf0 & Data[ 3 ]))
      {
        ::fprintf(stderr, "V");
        write(fd_video, "/tmp/vdr.video", Data, Length);
      }
      else if (0xc0 == (0xe0 & Data[ 3 ]))
      {
        ::fprintf(stderr, "A");
        write(fd_audio, "/tmp/vdr.audio", Data, Length);
      }
      else
      {
        ::fprintf(stderr, "U");
      }
    }
    else
    {
      ::fprintf(stderr, "X");
    }      
  }
  else
  {
    ::fprintf(stderr, "L");
  }

  return Length;
}

void cGstreamerDevice::write(int &fd, const char *fifoName, const uchar *data, int length)
{
  if (-1 == fd)
  {
    fd = ::open(fifoName, O_WRONLY | O_NONBLOCK);
    if (-1 == fd)
      return;

    fprintf(stderr, "::open(\"%s\") succeeded!\n", fifoName);
    
    ::fcntl(fd, F_SETFL, ~O_NONBLOCK & ::fcntl(fd, F_GETFL, 0));
  }
  
  int written  = 0;

  while (written < length)
  {
    void (* const sigPipeHandler)(int) = ::signal(SIGPIPE, SIG_IGN);

    int r = ::write(fd, ((char *)data) + written, length - written);
    int lastErrno = errno;

    ::signal(SIGPIPE, sigPipeHandler);

    if (r < 0)
    {
      if (EAGAIN == lastErrno)
        break;
      
      fprintf(stderr, "::write(\"%s\", %d) failed %d: ", fifoName, length, lastErrno);
      errno = lastErrno;
      perror("");

      ::close(fd);
      fd = -1;

      return;
    }

    written += r;
  }
}



#include <vdr/plugin.h>

static const char *VERSION        = "0.0.1";
static const char *DESCRIPTION    = "Enter description for 'gstreamer' plugin";
//static const char *MAINMENUENTRY  = "Gstreamer";

class cPluginGstreamer : public cPlugin {
private:
  // Add any member variables or functions you may need here.
public:
  cPluginGstreamer(void);
  virtual ~cPluginGstreamer();
  virtual const char *Version(void) { return VERSION; }
  virtual const char *Description(void) { return DESCRIPTION; }
  virtual const char *CommandLineHelp(void);
  virtual bool ProcessArgs(int argc, char *argv[]);
  virtual bool Initialize(void);
  virtual bool Start(void);
  virtual void Housekeeping(void);
  virtual const char *MainMenuEntry(void) { return 0; /*MAINMENUENTRY;*/ }
  virtual cOsdObject *MainMenuAction(void);
  virtual cMenuSetupPage *SetupMenu(void);
  virtual bool SetupParse(const char *Name, const char *Value);
  };

cPluginGstreamer::cPluginGstreamer(void)
{
  // Initialize any member variables here.
  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
}

cPluginGstreamer::~cPluginGstreamer()
{
  // Clean up after yourself!
}

const char *cPluginGstreamer::CommandLineHelp(void)
{
  // Return a string that describes all known command line options.
  return NULL;
}

bool cPluginGstreamer::ProcessArgs(int argc, char *argv[])
{
  // Implement command line argument processing here if applicable.
  return true;
}

bool cPluginGstreamer::Initialize(void)
{
  // Initialize any background activities the plugin shall perform.
  if (!cGstreamerDevice::Create())
    return false;
  
  return true;
}

bool cPluginGstreamer::Start(void)
{
  // Start any background activities the plugin shall perform.
  return true;
}

void cPluginGstreamer::Housekeeping(void)
{
  // Perform any cleanup or other regular tasks.
}

cOsdObject *cPluginGstreamer::MainMenuAction(void)
{
  // Perform the action when selected from the main VDR menu.
  return NULL;
}

cMenuSetupPage *cPluginGstreamer::SetupMenu(void)
{
  // Return a setup menu in case the plugin supports one.
  return NULL;
}

bool cPluginGstreamer::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  return false;
}

VDRPLUGINCREATOR(cPluginGstreamer); // Don't touch this!
