/*
 * transfer.c: Transfer mode
 *
 * See the main source file 'vdr.c' for copyright information and
 * how to reach the author.
 *
 * $Id: transfer.c 1.33 2006/01/29 17:24:39 kls Exp $
 */

#include "transfer.h"
#include "videodir.h"
#include "menu.h"

#define TRANSFERBUFSIZE  MEGABYTE(2)
#define POLLTIMEOUTS_BEFORE_DEVICECLEAR 6

// --- cTransfer -------------------------------------------------------------

cTransfer::cTransfer(int VPid, const int *APids, const int *DPids, const int *SPids)
:cReceiver(0, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
,cThread("transfer")
{
  ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
  remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids);
}

cTransfer::~cTransfer()
{
  cReceiver::Detach();
  cPlayer::Detach();
  delete remux;
  delete ringBuffer;
}

void cTransfer::Activate(bool On)
{
  if (On)
     Start();
  else
     Cancel(3);
}

void cTransfer::Receive(uchar *Data, int Length)
{
  if (IsAttached() && Running()) {
     int p = ringBuffer->Put(Data, Length);
     if (p != Length && Running())
        ringBuffer->ReportOverflow(Length - p);
     return;
     }
}

void cTransfer::Action(void)
{
  int PollTimeouts = 0;
  uchar *p = NULL;
  int Result = 0;
  while (Running()) {
        int Count;
        uchar *b = ringBuffer->Get(Count);
        if (b) {
           if (ringBuffer->Available() > TRANSFERBUFSIZE * 9 / 10) {
              // If the buffer runs full, we have no chance of ever catching up
              // since the data comes in at the same rate as it goes out (it's "live").
              // So let's clear the buffer instead of suffering from permanent
              // overflows.
              dsyslog("clearing transfer buffer to avoid overflows");
              DeviceClear();
              ringBuffer->Clear();
              remux->Clear();
              PlayPes(NULL, 0);
              p = NULL;
              continue;
              }
           Count = remux->Put(b, Count);
           if (Count)
              ringBuffer->Del(Count);
           }
        if (!p)
           p = remux->Get(Result);
        if (p) {
           cPoller Poller;
           if (DevicePoll(Poller, 100)) {
              PollTimeouts = 0;
              int w = PlayPes(p, Result);
              if (w > 0) {
                 p += w;
                 Result -= w;
                 remux->Del(w);
                 if (Result <= 0)
                    p = NULL;
                 }
              else if (w < 0 && FATALERRNO)
                 LOG_ERROR;
              }
           else {
              PollTimeouts++;
              if (PollTimeouts == POLLTIMEOUTS_BEFORE_DEVICECLEAR) {
                 dsyslog("clearing device because of consecutive poll timeouts");
                 DeviceClear();
                 ringBuffer->Clear();
                 remux->Clear();
                 PlayPes(NULL, 0);
                 p = NULL;
                 }
              }
           }
        }
}

// --- cLiveRecorder ---------------------------------------------------------

cLiveRecorder *cLiveRecorderControl::liveRecorder[MAXLIVERECORDERS] = {NULL};
cLiveRecorder *cLiveRecorder::ActiveLiveRecorder = NULL;
cLiveRecorderControl *cLiveRecorderControl::LiveThread = NULL;

cLiveRecorder::cLiveRecorder(cDevice *ReceiverDevice, const cChannel *Channel)
{
  start = time(NULL);
  New = true;
  receiverDevice = ReceiverDevice;
  channelID = tChannelID::tChannelID(Channel->Source(),Channel->Nid(),Channel->Tid(),Channel->Sid(),Channel->Rid());
  Pids.VPid=Channel->Vpid();
  for (int i=0; i<=MAXAPIDS; i++) 
     Pids.APids[i] = Channel->Apid(i);
  for (int i=0; i<=MAXDPIDS; i++)
     Pids.DPids[i] = Channel->Dpid(i);
  for (int i=0; i<=MAXSPIDS; i++)
     Pids.SPids[i] = Channel->Spid(i);
  Rew=false;
  sprintf(FileName,"%s/LiveBuffer/%s",BufferDirectory,Channel->Name());
  while (DirectoryOk(FileName,false))
    sprintf(FileName,"%s_",FileName);
  MakeDirs(FileName,true);
  Recorder=new cRecorder(FileName, 0, -1,Channel->Vpid(), Channel->Apids(),Channel->Dpids(),Channel->Spids(),true);
  while (!cLiveRecorderControl::LiveThread->wantadd && !ReceiverDevice->HasLock())
    usleep(100);
  if (!cLiveRecorderControl::LiveThread->wantadd)
    ReceiverDevice->AttachReceiver(Recorder);
  while (cControl::Control() && !cControl::Control()->Attached() && !cLiveRecorderControl::LiveThread->wantadd)
    usleep(100);
  Text(text);
  cReplayControl::SetRecording(FileName,text, true);
  replay=NULL;
  if (cDevice::PrimaryDevice()->Replaying() && !cControl::Control())
    return;
  if (!cLiveRecorderControl::LiveThread->wantadd)
    cControl::Launch(replay = new cReplayControl(true,this));
}

cLiveRecorder::~cLiveRecorder()
{
  delete Recorder;
  RemoveFileOrDir(FileName,true);
}

bool cLiveRecorder::PIDsChanged(const cChannel *Channel)
{
  bool changed=false;
  if (Pids.VPid != Channel->Vpid())
    changed=true;
  Pids.VPid=Channel->Vpid();
  for (int i=0; i<=MAXAPIDS; i++)
  {
    if (Pids.APids[i] != Channel->Apid(i))
      changed=true;
    Pids.APids[i]=Channel->Apid(i);
  }
  for (int i=0; i<=MAXDPIDS; i++)
  {
    if (Pids.DPids[i] != Channel->Dpid(i))
      changed=true;
    Pids.DPids[i]=Channel->Dpid(i);
  }
  for (int i=0; i<=MAXSPIDS; i++)
  {
    if (Pids.SPids[i] != Channel->Spid(i))
      changed=true;
    Pids.SPids[i]=Channel->Spid(i);
  }
  return changed;
}

void cLiveRecorder::Text(char *Name)
{
  strcpy(Name,"LiveBuffer");
  bool any=false;
  for (int i=0; i<MAXLIVERECORDERS; i++)
    if (cLiveRecorderControl::liveRecorder[i] && cLiveRecorderControl::liveRecorder[i]!=this)
    {
      sprintf(Name,"%s%s%d",Name,any ? ", " : " (", Channels.GetByChannelID(cLiveRecorderControl::liveRecorder[i]->channelID)->Number());
      any=true;
    }
  if (any)
    sprintf(Name,"%s)",Name);
}

void cLiveRecorder::rew(bool On)
{
  if (!Setup.KeepBuffer)
    return;
  if (!On)
    Recorder->priority = -1; 
  Rew=On;
  replay->Rew(On);
}

bool cLiveRecorder::IsAlreadyRecording(tChannelID ChannelID)
{
  return channelID==ChannelID;
}

void cLiveRecorder::Resume()
{
  if (Recorder->IsAttached())
  {
    while (!cLiveRecorderControl::LiveThread->wantadd && !receiverDevice->HasLock())
      usleep(100);
    if (!cLiveRecorderControl::LiveThread->wantadd)
      receiverDevice->AttachReceiver(Recorder);
  }
  Text(text);
  cReplayControl::SetRecording(FileName,text,true);
  cControl::Launch(replay = new cReplayControl(true,this));
  if (Recorder->priority != -2)
  {
    Recorder->priority = -1;
    New = true;
  }
  if (Rew)
    replay->Rew(true);
}

// --- cLiveRecorderControl --------------------------------------------------

cLiveRecorderControl::cLiveRecorderControl()
{
  wantadd=false;
  add=false;
  Start();
}

void cLiveRecorderControl::ClearBufferDirectory()
{
  char FileName[255];
  sprintf(FileName,"%s/LiveBuffer",BufferDirectory);
  cReadDir d(FileName);
  struct dirent *e;
  while ((e=d.Next()) != NULL) {
     if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
        char *buffer;
        asprintf(&buffer, "%s/%s", FileName, e->d_name);
        RemoveFileOrDir(buffer,true);
        }
     }
}

void cLiveRecorderControl::Action()
{
  ClearBufferDirectory();
  while (Running())
  {
   if (!add)
    {
      wantadd=false;
      usleep(100);
      continue;
    }
    cLiveRecorder *OldRecorder=NULL,*OldActive=cLiveRecorder::ActiveLiveRecorder;
    cLiveRecorder::ActiveLiveRecorder=NULL;
    for (int i=0; i<MAXLIVERECORDERS; i++)
    {
      if (liveRecorder[i])
      {
        if (!liveRecorder[i]->Recorder->IsAttached() || (!liveRecorder[i]->IsAlreadyRecording(tChannelID::tChannelID(Channel->Source(),Channel->Nid(),Channel->Tid(),Channel->Sid(),Channel->Rid())) && liveRecorder[i]->Recorder->priority==-1))
        {
          if (!OldRecorder && !liveRecorder[i]->IsAlreadyRecording(tChannelID::tChannelID(Channel->Source(),Channel->Nid(),Channel->Tid(),Channel->Sid(),Channel->Rid())))
            OldRecorder=liveRecorder[i];
          else
            delete liveRecorder[i];
          liveRecorder[i]=NULL;
        }
      }
      if (liveRecorder[i] && liveRecorder[i]->IsAlreadyRecording(tChannelID::tChannelID(Channel->Source(),Channel->Nid(),Channel->Tid(),Channel->Sid(),Channel->Rid())))
        cLiveRecorder::ActiveLiveRecorder = liveRecorder[i];
    }
    if (cLiveRecorder::ActiveLiveRecorder)
    {
      if (cLiveRecorder::ActiveLiveRecorder->PIDsChanged(Channel))
        cLiveRecorder::ActiveLiveRecorder->Recorder->ChangePIDs(ReceiverDevice,Channel->Vpid(), Channel->Apids(),Channel->Dpids(),Channel->Spids());
      if (cLiveRecorder::ActiveLiveRecorder != OldActive || !OldActive->replay || !cDevice::PrimaryDevice()->Replaying())
        cLiveRecorder::ActiveLiveRecorder->Resume();
    }
    else
    {
      int i;
      for (i=0; i<MAXLIVERECORDERS; i++)
        if (!liveRecorder[i])
        {
          cLiveRecorder::ActiveLiveRecorder = liveRecorder[i] = new cLiveRecorder(ReceiverDevice, Channel);
          break;
        }
      if (i==MAXLIVERECORDERS)
      {
        delete liveRecorder[0];
        cLiveRecorder::ActiveLiveRecorder = liveRecorder[0] = new cLiveRecorder(ReceiverDevice, Channel);
      }
    }
    if (OldRecorder)
      delete OldRecorder;
    cTransferControl::receiverDevice = ReceiverDevice;
    add=false;
  }
  for (int i=0; i<MAXLIVERECORDERS; i++)
    if (liveRecorder[i])
    {
      delete liveRecorder[i];
      liveRecorder[i]=NULL;
    }
  cTransferControl::receiverDevice = NULL;
  cLiveRecorder::ActiveLiveRecorder=NULL;
  ClearBufferDirectory();
}

void cLiveRecorderControl::Add(cDevice *ReceiverDevice, const cChannel *Channel)
{
  if (!LiveThread)
    LiveThread = new cLiveRecorderControl;
  LiveThread->wantadd=true;
  while (LiveThread->wantadd)
    usleep(10);
  LiveThread->ReceiverDevice=ReceiverDevice;
  LiveThread->Channel=Channel;
  LiveThread->add=true;
}

void cLiveRecorderControl::Shutdown()
{
  if (LiveThread)
    LiveThread->Cancel(3);
  LiveThread=NULL;
}

cLiveRecorder* cLiveRecorderControl::GetLiveRecorder(const cChannel *Channel)
{
  if (Channel)
  {
    for (int i=0; i<MAXLIVERECORDERS; i++)
      if (liveRecorder[i] && liveRecorder[i]->IsAlreadyRecording(tChannelID::tChannelID(Channel->Source(),Channel->Nid(),Channel->Tid(),Channel->Sid(),Channel->Rid())))
      {
        return liveRecorder[i];
        break;
      }
    return NULL;
  }
  else
    return cLiveRecorder::ActiveLiveRecorder;
}

void cLiveRecorderControl::UpdateTimes()
{
  if (cControl::Control() && cControl::Control()->IsLiveRecording() && !LiveThread->add)
  {
    bool play,forward;
    int speed;
    cControl::Control()->GetReplayMode(play,forward,speed);
    if (!play && cLiveRecorder::ActiveLiveRecorder->Rew)
      cLiveRecorder::ActiveLiveRecorder->Recorder->priority=-2;
  }
  int l=-1;
  if (Setup.KeepLastBuffer)
    for (int i=0; i<MAXLIVERECORDERS; i++)
      if (liveRecorder[i] && liveRecorder[i]->Recorder->priority==-1 && liveRecorder[i]->New && time(NULL)-liveRecorder[i]->start > Setup.BufferTimeOut)
      {
        liveRecorder[i]->Recorder->priority = -3;
        liveRecorder[i]->New = false;
        l=i;
        break;
      }
  if (l>=0)
    for (int i=0; i<MAXLIVERECORDERS; i++)
      if (i!=l && liveRecorder[i])
        liveRecorder[i]->Recorder->priority=liveRecorder[i]->Recorder->priority < -2 ? liveRecorder[i]->Recorder->priority > -Setup.NumLastBuffers - 2 ? liveRecorder[i]->Recorder->priority - 1 : -1 : liveRecorder[i]->Recorder->priority;
} 

bool cLiveRecorderControl::InLiveBuffer(cTimer *timer)
{
  if (timer && cLiveRecorderControl::GetLiveRecorder(timer->Channel()) && !timer->Recording())
  {
    cIndexFile *index = new cIndexFile(cLiveRecorderControl::GetLiveRecorder(timer->Channel())->GetFileName(),false);
    int now=index->GetLast() - 1;
    int beginning=index->GetFirstFrame();
    delete index;
    if (timer->StartTime() < time(NULL) && now-(time(NULL)-timer->StopTime())*FRAMESPERSEC > beginning)
      return true;
  }
  return false;
}

int cLiveRecorderControl::MatchesEvent(const cEvent *event)
{
  cLiveRecorder *liverecorder=cLiveRecorderControl::GetLiveRecorder(Channels.GetByChannelID(event->ChannelID()));
  if (!liverecorder)
    return 0;
  cIndexFile *index = new cIndexFile(liverecorder->GetFileName(),false);
  int endtime=time(NULL);
  int starttime=time(NULL)-(index->GetLast()-index->GetFirstFrame())/FRAMESPERSEC;
  int m=0;
  if (starttime<=event->StartTime() && endtime>event->StartTime() || starttime<=event->EndTime() && endtime>event->StartTime())
     m=1;
  if (starttime<=event->StartTime() && endtime>=event->StartTime())
     m=2;
  delete index;
  return m;
}

// --- cTransferControl ------------------------------------------------------

cDevice *cTransferControl::receiverDevice = NULL;

cTransferControl::cTransferControl(cDevice *ReceiverDevice, int VPid, const int *APids, const int *DPids, const int *SPids)
:cControl(transfer = new cTransfer(VPid, APids, DPids, SPids), true)
{
  ReceiverDevice->AttachReceiver(transfer);
  receiverDevice = ReceiverDevice;
}

cTransferControl::~cTransferControl()
{
  receiverDevice = NULL;
  delete transfer;
}
