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

#include "cal-mpeg.h"
#include "cal-gapmarker.h"
#include <vdr/recording.h>
#include <vdr/skins.h>
#include <vdr/thread.h>
//#include <vdr/videodir.h>

#define PTS_THRESHOLD 5
#define YET_NO_PTS NO_PTS - 1

// --- cCalGapMarkingThread --------------------------------------------------

class cCalGapMarkingThread : public cThread {
private:
  const char *error;
  cUnbufferedFile *file;
  cFileName *fileName;
  cIndexFile *index;
  cMarks marks;
  int ptsThreshold;
protected:
  virtual void Action(void);
public:
  cCalGapMarkingThread(const char *FileName, int PtsThreshold);
  virtual ~cCalGapMarkingThread();
  //const char *Error(void) { return error; }
  };

cCalGapMarkingThread::cCalGapMarkingThread(const char *FileName, int PtsThreshold)
:cThread("Cut-a-Lot: Gap finder")
{
  file = NULL;
  fileName = new cFileName(FileName, false, true);
  index = new cIndexFile(FileName, false);
  marks.Load(FileName);
  
  // add the marks for the first and the last frame (the might be already there
  // but who cares ;o)
  marks.Add(0);
  marks.Add(index->GetNextIFrame(index->Last(), false));
  marks.Save();

  ptsThreshold = PtsThreshold;
  Start();
}

cCalGapMarkingThread::~cCalGapMarkingThread()
{
  Cancel(3);
  delete fileName;
  delete index;
}

void cCalGapMarkingThread::Action(void)
{
  file = fileName->Open();
  if (!file)
    return;
  //file->SetReadAhead(MEGABYTE(20));
  //toMarks.Add(0);
  //toMarks.Save();
  uchar buffer[MAXFRAMESIZE];
  int Index = 0;
  int lastIndex = 0;
  pts_t lastPts, ptsDiff;
  lastPts = YET_NO_PTS;
  while (Running()) {
    uchar FileNumber;
    int FileOffset, Length;

    // start directly with 2nd I-frame
    lastIndex = Index;
    Index = index->GetNextIFrame(Index, true, &FileNumber, &FileOffset, &Length);

    if(Index > -1) {
      file = fileName->SetOffset(FileNumber, FileOffset);
      int len = ReadFrame(file, buffer,  Length, sizeof(buffer));
    
      cCalPES *pes = new cCalPES(buffer, len);
      if(pes) {
        ptsDiff = pes->GetPTS() - lastPts;
        // what about jumping from max to 0?!?
        if(lastPts != YET_NO_PTS) {
	  if(abs(((Index-lastIndex) * 90000 / FRAMESPERSEC) - (int)ptsDiff) > ptsThreshold) {
            //fprintf(stderr, "Index: %d, lastIndex: %d, Diff: %d, PTS-Diff: %lld\n", Index,lastIndex, ((Index-lastIndex) * 90000 / FRAMESPERSEC), ptsDiff);
	    marks.Add(lastIndex);
	    marks.Add(Index);
	    marks.Save();
	  }
	}
        lastPts = pes->GetPTS();
      } else {
        fprintf(stderr, "pes is NULL!!!\n");
        break;
      }
      delete pes;
    } else {
      // an Index of -1 means we have reached last I-frame, so terminate
      break;
    }
  }
}

// --- cCalGapMarkerMenu -----------------------------------------------------

cCalGapMarkerMenu::cCalGapMarkerMenu(const char *FileName)
:cOsdMenu(tr("Find gaps"), 25)
{
  ptsThreshold = 3605;
  fileName = strdup(FileName);
  Add(new cMenuEditIntItem(tr("PTS-threshold"), &ptsThreshold, 0, 30000));
  SetHelp(tr("Button$Start"));
}

eOSState cCalGapMarkerMenu::ProcessKey(eKeys Key) {
  eOSState state = cOsdMenu::ProcessKey(Key);
  if(state == osUnknown) {
    if(Key == kRed) {
      if(cCalGapMarker::Start(fileName, ptsThreshold)) {
        char *message = NULL;
	asprintf(&message, "%s: %s", "Cut-a-Lot", tr("Gap finder process started"));
        Skins.QueueMessage(mtInfo, message, Setup.OSDMessageTime, 0);
	free(message);
	return osEnd;
      } else {
        char *message = NULL;
	asprintf(&message, "%s: %s", "Cut-a-Lot", tr("Gap finder process already active!"));
        Skins.Message(mtError, message);
	free(message);
      }
    }
    return osContinue;
  }
  return state;
}

// --- cCalGapMarker ---------------------------------------------------------

cCalGapMarkingThread *cCalGapMarker::gapMarkingThread = NULL;
bool cCalGapMarker::ended = false;
bool cCalGapMarker::Start(const char *FileName, int ptsThreshold) {
  if (!gapMarkingThread) {
    ended = false;
    gapMarkingThread = new cCalGapMarkingThread(FileName, ptsThreshold);
    return true;
  }
  return false;
}

void cCalGapMarker::Stop(void) {
  bool Interrupted = gapMarkingThread && gapMarkingThread->Active();
  delete gapMarkingThread;
  gapMarkingThread = NULL;
  if (Interrupted)
    isyslog("gap finder process has been interrupted");
}

bool cCalGapMarker::Active(void) {
  if (gapMarkingThread) {
    if (gapMarkingThread->Active())
      return true;
    Stop();
    ended = true;
  }
  return false;
}

bool cCalGapMarker::Ended(void) {
  bool result = ended;
  ended = false;
  return result;
}
