/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2019 Live Networks, Inc.  All rights reserved.
// Abstract class for parsing a byte stream
// C++ header

#ifndef _STREAM_PARSER_HH
#define _STREAM_PARSER_HH

#ifndef _FRAMED_SOURCE_HH
#include "FramedSource.hh"
#endif

class StreamParser {
public:
  virtual void flushInput();

protected: // we're a virtual base class
  typedef void (clientContinueFunc)(void* clientData,
				    unsigned char* ptr, unsigned size,
				    struct timeval presentationTime);
  StreamParser(FramedSource* inputSource,
	       FramedSource::onCloseFunc* onInputCloseFunc,
	       void* onInputCloseClientData,
	       clientContinueFunc* clientContinueFunc,
	       void* clientContinueClientData);
  virtual ~StreamParser();

  void saveParserState();
  virtual void restoreSavedParserState();

  u_int32_t get4Bytes() { // byte-aligned; returned in big-endian order
    u_int32_t result = test4Bytes();
    fCurParserIndex += 4;
    fRemainingUnparsedBits = 0;

    return result;
  }
  u_int32_t test4Bytes() { // as above, but doesn't advance ptr
    ensureValidBytes(4);

    unsigned char const* ptr = nextToParse();
    return (ptr[0]<<24)|(ptr[1]<<16)|(ptr[2]<<8)|ptr[3];
  }

  u_int16_t get2Bytes() {
    ensureValidBytes(2);

    unsigned char const* ptr = nextToParse();
    u_int16_t result = (ptr[0]<<8)|ptr[1];

    fCurParserIndex += 2;
    fRemainingUnparsedBits = 0;

    return result;
  }
  u_int16_t test2Bytes() {
    ensureValidBytes(2);

    unsigned char const* ptr = nextToParse();
    return (ptr[0]<<8)|ptr[1];
  }


  u_int8_t get1Byte() { // byte-aligned
    ensureValidBytes(1);
    fRemainingUnparsedBits = 0;
    return curBank()[fCurParserIndex++];
  }
  u_int8_t test1Byte() { // as above, but doesn't advance ptr
    ensureValidBytes(1);
    return nextToParse()[0];
  }

  void getBytes(u_int8_t* to, unsigned numBytes) {
    testBytes(to, numBytes);
    fCurParserIndex += numBytes;
    fRemainingUnparsedBits = 0;
  }
  void testBytes(u_int8_t* to, unsigned numBytes) { // as above, but doesn't advance ptr
    ensureValidBytes(numBytes);
    memmove(to, nextToParse(), numBytes);
  }
  void skipBytes(unsigned numBytes) {
    ensureValidBytes(numBytes);
    fCurParserIndex += numBytes;
  }

  void skipBits(unsigned numBits);
  unsigned getBits(unsigned numBits);
      // numBits <= 32; returns data into low-order bits of result

  unsigned curOffset() const { return fCurParserIndex; }

  unsigned& totNumValidBytes() { return fTotNumValidBytes; }

  Boolean haveSeenEOF() const { return fHaveSeenEOF; }

  unsigned bankSize() const;

private:
  unsigned char* curBank() { return fCurBank; }
  unsigned char* nextToParse() { return &curBank()[fCurParserIndex]; }
  unsigned char* lastParsed() { return &curBank()[fCurParserIndex-1]; }

  // makes sure that at least "numBytes" valid bytes remain:
  void ensureValidBytes(unsigned numBytesNeeded) {
    // common case: inlined:
    if (fCurParserIndex + numBytesNeeded <= fTotNumValidBytes) return;

    ensureValidBytes1(numBytesNeeded);
  }
  void ensureValidBytes1(unsigned numBytesNeeded);

  static void afterGettingBytes(void* clientData, unsigned numBytesRead,
				unsigned numTruncatedBytes,
				struct timeval presentationTime,
				unsigned durationInMicroseconds);
  void afterGettingBytes1(unsigned numBytesRead, struct timeval presentationTime);

  static void onInputClosure(void* clientData);
  void onInputClosure1();

private:
  FramedSource* fInputSource; // should be a byte-stream source??
  FramedSource::onCloseFunc* fClientOnInputCloseFunc;
  void* fClientOnInputCloseClientData;
  clientContinueFunc* fClientContinueFunc;
  void* fClientContinueClientData;

  // Use a pair of 'banks', and swap between them as they fill up:
  unsigned char* fBank[2];
  unsigned char fCurBankNum;
  unsigned char* fCurBank;

  // The most recent 'saved' parse position:
  unsigned fSavedParserIndex; // <= fCurParserIndex
  unsigned char fSavedRemainingUnparsedBits;

  // The current position of the parser within the current bank:
  unsigned fCurParserIndex; // <= fTotNumValidBytes
  unsigned char fRemainingUnparsedBits; // in previous byte: [0,7]

  // The total number of valid bytes stored in the current bank:
  unsigned fTotNumValidBytes; // <= BANK_SIZE

  // Whether we have seen EOF on the input source:
  Boolean fHaveSeenEOF;

  struct timeval fLastSeenPresentationTime; // hack used for EOF handling
};

#endif
