// A filter that passes through (unchanged) chunks that contain an integral number
// of MPEG-2 Transport Stream packets, but returning (in "fDurationInMicroseconds")
// an updated estimate of the time gap between chunks.
// Implementation

#include <assert.h>
#include "MPEG2TransportStreamFramerSimple.hh"

////////// MPEG2TransportStreamFramerSimple //////////

MPEG2TransportStreamFramerSimple* MPEG2TransportStreamFramerSimple
::createNew(UsageEnvironment& env, FramedSource* inputSource, unsigned maxFrameSize) {
  return new MPEG2TransportStreamFramerSimple(env, inputSource, maxFrameSize);
}

MPEG2TransportStreamFramerSimple
::MPEG2TransportStreamFramerSimple(UsageEnvironment& env, FramedSource* inputSource, 
                                   unsigned maxFrameSize)
  : FramedFilter(env, inputSource), fNumGetNextFrame(0),
    fNumGetNextFrameErrPrintOnce(False), fIncompleteTSPacketSize(0) {
  // be sure we ask/read entire TS packet
  fMaxFrameSize = maxFrameSize - (maxFrameSize % TRANSPORT_PACKET_SIZE);
}

MPEG2TransportStreamFramerSimple::~MPEG2TransportStreamFramerSimple() {
}

// Read directly from our input source into our client's buffer:
void MPEG2TransportStreamFramerSimple::doGetNextFrame() {
  // be sure we request to read entire TS packet(s)
  if(fMaxSize < fMaxFrameSize) {
    fMaxSize-= fMaxSize % TRANSPORT_PACKET_SIZE;
    if (fMaxSize == 0) {
      envir() << "Insufficient buffer size, closing!!!\n";
      handleClosure(this);
      return;
    }
  }

  // if previuóusli readed incomplete TS packet, show them
  if(fIncompleteTSPacketSize) {
    memcpy(fTo, fIncompleteTSPacketBuffer, fIncompleteTSPacketSize);
    fFrameSize = fIncompleteTSPacketSize;
    fIncompleteTSPacketSize = 0;
  } else
    fFrameSize = 0;

  fInputSource->getNextFrame(fTo + fIncompleteTSPacketSize, fMaxSize,
			     afterGettingFrame, this,
			     FramedSource::handleClosure, this);
}

void MPEG2TransportStreamFramerSimple::doStopGettingFrames() {
  FramedFilter::doStopGettingFrames();

  fNumGetNextFrame = 0;
  fNumGetNextFrameErrPrintOnce = False;
  fIncompleteTSPacketSize = 0;
}

void MPEG2TransportStreamFramerSimple
::afterGettingFrame(void* clientData, unsigned frameSize,
		    unsigned /*numTruncatedBytes*/,
		    struct timeval presentationTime,
		    unsigned /*durationInMicroseconds*/) {
  MPEG2TransportStreamFramerSimple* framer = (MPEG2TransportStreamFramerSimple*)clientData;
  framer->afterGettingFrame1(frameSize, presentationTime);
}

#define TRANSPORT_SYNC_BYTE 0x47

void MPEG2TransportStreamFramerSimple::afterGettingFrame1(unsigned frameSize,
						    struct timeval presentationTime) {
  // We didn'n read anything; There is probably stupid source.
  assert(frameSize > 0);       // this newr happend ! :]
  fFrameSize += frameSize;

  /* Check if Transport Stream is continuous. 
   * If not, remove all garbage */
  unsigned syncBytePosition = 0;
  unsigned syncBytePositionPrev = 0; // point behind the end of last valid TS packet
  while ( syncBytePosition < fFrameSize ) {
    // search a TS_SYNC byte
    if (fTo[syncBytePosition] != TRANSPORT_SYNC_BYTE) {
      syncBytePosition++;
      continue;
    }

    // if garbage present, replace them by new (potentiali) valid TS packet(s)
    if (syncBytePosition != syncBytePositionPrev) {
      fprintf(stderr, "   ---   " __FILE__ "A memove( -, -, size :%i )\n", syncBytePosition - syncBytePositionPrev);
      memmove(&fTo[syncBytePositionPrev], &fTo[syncBytePosition], fFrameSize - syncBytePosition);
      fFrameSize -= syncBytePosition - syncBytePositionPrev;
      syncBytePosition = syncBytePositionPrev;
      // check for syncBytePosition < fFrameSize
      // continue;
    }

    // if there are no sufficient amount of data to check next TS packet
    if ((syncBytePosition + TRANSPORT_PACKET_SIZE) >= fFrameSize)
      break;

    // TS packet is valid only if is folowed by next TS packet (header)
    if (fTo[syncBytePosition + TRANSPORT_PACKET_SIZE] == TRANSPORT_SYNC_BYTE) {
      syncBytePositionPrev += TRANSPORT_PACKET_SIZE;
      syncBytePosition += TRANSPORT_PACKET_SIZE;
      continue;
    }
    
    // if after current TS packet does not follow next (at least TS header)
    // discard current packet too, it might be incomplete
    syncBytePosition++;
  }

  // be sure we drop all garbage
  if (syncBytePosition != syncBytePositionPrev) {
    fprintf(stderr, "   ---   " __FILE__ "B memove( -, -, size :%i )\n", syncBytePosition - syncBytePositionPrev);
    memmove(&fTo[syncBytePositionPrev], &fTo[syncBytePosition], fFrameSize - syncBytePosition);
    fFrameSize -= syncBytePosition - syncBytePositionPrev;
  }

  // set presentationTime synced with first incomming byte
  if(!fNumGetNextFrame)
    fPresentationTime = presentationTime;

  fprintf(stderr, "   --- " __FILE__ " fFrameSize %i   frameSize %i\n",  fFrameSize, frameSize);
  // number of recursive calls getNextFrame cannot exceed limit
  // (to prevent infinite loops, if there is only garbage present)
  if (fNumGetNextFrame >= 4) {
    if(fNumGetNextFrameErrPrintOnce == False) {
      fNumGetNextFrameErrPrintOnce = True; 
      envir() << "Incoming data are not probably MPEG2 transport stream!\n";
    }
    return;
  }
  
  // fill buffer as much as possible, if nothigh to read nevermind
  if (fFrameSize < fMaxFrameSize) {
    fNumGetNextFrame++;
    fInputSource->getNextFrame(&fTo[fFrameSize], fMaxFrameSize - fFrameSize,
			       afterGettingFrame, this,
			       FramedSource::handleClosure, this);
    // there might happend that this framer is close -> this variable is reset
    if(fNumGetNextFrame)
      fNumGetNextFrame--;
    return;
  }

  // no TS packets, or end of stream, or we cannot check if this packet 
  // is valid because is alone
  if (fFrameSize <= TRANSPORT_PACKET_SIZE) {
    fIncompleteTSPacketSize = TRANSPORT_PACKET_SIZE;
    memcpy(fIncompleteTSPacketBuffer, fTo, TRANSPORT_PACKET_SIZE);
    fprintf(stderr, "line: %i\n; fFrameSize: %i\n", __LINE__, fFrameSize);
    // We didn't read a complete TS packet; Nothigh to delivery
    return;
  }
  
  // align to be present only whole TS packet(s)
  // store and wait to complete it latter (if posibile)
  fIncompleteTSPacketSize = fFrameSize % TRANSPORT_PACKET_SIZE;
  if(fIncompleteTSPacketSize) {
    fFrameSize-= fIncompleteTSPacketSize;
    memcpy(fIncompleteTSPacketBuffer, &fTo[fFrameSize], fIncompleteTSPacketSize);
  }

  // Reset this variable to reprint this error if garbage is comming again
  fNumGetNextFrameErrPrintOnce = False;
  // Complete the delivery to our client
  afterGetting(this);
}

