/**********
**********/
// Filters for converting between raw PCM audio and uLaw
// Implementation

#include "uLawAudioFilter.hh"

uLawFromPCMAudioSource* uLawFromPCMAudioSource
::createNew(UsageEnvironment& env, FramedSource* inputSource, int byteOrdering) {
  if (byteOrdering < 0 || byteOrdering > 2) {
    env.setResultMsg("uLawFromPCMAudioSource::createNew(): bad \"byteOrdering\" parameter");
    return NULL;
  }
  return new uLawFromPCMAudioSource(env, inputSource, byteOrdering);
}

uLawFromPCMAudioSource
::uLawFromPCMAudioSource(UsageEnvironment& env, FramedSource* inputSource,
			 int byteOrdering)
  : FramedFilter(env, inputSource),
    fByteOrdering(byteOrdering), fInputBuffer(NULL), fInputBufferSize(0) {
}

uLawFromPCMAudioSource::~uLawFromPCMAudioSource() {
  delete[] fInputBuffer;
}

void uLawFromPCMAudioSource::doGetNextFrame() {
  // Figure out how many bytes of input data to ask for, and increase
  // our input buffer if necessary:
  unsigned bytesToRead = fMaxSize*2; // because we're converting 16 bits->8
  if (bytesToRead > fInputBufferSize) {
    delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead];
    fInputBufferSize = bytesToRead;
  }

  // Arrange to read samples into the input buffer:
  fInputSource->getNextFrame(fInputBuffer, bytesToRead,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

#define BIAS 0x84   // the add-in bias for 16 bit samples
#define CLIP 32635

static unsigned char uLawFrom16BitLinear(u_int16_t sample) {
  static int const exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
				   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
  unsigned char sign = (sample >> 8) & 0x80;
  if (sign != 0) sample = -sample; // get the magnitude

  if (sample > CLIP) sample = CLIP; // clip the magnitude
  sample += BIAS;

  unsigned char exponent = exp_lut[(sample>>7) & 0xFF];
  unsigned char mantissa = (sample >> (exponent+3)) & 0x0F;
  unsigned char result = ~(sign | (exponent << 4) | mantissa);
  if (result == 0 ) result = 0x02;  // CCITT trap

  return result;
}

void uLawFromPCMAudioSource
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate raw 16-bit PCM samples (in the input buffer)
  // into uLaw samples (in the output buffer).
  unsigned numSamples = frameSize/2;
  switch (fByteOrdering) {
    case 0: { // host order
      u_int16_t* inputSample = (u_int16_t*)fInputBuffer;
      for (unsigned i = 0; i < numSamples; ++i) {
	fTo[i] = uLawFrom16BitLinear(inputSample[i]);
      }
      break;
    }
    case 1: { // little-endian order
      for (unsigned i = 0; i < numSamples; ++i) {
	u_int16_t const newValue = (fInputBuffer[2*i+1]<<8)|fInputBuffer[2*i];
	fTo[i] = uLawFrom16BitLinear(newValue);
      }
      break;
    }
    case 2: { // network (i.e., big-endian) order
      for (unsigned i = 0; i < numSamples; ++i) {
	u_int16_t const newValue = (fInputBuffer[2*i]<<8)|fInputBuffer[2*i+i];
	fTo[i] = uLawFrom16BitLinear(newValue);
      }
      break;
    }
  }

  // Complete delivery to the client:
  fFrameSize = numSamples;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// u-Law -> 16-bit PCM (in host order) //////////

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

PCMFromuLawAudioSource
::PCMFromuLawAudioSource(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource),
    fInputBuffer(NULL), fInputBufferSize(0) {
}

PCMFromuLawAudioSource::~PCMFromuLawAudioSource() {
  delete[] fInputBuffer;
}

void PCMFromuLawAudioSource::doGetNextFrame() {
  // Figure out how many bytes of input data to ask for, and increase
  // our input buffer if necessary:
  unsigned bytesToRead = fMaxSize/2; // because we're converting 8 bits->16
  if (bytesToRead > fInputBufferSize) {
    delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead];
    fInputBufferSize = bytesToRead;
  }

  // Arrange to read samples into the input buffer:
  fInputSource->getNextFrame(fInputBuffer, bytesToRead,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

static u_int16_t linear16FromuLaw(unsigned char uLawByte) {
  static int const exp_lut[8] = {0,132,396,924,1980,4092,8316,16764};
  uLawByte = ~uLawByte;

  Boolean sign = (uLawByte & 0x80) != 0;
  unsigned char exponent = (uLawByte>>4) & 0x07;
  unsigned char mantissa = uLawByte & 0x0F;

  u_int16_t result = exp_lut[exponent] + (mantissa << (exponent+3));
  if (sign) result = -result;
  return result;
}

void PCMFromuLawAudioSource
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate uLaw samples (in the input buffer)
  // into 16-bit PCM samples (in the output buffer), in host order.
  unsigned numSamples = frameSize;
  u_int16_t* outputSample = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numSamples; ++i) {
    outputSample[i] = linear16FromuLaw(fInputBuffer[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numSamples*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// 16-bit values (in host order) -> 16-bit network order //////////

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

NetworkFromHostOrder16
::NetworkFromHostOrder16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

NetworkFromHostOrder16::~NetworkFromHostOrder16() {
}

void NetworkFromHostOrder16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void NetworkFromHostOrder16
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate the 16-bit values that we have just read from host
  // to network order (in-place)
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    value[i] = htons(value[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = pr

  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// 16-bit values (in network order) -> 16-bit host order //////////

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

HostFromNetworkOrder16
::HostFromNetworkOrder16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

HostFromNetworkOrder16::~HostFromNetworkOrder16() {
}

void HostFromNetworkOrder16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void HostFromNetworkOrder16
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate the 16-bit values that we have just read from network
  // to host order (in-place):
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    value[i] = ntohs(value[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

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

EndianSwap16::EndianSwap16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

EndianSwap16::~EndianSwap16() {
}

void EndianSwap16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void EndianSwap16::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
				      struct timeval presentationTime,
				      unsigned durationInMicroseconds) {
  // Swap the byte order of the 16-bit values that we have just read (in place):
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    u_int16_t const orig = value[i];
    value[i] = ((orig&0xFF)<<8) | ((orig&0xFF00)>>8);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize);
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

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

EndianSwap24::EndianSwap24(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

EndianSwap24::~EndianSwap24() {
}

void EndianSwap24::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void EndianSwap24::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
				      struct timeval presentationTime,
				      unsigned durationInMicroseconds) {
  // Swap the byte order of the 24-bit values that we have just read (in place):
  unsigned const numValues = frameSize/3;
  u_int8_t* p = fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    u_int8_t tmp = p[0];
    p[0] = p[2];
    p[2] = tmp;
    p += 3;
  }

  // Complete delivery to the client:
  fFrameSize = numValues*3;
  fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize);
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}


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

/**********
**********/
// Filters for converting between raw PCM audio and uLaw
// Implementation

#include "uLawAudioFilter.hh"

uLawFromPCMAudioSource* uLawFromPCMAudioSource
::createNew(UsageEnvironment& env, FramedSource* inputSource, int byteOrdering) {
  if (byteOrdering < 0 || byteOrdering > 2) {
    env.setResultMsg("uLawFromPCMAudioSource::createNew(): bad \"byteOrdering\" parameter");
    return NULL;
  }
  return new uLawFromPCMAudioSource(env, inputSource, byteOrdering);
}

uLawFromPCMAudioSource
::uLawFromPCMAudioSource(UsageEnvironment& env, FramedSource* inputSource,
			 int byteOrdering)
  : FramedFilter(env, inputSource),
    fByteOrdering(byteOrdering), fInputBuffer(NULL), fInputBufferSize(0) {
}

uLawFromPCMAudioSource::~uLawFromPCMAudioSource() {
  delete[] fInputBuffer;
}

void uLawFromPCMAudioSource::doGetNextFrame() {
  // Figure out how many bytes of input data to ask for, and increase
  // our input buffer if necessary:
  unsigned bytesToRead = fMaxSize*2; // because we're converting 16 bits->8
  if (bytesToRead > fInputBufferSize) {
    delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead];
    fInputBufferSize = bytesToRead;
  }

  // Arrange to read samples into the input buffer:
  fInputSource->getNextFrame(fInputBuffer, bytesToRead,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

#define BIAS 0x84   // the add-in bias for 16 bit samples
#define CLIP 32635

static unsigned char uLawFrom16BitLinear(u_int16_t sample) {
  static int const exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
				   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
  unsigned char sign = (sample >> 8) & 0x80;
  if (sign != 0) sample = -sample; // get the magnitude

  if (sample > CLIP) sample = CLIP; // clip the magnitude
  sample += BIAS;

  unsigned char exponent = exp_lut[(sample>>7) & 0xFF];
  unsigned char mantissa = (sample >> (exponent+3)) & 0x0F;
  unsigned char result = ~(sign | (exponent << 4) | mantissa);
  if (result == 0 ) result = 0x02;  // CCITT trap

  return result;
}

void uLawFromPCMAudioSource
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate raw 16-bit PCM samples (in the input buffer)
  // into uLaw samples (in the output buffer).
  unsigned numSamples = frameSize/2;
  switch (fByteOrdering) {
    case 0: { // host order
      u_int16_t* inputSample = (u_int16_t*)fInputBuffer;
      for (unsigned i = 0; i < numSamples; ++i) {
	fTo[i] = uLawFrom16BitLinear(inputSample[i]);
      }
      break;
    }
    case 1: { // little-endian order
      for (unsigned i = 0; i < numSamples; ++i) {
	u_int16_t const newValue = (fInputBuffer[2*i+1]<<8)|fInputBuffer[2*i];
	fTo[i] = uLawFrom16BitLinear(newValue);
      }
      break;
    }
    case 2: { // network (i.e., big-endian) order
      for (unsigned i = 0; i < numSamples; ++i) {
	u_int16_t const newValue = (fInputBuffer[2*i]<<8)|fInputBuffer[2*i+i];
	fTo[i] = uLawFrom16BitLinear(newValue);
      }
      break;
    }
  }

  // Complete delivery to the client:
  fFrameSize = numSamples;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// u-Law -> 16-bit PCM (in host order) //////////

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

PCMFromuLawAudioSource
::PCMFromuLawAudioSource(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource),
    fInputBuffer(NULL), fInputBufferSize(0) {
}

PCMFromuLawAudioSource::~PCMFromuLawAudioSource() {
  delete[] fInputBuffer;
}

void PCMFromuLawAudioSource::doGetNextFrame() {
  // Figure out how many bytes of input data to ask for, and increase
  // our input buffer if necessary:
  unsigned bytesToRead = fMaxSize/2; // because we're converting 8 bits->16
  if (bytesToRead > fInputBufferSize) {
    delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead];
    fInputBufferSize = bytesToRead;
  }

  // Arrange to read samples into the input buffer:
  fInputSource->getNextFrame(fInputBuffer, bytesToRead,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

static u_int16_t linear16FromuLaw(unsigned char uLawByte) {
  static int const exp_lut[8] = {0,132,396,924,1980,4092,8316,16764};
  uLawByte = ~uLawByte;

  Boolean sign = (uLawByte & 0x80) != 0;
  unsigned char exponent = (uLawByte>>4) & 0x07;
  unsigned char mantissa = uLawByte & 0x0F;

  u_int16_t result = exp_lut[exponent] + (mantissa << (exponent+3));
  if (sign) result = -result;
  return result;
}

void PCMFromuLawAudioSource
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate uLaw samples (in the input buffer)
  // into 16-bit PCM samples (in the output buffer), in host order.
  unsigned numSamples = frameSize;
  u_int16_t* outputSample = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numSamples; ++i) {
    outputSample[i] = linear16FromuLaw(fInputBuffer[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numSamples*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// 16-bit values (in host order) -> 16-bit network order //////////

NetworkFromHostOrder16
::NetworkFromHostOrder16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

NetworkFromHostOrder16::~NetworkFromHostOrder16() {
}

void NetworkFromHostOrder16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void NetworkFromHostOrder16
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate the 16-bit values that we have just read from host
  // to network order (in-place)
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    value[i] = htons(value[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// 16-bit values (in network order) -> 16-bit host order //////////

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

HostFromNetworkOrder16
::HostFromNetworkOrder16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

HostFromNetworkOrder16::~HostFromNetworkOrder16() {
}

void HostFromNetworkOrder16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void HostFromNetworkOrder16
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate the 16-bit values that we have just read from network
  // to host order (in-place):
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    value[i] = ntohs(value[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

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

EndianSwap16::EndianSwap16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

EndianSwap16::~EndianSwap16() {
}

void EndianSwap16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void EndianSwap16::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
				      struct timeval presentationTime,
				      unsigned durationInMicroseconds) {
  // Swap the byte order of the 16-bit values that we have just read (in place):
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    u_int16_t const orig = value[i];
    value[i] = ((orig&0xFF)<<8) | ((orig&0xFF00)>>8);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize);
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

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

EndianSwap24::EndianSwap24(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

EndianSwap24::~EndianSwap24() {
}

void EndianSwap24::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void EndianSwap24::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
				      struct timeval presentationTime,
				      unsigned durationInMicroseconds) {
  // Swap the byte order of the 24-bit values that we have just read (in place):
  unsigned const numValues = frameSize/3;
  u_int8_t* p = fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    u_int8_t tmp = p[0];
    p[0] = p[2];
    p[2] = tmp;
    p += 3;
  }

  // Complete delivery to the client:
  fFrameSize = numValues*3;
  fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize);
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}


/**********
**********/
// Filters for converting between raw PCM audio and uLaw
// Implementation

#include "uLawAudioFilter.hh"

uLawFromPCMAudioSource* uLawFromPCMAudioSource
::createNew(UsageEnvironment& env, FramedSource* inputSource, int byteOrdering) {
  if (byteOrdering < 0 || byteOrdering > 2) {
    env.setResultMsg("uLawFromPCMAudioSource::createNew(): bad \"byteOrdering\" parameter");
    return NULL;
  }
  return new uLawFromPCMAudioSource(env, inputSource, byteOrdering);
}

uLawFromPCMAudioSource
::uLawFromPCMAudioSource(UsageEnvironment& env, FramedSource* inputSource,
			 int byteOrdering)
  : FramedFilter(env, inputSource),
    fByteOrdering(byteOrdering), fInputBuffer(NULL), fInputBufferSize(0) {
}

uLawFromPCMAudioSource::~uLawFromPCMAudioSource() {
  delete[] fInputBuffer;
}

void uLawFromPCMAudioSource::doGetNextFrame() {
  // Figure out how many bytes of input data to ask for, and increase
  // our input buffer if necessary:
  unsigned bytesToRead = fMaxSize*2; // because we're converting 16 bits->8
  if (bytesToRead > fInputBufferSize) {
    delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead];
    fInputBufferSize = bytesToRead;
  }

  // Arrange to read samples into the input buffer:
  fInputSource->getNextFrame(fInputBuffer, bytesToRead,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

#define BIAS 0x84   // the add-in bias for 16 bit samples
#define CLIP 32635

static unsigned char uLawFrom16BitLinear(u_int16_t sample) {
  static int const exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
				   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
  unsigned char sign = (sample >> 8) & 0x80;
  if (sign != 0) sample = -sample; // get the magnitude

  if (sample > CLIP) sample = CLIP; // clip the magnitude
  sample += BIAS;

  unsigned char exponent = exp_lut[(sample>>7) & 0xFF];
  unsigned char mantissa = (sample >> (exponent+3)) & 0x0F;
  unsigned char result = ~(sign | (exponent << 4) | mantissa);

  // CCITT trap

  return result;
}

void uLawFromPCMAudioSource
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate raw 16-bit PCM samples (in the input buffer)
  // into uLaw samples (in the output buffer).
  unsigned numSamples = frameSize/2;
  switch (fByteOrdering) {
    case 0: { // host order
      u_int16_t* inputSample = (u_int16_t*)fInputBuffer;
      for (unsigned i = 0; i < numSamples; ++i) {
	fTo[i] = uLawFrom16BitLinear(inputSample[i]);
      }
      break;
    }
    case 1: { // little-endian order
      for (unsigned i = 0; i < numSamples; ++i) {
	u_int16_t const newValue = (fInputBuffer[2*i+1]<<8)|fInputBuffer[2*i];
	fTo[i] = uLawFrom16BitLinear(newValue);
      }
      break;
    }
    case 2: { // network (i.e., big-endian) order
      for (unsigned i = 0; i < numSamples; ++i) {
	u_int16_t const newValue = (fInputBuffer[2*i]<<8)|fInputBuffer[2*i+i];
	fTo[i] = uLawFrom16BitLinear(newValue);
      }
      break;
    }
  }

  // Complete delivery to the client:
  fFrameSize = numSamples;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// u-Law -> 16-bit PCM (in host order) //////////

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

PCMFromuLawAudioSource
::PCMFromuLawAudioSource(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource),
    fInputBuffer(NULL), fInputBufferSize(0) {
}

PCMFromuLawAudioSource::~PCMFromuLawAudioSource() {
  delete[] fInputBuffer;
}

void PCMFromuLawAudioSource::doGetNextFrame() {
  // Figure out how many bytes of input data to ask for, and increase
  // our input buffer if necessary:
  unsigned bytesToRead = fMaxSize/2; // because we're converting 8 bits->16
  if (bytesToRead > fInputBufferSize) {
    delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead];
    fInputBufferSize = bytesToRead;
  }

  // Arrange to read samples into the input buffer:
  fInputSource->getNextFrame(fInputBuffer, bytesToRead,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

static u_int16_t linear16FromuLaw(unsigned char uLawByte) {
  static int const exp_lut[8] = {0,132,396,924,1980,4092,8316,16764};
  uLawByte = ~uLawByte;

  Boolean sign = (uLawByte & 0x80) != 0;
  unsigned char exponent = (uLawByte>>4) & 0x07;
  unsigned char mantissa = uLawByte & 0x0F;

  u_int16_t result = exp_lut[exponent] + (mantissa << (exponent+3));
  if (sign) result = -result;
  return result;
}

void PCMFromuLawAudioSource
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate uLaw samples (in the input buffer)
  // into 16-bit PCM samples (in the output buffer), in host order.
  unsigned numSamples = frameSize;
  u_int16_t* outputSample = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numSamples; ++i) {
    outputSample[i] = linear16FromuLaw(fInputBuffer[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numSamples*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// 16-bit values (in host order) -> 16-bit network order //////////

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

NetworkFromHostOrder16
::NetworkFromHostOrder16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

NetworkFromHostOrder16::~NetworkFromHostOrder16() {
}

void NetworkFromHostOrder16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void NetworkFromHostOrder16
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate the 16-bit values that we have just read from host
  // to network order (in-place)
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    value[i] = htons(value[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// 16-bit values (in network order) -> 16-bit host order //////////

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

HostFromNetworkOrder16
::HostFromNetworkOrder16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

HostFromNetworkOrder16::~HostFromNetworkOrder16() {
}

void HostFromNetworkOrder16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void HostFromNetworkOrder16
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate the 16-bit values that we have just read from network
  // to host order (in-place):
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    value[i] = ntohs(value[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

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

EndianSwap16::EndianSwap16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

EndianSwap16::~EndianSwap16() {
}

void EndianSwap16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void EndianSwap16::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
				      struct timeval presentationTime,
				      unsigned durationInMicroseconds) {
  // Swap the byte order of the 16-bit values that we have just read (in place):
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    u_int16_t const orig = value[i];
    value[i] = ((orig&0xFF)<<8) | ((orig&0xFF00)>>8);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize);
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

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

EndianSwap24::EndianSwap24(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

EndianSwap24::~EndianSwap24() {
}

void EndianSwap24::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void EndianSwap24::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
				      struct timeval presentationTime,
				      unsigned durationInMicroseconds) {
  // Swap the byte order of the 24-bit values that we have just read (in place):
  unsigned const numValues = frameSize/3;
  u_int8_t* p = fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    u_int8_t tmp = p[0];
    p[0] = p[2];
    p[2] = tmp;
    p += 3;
  }

  // Complete delivery to the client:
  fFrameSize = numValues*3;
  fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize);
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}


for (unsigned i = 0; i < numSamples; ++i) {
	u_int16_t const newValue = (fInputBuffer[2*i]<<8)|fInputBuffer[2*i+i];
	fTo[i] = uLawFrom16BitLinear(newValue);
      }

/**********
**********/
// Filters for converting between raw PCM audio and uLaw
// Implementation

#include "uLawAudioFilter.hh"

uLawFromPCMAudioSource* uLawFromPCMAudioSource
::createNew(UsageEnvironment& env, FramedSource* inputSource, int byteOrdering) {
  if (byteOrdering < 0 || byteOrdering > 2) {
    env.setResultMsg("uLawFromPCMAudioSource::createNew(): bad \"byteOrdering\" parameter");
    return NULL;
  }
  return new uLawFromPCMAudioSource(env, inputSource, byteOrdering);
}

uLawFromPCMAudioSource
::uLawFromPCMAudioSource(UsageEnvironment& env, FramedSource* inputSource,
			 int byteOrdering)
  : FramedFilter(env, inputSource),
    fByteOrdering(byteOrdering), fInputBuffer(NULL), fInputBufferSize(0) {
}

uLawFromPCMAudioSource::~uLawFromPCMAudioSource() {
  delete[] fInputBuffer;
}

void uLawFromPCMAudioSource::doGetNextFrame() {
  // Figure out how many bytes of input data to ask for, and increase
  // our input buffer if necessary:
  unsigned bytesToRead = fMaxSize*2; // because we're converting 16 bits->8
  if (bytesToRead > fInputBufferSize) {
    delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead];
    fInputBufferSize = bytesToRead;
  }

  // Arrange to read samples into the input buffer:
  fInputSource->getNextFrame(fInputBuffer, bytesToRead,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

#define BIAS 0x84   // the add-in bias for 16 bit samples
#define CLIP 32635

static unsigned char uLawFrom16BitLinear(u_int16_t sample) {
  static int const exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
				   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
  unsigned char sign = (sample >> 8) & 0x80;
  if (sign != 0) sample = -sample; // get the magnitude

  if (sample > CLIP) sample = CLIP; // clip the magnitude
  sample += BIAS;

  unsigned char exponent = exp_lut[(sample>>7) & 0xFF];
  unsigned char mantissa = (sample >> (exponent+3)) & 0x0F;
  unsigned char result = ~(sign | (exponent << 4) | mantissa);
  if (result == 0 ) result = 0x02;  // CCITT trap

  return result;
}

void uLawFromPCMAudioSource
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate raw 16-bit PCM samples (in the input buffer)
  // into uLaw samples (in the output buffer).
  unsigned numSamples = frameSize/2;
  switch (fByteOrdering) {
    case 0: { // host order
      u_int16_t* inputSample = (u_int16_t*)fInputBuffer;
      for (unsigned i = 0; i < numSamples; ++i) {
	fTo[i] = uLawFrom16BitLinear(inputSample[i]);
      }
      break;
    }
    case 1: { // little-endian order
      for (unsigned i = 0; i < numSamples; ++i) {
	u_int16_t const newValue = (fInputBuffer[2*i+1]<<8)|fInputBuffer[2*i];
	fTo[i] = uLawFrom16BitLinear(newValue);
      }
      break;
    }
    case 2: { // network (i.e., big-endian) order

      break;
    }
  }

  // Complete delivery to the client:
  fFrameSize = numSamples;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// u-Law -> 16-bit PCM (in host order) //////////

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

PCMFromuLawAudioSource
::PCMFromuLawAudioSource(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource),
    fInputBuffer(NULL), fInputBufferSize(0) {
}

PCMFromuLawAudioSource::~PCMFromuLawAudioSource() {
  delete[] fInputBuffer;
}

void PCMFromuLawAudioSource::doGetNextFrame() {
  // Figure out how many bytes of input data to ask for, and increase
  // our input buffer if necessary:
  unsigned bytesToRead = fMaxSize/2; // because we're converting 8 bits->16
  if (bytesToRead > fInputBufferSize) {
    delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead];
    fInputBufferSize = bytesToRead;
  }

  // Arrange to read samples into the input buffer:
  fInputSource->getNextFrame(fInputBuffer, bytesToRead,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

static u_int16_t linear16FromuLaw(unsigned char uLawByte) {
  static int const exp_lut[8] = {0,132,396,924,1980,4092,8316,16764};
  uLawByte = ~uLawByte;

  Boolean sign = (uLawByte & 0x80) != 0;
  unsigned char exponent = (uLawByte>>4) & 0x07;
  unsigned char mantissa = uLawByte & 0x0F;

  u_int16_t result = exp_lut[exponent] + (mantissa << (exponent+3));
  if (sign) result = -result;
  return result;
}

void PCMFromuLawAudioSource
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate uLaw samples (in the input buffer)
  // into 16-bit PCM samples (in the output buffer), in host order.
  unsigned numSamples = frameSize;
  u_int16_t* outputSample = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numSamples; ++i) {
    outputSample[i] = linear16FromuLaw(fInputBuffer[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numSamples*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// 16-bit values (in host order) -> 16-bit network order //////////

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

NetworkFromHostOrder16
::NetworkFromHostOrder16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

NetworkFromHostOrder16::~NetworkFromHostOrder16() {
}

void NetworkFromHostOrder16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void NetworkFromHostOrder16
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate the 16-bit values that we have just read from host
  // to network order (in-place)
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    value[i] = htons(value[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// 16-bit values (in network order) -> 16-bit host order //////////

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

HostFromNetworkOrder16
::HostFromNetworkOrder16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

HostFromNetworkOrder16::~HostFromNetworkOrder16() {
}

void HostFromNetworkOrder16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void HostFromNetworkOrder16
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate the 16-bit values that we have just read from network
  // to host order (in-place):
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    value[i] = ntohs(value[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

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

EndianSwap16::EndianSwap16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

EndianSwap16::~EndianSwap16() {
}

void EndianSwap16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void EndianSwap16::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
				      struct timeval presentationTime,
				      unsigned durationInMicroseconds) {
  // Swap the byte order of the 16-bit values that we have just read (in place):
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    u_int16_t const orig = value[i];
    value[i] = ((orig&0xFF)<<8) | ((orig&0xFF00)>>8);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize);
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

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

EndianSwap24::EndianSwap24(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

EndianSwap24::~EndianSwap24() {
}

void EndianSwap24::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void EndianSwap24::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
				      struct timeval presentationTime,
				      unsigned durationInMicroseconds) {
  // Swap the byte order of the 24-bit values that we have just read (in place):
  unsigned const numValues = frameSize/3;
  u_int8_t* p = fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    u_int8_t tmp = p[0];
    p[0] = p[2];
    p[2] = tmp;
    p += 3;
  }

  // Complete delivery to the client:
  fFrameSize = numValues*3;
  fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize);
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}


/**********
**********/
// Filters for converting between raw PCM audio and uLaw
// Implementation

#include "uLawAudioFilter.hh"

uLawFromPCMAudioSource* uLawFromPCMAudioSource
::createNew(UsageEnvironment& env, FramedSource* inputSource, int byteOrdering) {
  if (byteOrdering < 0 || byteOrdering > 2) {
    env.setResultMsg("uLawFromPCMAudioSource::createNew(): bad \"byteOrdering\" parameter");
    return NULL;
  }
  return new uLawFromPCMAudioSource(env, inputSource, byteOrdering);
}

uLawFromPCMAudioSource
::uLawFromPCMAudioSource(UsageEnvironment& env, FramedSource* inputSource,
			 int byteOrdering)
  : FramedFilter(env, inputSource),
    fByteOrdering(byteOrdering), fInputBuffer(NULL), fInputBufferSize(0) {
}

uLawFromPCMAudioSource::~uLawFromPCMAudioSource() {
  delete[] fInputBuffer;
}

void uLawFromPCMAudioSource::doGetNextFrame() {
  // Figure out how many bytes of input data to ask for, and increase
  // our input buffer if necessary:
  unsigned bytesToRead = fMaxSize*2; // because we're converting 16 bits->8
  if (bytesToRead > fInputBufferSize) {
    delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead];
    fInputBufferSize = bytesToRead;
  }

  // Arrange to read samples into the input buffer:
  fInputSource->getNextFrame(fInputBuffer, bytesToRead,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

#define BIAS 0x84   // the add-in bias for 16 bit samples
#define CLIP 32635

static unsigned char uLawFrom16BitLinear(u_int16_t sample) {
  static int const exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
				   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
  unsigned char sign = (sample >> 8) & 0x80;
  if (sign != 0) sample = -sample; // get the magnitude

  if (sample > CLIP) sample = CLIP; // clip the magnitude
  sample += BIAS;

  unsigned char exponent = exp_lut[(sample>>7) & 0xFF];
  unsigned char mantissa = (sample >> (exponent+3)) & 0x0F;
  unsigned char result = ~(sign | (exponent << 4) | mantissa);
  if (result == 0 ) result = 0x02;  // CCITT trap

  return result;
}

void uLawFromPCMAudioSource
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate raw 16-bit PCM samples (in the input buffer)
  // into uLaw samples (in the output buffer).
  unsigned numSamples = frameSize/2;
  switch (fByteOrdering) {
    case 0: { // host order
      u_int16_t* inputSample = (u_int16_t*)fInputBuffer;
      for (unsigned i = 0; i < numSamples; ++i) {
	fTo[i] = uLawFrom16BitLinear(inputSample[i]);
      }
      break;
    }
    case 1: { // little-endian order
      for (unsigned i = 0; i < numSamples; ++i) {
	u_int16_t const newValue = (fInputBuffer[2*i+1]<<8)|fInputBuffer[2*i];
	fTo[i] = uLawFrom16BitLinear(newValue);
      }
      break;
    }
    case 2: { // network (i.e., big-endian) order
      for (unsigned i = 0; i < numSamples; ++i) {
	u_int16_t const newValue = (fInputBuffer[2*i]<<8)|fInputBuffer[2*i+i];
	fTo[i] = uLawFrom16BitLinear(newValue);
      }
      break;
    }
  }

  // Complete delivery to the client:
  fFrameSize = numSamples;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// u-Law -> 16-bit PCM (in host order) //////////

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

PCMFromuLawAudioSource
::PCMFromuLawAudioSource(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource),
    fInputBuffer(NULL), fInputBufferSize(0) {
}

PCMFromuLawAudioSource::~PCMFromuLawAudioSource() {
  delete[] fInputBuffer;
}

void PCMFromuLawAudioSource::doGetNextFrame() {
  // Figure out how many bytes of input data to ask for, and increase
  // our input buffer if necessary:
  unsigned bytesToRead = fMaxSize/2; // because we're converting 8 bits->16
  if (bytesToRead > fInputBufferSize) {
    delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead];
    fInputBufferSize = bytesToRead;
  }

  // Arrange to read samples into the input buffer:
  fInputSource->getNextFrame(fInputBuffer, bytesToRead,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

static u_int16_t linear16FromuLaw(unsigned char uLawByte) {
  static int const exp_lut[8] = {0,132,396,924,1980,4092,8316,16764};
  uLawByte = ~uLawByte;

  Boolean sign = (uLawByte & 0x80) != 0;
  unsigned char exponent = (uLawByte>>4) & 0x07;
  unsigned char mantissa = uLawByte & 0x0F;

  u_int16_t result = exp_lut[exponent] + (mantissa << (exponent+3));
  if (sign) result = -result;
  return result;
}

void PCMFromuLawAudioSource
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate uLaw samples (in the input buffer)
  // into 16-bit PCM samples (in the output buffer), in host order.
  unsigned numSamples = frameSize;
  u_int16_t* outputSample = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numSamples; ++i) {
    outputSample[i] = linear16FromuLaw(fInputBuffer[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numSamples*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// 16-bit values (in host order) -> 16-bit network order //////////

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

NetworkFromHostOrder16
::NetworkFromHostOrder16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

NetworkFromHostOrder16::~NetworkFromHostOrder16() {
}

void NetworkFromHostOrder16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void NetworkFromHostOrder16
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate the 16-bit values that we have just read from host
  // to network order (in-place)
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    value[i] = htons(value[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

////////// 16-bit values (in network order) -> 16-bit host order //////////

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

HostFromNetworkOrder16
::HostFromNetworkOrder16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

HostFromNetworkOrder16::~HostFromNetworkOrder16() {
}

void HostFromNetworkOrder16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void HostFromNetworkOrder16
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
		     struct timeval presentationTime,
		     unsigned durationInMicroseconds) {
  // Translate the 16-bit values that we have just read from network
  // to host order (in-place):
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    value[i] = ntohs(value[i]);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

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

EndianSwap16::EndianSwap16(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

EndianSwap16::~EndianSwap16() {
}

void EndianSwap16::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void EndianSwap16::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
				      struct timeval presentationTime,
				      unsigned durationInMicrosecon

ds) {
  // Swap the byte order of the 16-bit values that we have just read (in place):
  unsigned numValues = frameSize/2;
  u_int16_t* value = (u_int16_t*)fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    u_int16_t const orig = value[i];
    value[i] = ((orig&0xFF)<<8) | ((orig&0xFF00)>>8);
  }

  // Complete delivery to the client:
  fFrameSize = numValues*2;
  fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize);
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

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

EndianSwap24::EndianSwap24(UsageEnvironment& env,
			 FramedSource* inputSource)
  : FramedFilter(env, inputSource) {
}

EndianSwap24::~EndianSwap24() {
}

void EndianSwap24::doGetNextFrame() {
  // Arrange to read data directly into the client's buffer:
  fInputSource->getNextFrame(fTo, fMaxSize,
			     afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}

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

void EndianSwap24::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
				      struct timeval presentationTime,
				      unsigned durationInMicroseconds) {
  // Swap the byte order of the 24-bit values that we have just read (in place):
  unsigned const numValues = frameSize/3;
  u_int8_t* p = fTo;
  for (unsigned i = 0; i < numValues; ++i) {
    u_int8_t tmp = p[0];
    p[0] = p[2];
    p[2] = tmp;
    p += 3;
  }

  // Complete delivery to the client:
  fFrameSize = numValues*3;
  fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize);
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}


/*
*  This file is part of aasdk library project.
*
*/

#include <cstring>
#include <f1x/aasdk/Transport/DataSink.hpp>
#include <f1x/aasdk/Error/Error.hpp>

namespace f1x
{
namespace aasdk
{
namespace transport
{

DataSink::DataSink()
    : data_(common::cStaticDataSize)
{
}

common::DataBuffer DataSink::fill()
{
    const auto offset = data_.size();
    data_.resize(data_.size() + cChunkSize);

    auto ptr = data_.is_linearized() ? &data_[offset] : data_.linearize() + offset;
    return common::DataBuffer(ptr, cChunkSize);
}

void DataSink::commit(common::Data::size_type size)
{
    if(size > cChunkSize)
    {
        throw error::Error(error::ErrorCode::DATA_SINK_COMMIT_OVERFLOW);
    }

    data_.erase_end((cChunkSize - size));
}

common::Data::size_type DataSink::getAvailableSize()
{
    return data_.size();
}

{
    if(size > data_.size())
    {
        throw error::Error(error::ErrorCode::DATA_SINK_CONSUME_UNDERFLOW);
    }

    common::Data data(size, 0);
    std::copy(data_.begin(), data_.begin() + size, data.begin());
    data_.erase_begin(size);

    return data;
}

}
}
}


{
    if(size > cChunkSize)
    {
        throw error::Error(error::ErrorCode::DATA_SINK_COMMIT_OVERFLOW);
    }

    data_.erase_end((cChunkSize - size));
}

/*
*  This file is part of aasdk library project.
*
*/

#include <cstring>
#include <f1x/aasdk/Transport/DataSink.hpp>
#include <f1x/aasdk/Error/Error.hpp>

namespace f1x
{
namespace aasdk
{
namespace transport
{

DataSink::DataSink()
    : data_(common::cStaticDataSize)
{
}

common::DataBuffer DataSink::fill()
{
    const auto offset = data_.size();
    data_.resize(data_.size() + cChunkSize);

    auto ptr = data_.is_linearized() ? &data_[offset] : data_.linearize() + offset;
    return common::DataBuffer(ptr, cChunkSize);
}

void DataSink::commit(common::Data::size_type size)

common::Data::size_type DataSink::getAvailableSize()
{
    return data_.size();
}

common::Data DataSink::consume(common::Data::size_type size)
{
    if(size > data_.size())
    {
        throw error::Error(error::ErrorCode::DATA_SINK_CONSUME_UNDERFLOW);
    }

    common::Data data(size, 0);
    std::copy(data_.begin(), data_.begin() + size, data.begin());
    data_.erase_begin(size);

    return data;
}

}
}
}


{
    if(size > cChunkSize)
    {
        throw error::Error(error::ErrorCode::DATA_SINK_COMMIT_OVERFLOW);
    }

    data_.erase_end((cChunkSize - size));
}

common::Data::size_type DataSink::getAvailableSize()
{
    return data_.size();
}

common::Data DataSink::consume(common::Data::size_type size)
{
    if(size > data_.size())
    {
        throw error::Error(error::ErrorCode::DATA_SINK_CONSUME_UNDERFLOW);
    }

    common::Data data(size, 0);
    std::copy(data_.begin(), data_.begin() + size, data.begin());
    data_.erase_begin(size);

    return data;
}

}
}
}


/*
*  This file is part of aasdk library project.
*
*/

#include <cstring>
#include <f1x/aasdk/Transport/DataSink.hpp>
#include <f1x/aasdk/Error/Error.hpp>

namespace f1x
{
namespace aasdk
{
namespace transport
{

DataSink::DataSink()
    : data_(common::cStaticDataSize)
{
}

common::DataBuffer DataSink::fill()
{
    const auto offset = data_.size();
    data_.resize(data_.size() + cChunkSize);

    auto ptr = data_.is_linearized() ? &data_[offset] : data_.linearize() + offset;
    return common::DataBuffer(ptr, cChunkSize);
}

void DataSink::commit(common::Data::size_type size)
{
    if(size > cChunkSize)
    {
        throw error::Error(error::ErrorCode::DATA_SINK_COMMIT_OVERFLOW);
    }

}

common::Data::size_type DataSink::getAvailableSize()
{
    return data_.size();
}

common::Data DataSink::consume(common::Data::size_type size)
{
    if(size > data_.size())
    {
        throw error::Error(error::ErrorCode::DATA_SINK_CONSUME_UNDERFLOW);
    }

    common::Data data(size, 0);
    std::copy(data_.begin(), data_.begin() + size, data.begin());
    data_.erase_begin(size);

    return data;
}

}
}
}


/*
*  This file is part of aasdk library project.
*
*/

#include <cstring>
#include <f1x/aasdk/Transport/DataSink.hpp>
#include <f1x/aasdk/Error/Error.hpp>

namespace f1x
{
namespace aasdk
{
namespace transport
{

DataSink::DataSink()
    : data_(common::cStaticDataSize)
{
}

common::DataBuffer DataSink::fill()
{
    const auto offset = data_.size();
    data_.resize(data_.size() + cChunkSize);

    auto ptr = data_.is_linearized() ? &data_[offset] : data_.linearize() + offset;
    return common::DataBuffer(ptr, cChunkSize);
}

void DataSink::commit(common::Data::size_type size)
{
    if(size > cChunkSize)
    {
        throw error::Error(error::ErrorCode::DATA_SINK_COMMIT_OVERFLOW);
    }

    data_.erase_end((cChunkSize - size));


/*
*  This file is part of aasdk library project.
*
*/

#include <cstring>
#include <f1x/aasdk/Transport/DataSink.hpp>
#include <f1x/aasdk/Error/Error.hpp>

namespace f1x
{
namespace aasdk
{
namespace transport
{

DataSink::DataSink()
    : data_(common::cStaticDataSize)
{
}

common::DataBuffer DataSink::fill()
{
    const auto offset = data_.size();
    data_.resize(data_.size()

    auto ptr = data_.is_linearized() ? &data_[offset] : data_.linearize() + offset;
    return common::DataBuffer(ptr, cChunkSize);
}

void DataSink::commit(common::Data::size_type size)
{
    if(size > cChunkSize)
    {
        throw error::Error(error::ErrorCode::DATA_SINK_COMMIT_OVERFLOW);
    }

    data_.erase_end((cChunkSize - size));
}

common::Data::size_type DataSink::getAvailableSize()
{
    return data_.size();
}

common::Data DataSink::consume(common::Data::size_type size)
{
    if(size > data_.size())
    {
        throw error::Error(error::ErrorCode::DATA_SINK_CONSUME_UNDERFLOW);
    }

    common::Data data(size, 0);
    std::copy(data_.begin(), data_.begin() + size, data.begin());
    data_.erase_begin(size);

    return data;
}

}
}
}


if(size > cChunkSize)
    {
        throw error::Error(error::ErrorCode::DATA_SINK_COMMIT_OVERFLOW);
    }

/*
*  This file is part of aasdk library project.
*
*/

#include <cstring>
#include <f1x/aasdk/Transport/DataSink.hpp>
#include <f1x/aasdk/Error/Error.hpp>

namespace f1x
{
namespace aasdk
{
namespace transport
{

DataSink::DataSink()
    : data_(common::cStaticDataSize)
{
}

common::DataBuffer DataSink::fill()
{
    const auto offset = data_.size();
    data_.resize(data_.size() + cChunkSize);

    auto ptr = data_.is_linearized() ? &data_[offset] : data_.linearize() + offset;
    return common::DataBuffer(ptr, cChunkSize);
}

void DataSink::commit(common::Data::size_type size)
{


/*
*  This file is part of aasdk library project.
*
*/

#include <cstring>
#include <f1x/aasdk/Transport/DataSink.hpp>
#include <f1x/aasdk/Error/Error.hpp>

namespace f1x
{
namespace aasdk
{
namespace transport
{

DataSink::DataSink()
    : data_(common::cStaticDataSize)
{
}

common::DataBuffer DataSink::fill()
{
    const auto offset = data_.size();
    data_.resize(data_.size() + cChunkSize);

    auto ptr = data_.is_linearized() ? &data_[offset] : da

ta_.linearize() + offset;
    return common::DataBuffer(ptr, cChunkSize);
}

void DataSink::commit(common::Data::size_type size)
{
    if(size > cChunkSize)
    {
        throw error::Error(error::ErrorCode::DATA_SINK_COMMIT_OVERFLOW);
    }

    data_.erase_end((cChunkSize - size));
}

common::Data::size_type DataSink::getAvailableSize()
{
    return data_.size();
}

common::Data DataSink::consume(common::Data::size_type size)
{
    if(size > data_.size())
    {
        throw error::Error(error::ErrorCode::DATA_SINK_CONSUME_UNDERFLOW);
    }

    common::Data data(size, 0);
    std::copy(data_.begin(), data_.begin() + size, data.begin());
    data_.erase_begin(size);

    return data;
}

}
}
}


/*=============================================================================

  Library: CppMicroServices

  file at the top-level directory of this distribution and at
  http://example.com .

=============================================================================*/

#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"
#include "cppmicroservices/LDAPProp.h"
#include "cppmicroservices/ServiceRegistration.h"
#include "cppmicroservices/ServiceTracker.h"

#include "cppmicroservices/util/String.h"

#include <condition_variable>
#include <future>
#include <queue>
#include <thread>

namespace cppmicroservices {

class TestBundleC1Activator
  : public BundleActivator
  , public ServiceTrackerCustomizer<void>
{
public:
  TestBundleC1Activator()
    : context()
    , stop(false)
    , count(0)
  {}

  ~TestBundleC1Activator() {}

  void Start(BundleContext context)
  {
    this->context = context;

    cppmicroservices::LDAPFilter filter(
      cppmicroservices::LDAPProp(Constants::OBJECTCLASS) ==
        "org.cppmicroservices.c1.*" &&
      cppmicroservices::LDAPProp("i") == 5);
    tracker.reset(new ServiceTracker<void>(context, filter, this));
    tracker->Open();

    std::promise<void> p;
    std::shared_future<void> f = p.get_future().share();

    // Start ten threads, each registering ten services
    for (int c = 0; c < 10; ++c) {
      threads.emplace_back(
        &TestBundleC1Activator::RegisterServices, this, f, c);
    }

    // Start a thread that unregisters the services
    unregThread = std::thread(&TestBundleC1Activator::UnRegister, this);

    p.set_value();
  }

  void Stop(BundleContext)
  {
    for (auto& th : threads) {
      th.join();
    }

    stop = true;
    unregThread.join();

    tracker->Close();

    if (count != 0)
      throw std::logic_error("Not unregistered all services");
  }

  void RegisterServices(std::shared_future<void> f, int c)
  {
    f.wait();

    for (int i = 0; i < 10; ++i) {
      // Register a service ten times with different properties
      InterfaceMap im;
      im[std::string("org.cppmicroservices.c1.") + util::ToString(c)] =
        std::make_shared<int>(1);
      ServiceProperties props;
      props["i"] = i;
      auto reg = context.RegisterService(
        std::make_shared<const InterfaceMap>(im), props);
      regs.Lock(), regs.v.emplace_back(std::move(reg));
      count++;
      if (i % 5 == 0)
        regs.NotifyAll();
    }
  }

{
    while (!(stop.load() && (regs.Lock(), regs.v.empty()))) {
      std::vector<ServiceRegistrationU> currRegs;
      regs.Lock(), std::swap(currRegs, regs.v);
      if (currRegs.empty()) {
        auto l = regs.Lock();
        regs.WaitFor(l, std::chrono::milliseconds(100), [this] {
          return !regs.v.empty();
        });
        std::swap(currRegs, regs.v);
      }

      for (auto& reg : currRegs) {
        if (reg.GetReference().GetProperty("i") == 5) {
          // change a property, unregister later
          ServiceProperties newProps;
          newProps["i"] = 15;
          reg.SetProperties(newProps);
          regs.Lock(), regs.v.push_back(reg);
        } else {
          reg.Unregister();
          count--;
        }
      }
    }
  }

  // -------------------- Service Tracker Customizer -------------------------

  InterfaceMapConstPtr AddingService(const ServiceReferenceU& reference)
  {
    if (reference.GetProperty("i") == 5) {
      auto l = additionalReg.Lock();
      if (!additionalReg.v) {
        InterfaceMap im;
        im[std::string("org.cppmicroservices.c1.additional")] =
          std::make_shared<int>(2);
        additionalReg.v =
          context.RegisterService(std::make_shared<const InterfaceMap>(im));
      }
      return InterfaceMapConstPtr();
    }
    return context.GetService(reference);
  }

  void ModifiedService(const ServiceReferenceU& reference,
                       const InterfaceMapConstPtr& /*service*/)
  {
    if (reference.GetProperty("i") != 5)
      throw std::logic_error("modified end match: wrong property");
    context.GetService(reference);
  }

  void RemovedService(const ServiceReferenceU& /*reference*/,
                      const InterfaceMapConstPtr& /*service*/)
  {
    auto l = additionalReg.Lock();
    if (additionalReg.v)
      additionalReg.v.Unregister();
  }

private:
  BundleContext context;

  std::unique_ptr<ServiceTracker<void>> tracker;

  std::vector<std::thread> threads;
  std::thread unregThread;

  struct
    : detail::MultiThreaded<detail::MutexLockingStrategy<>,
                            detail::WaitCondition>
  {
    std::vector<ServiceRegistrationU> v;
  } regs;

  struct : detail::MultiThreaded<>
  {
    ServiceRegistrationU v;
  } additionalReg;

  std::atomic<bool> stop;
  std::atomic<int> count;
};

} // namespace cppmicroservices

CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(
  cppmicroservices::TestBundleC1Activator)


/*=============================================================================

  Library: CppMicroServices

  file at the top-level directory of this distribution and at
  http://example.com .

=============================================================================*/

#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"
#include "cppmicroservices/LDAPProp.h"
#include "cppmicroservices/ServiceRegistration.h"
#include "cppmicroservices/ServiceTracker.h"

#include "cppmicroservices/util/String.h"

#include <condition_variable>
#include <future>
#include <queue>
#include <thread>

namespace cppmicroservices {

class TestBundleC1Activator
  : public BundleActivator
  , public ServiceTrackerCustomizer<void>
{
public:
  TestBundleC1Activator()
    : context()
    , stop(false)
    , count(0)

  ~TestBundleC1Activator() {}

  void Start(BundleContext context)
  {
    this->context = context;

    cppmicroservices::LDAPFilter filter(
      cppmicroservices::LDAPProp(Constants::OBJECTCLASS) ==
        "org.cppmicroservices.c1.*" &&
      cppmicroservices::LDAPProp("i") == 5);
    tracker.reset(new ServiceTracker<void>(context, filter, this));
    tracker->Open();

    std::promise<void> p;
    std::shared_future<void> f = p.get_future().share();

    // Start ten threads, each registering ten services
    for (int c = 0; c < 10; ++c) {
      threads.emplace_back(
        &TestBundleC1Activator::RegisterServices, this, f, c);
    }

    // Start a thread that unregisters the services
    unregThread = std::thread(&TestBundleC1Activator::UnRegister, this);

    p.set_value();
  }

  void Stop(BundleContext)
  {
    for (auto& th : threads) {
      th.join();
    }

    stop = true;
    unregThread.join();

    tracker->Close();

    if (count != 0)
      throw std::logic_error("Not unregistered all services");
  }

  void RegisterServices(std::shared_future<void> f, int c)
  {
    f.wait();

    for (int i = 0; i < 10; ++i) {
      // Register a service ten times with different properties
      InterfaceMap im;
      im[std::string("org.cppmicroservices.c1.") + util::ToString(c)] =
        std::make_shared<int>(1);
      ServiceProperties props;
      props["i"] = i;
      auto reg = context.RegisterService(
        std::make_shared<const InterfaceMap>(im), props);
      regs.Lock(), regs.v.emplace_back(std::move(reg));
      count++;
      if (i % 5 == 0)
        regs.NotifyAll();
    }
  }

  void UnRegister()
  {
    while (!(stop.load() && (regs.Lock(), regs.v.empty()))) {
      std::vector<ServiceRegistrationU> currRegs;
      regs.Lock(), std::swap(currRegs, regs.v);
      if (currRegs.empty()) {
        auto l = regs.Lock();
        regs.WaitFor(l, std::chrono::milliseconds(100), [this] {
          return !regs.v.empty();
        });
        std::swap(currRegs, regs.v);
      }

      for (auto& reg : currRegs) {
        if (reg.GetReference().GetProperty("i") == 5) {
          // change a property, unregister later
          ServiceProperties newProps;
          newProps["i"] = 15;
          reg.SetProperties(newProps);
          regs.Lock(), regs.v.push_back(reg);
        } else {
          reg.Unregister();
          count--;
        }
      }
    }
  }

  // -------------------- Service Tracker Customizer -------------------------

  InterfaceMapConstPtr AddingService(const ServiceReferenceU& reference)
  {
    if (reference.GetProperty("i") == 5) {
      auto l = additionalReg.Lock();
      if (!additionalReg.v) {
        InterfaceMap im;
        im[std::string("org.cppmicroservices.c1.additional")] =
          std::make_shared<int>(2);
        additionalReg.v =
          context.RegisterService(std::make_shared<const InterfaceMap>(im));
      }
      return InterfaceMapConstPtr();
    }
    return context.GetService(reference);
  }

  void ModifiedService(const ServiceReferenceU& reference,
                       const InterfaceMapConstPtr& /*service*/)
  {
    if (reference.GetProperty("i") != 5)
      throw std::logic_error("modified end match: wrong property");
    context.GetService(reference);
  }

  void RemovedService(const ServiceReferenceU& /*reference*/,
                      const InterfaceMapConstPtr& /*service*/)
  {
    auto l = additionalReg.Lock();
    if (additionalReg.v)
      additionalReg.v.Unregister();
  }

private:
  BundleContext context;

  std::unique_ptr<ServiceTracker<void>> tracker;

  std::vector<std::thread> threads;
  std::thread unregThread;

  struct
    : detail::MultiThreaded<detail::MutexLockingStrategy<>,
                            detail::WaitCondition>
  {
    std::vector<ServiceRegistrationU> v;
  } regs;

  struct : detail::MultiThreaded<>
  {
    ServiceRegistrationU v;
  } additionalReg;

  std::atomic<bool> stop;
  std::atomic<int> count;
};

} // namespace cppmicroservices

CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(
  cppmicroservices::TestBundleC1Activator)


{
    if (reference.GetProperty("i") == 5) {
      auto l = additionalReg.Lock();
      if (!additionalReg.v) {
        InterfaceMap im;
        im[std::string("org.cppmicroservices.c1.additional")] =
          std::make_shared<int>(2);
        additionalReg.v =
          context.RegisterService(std::make_shared<const InterfaceMap>(im));
      }
      return InterfaceMapConstPtr();
    }

/*=============================================================================

  Library: CppMicroServices

  file at the top-level directory of this distribution and at
  http://example.com .

=============================================================================*/

#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"
#include "cppmicroservices/LDAPProp.h"
#include "cppmicroservices/ServiceRegistration.h"
#include "cppmicroservices/ServiceTracker.h"

#include "cppmicroservices/util/String.h"

#include <condition_variable>
#include <future>
#include <queue>
#include <thread>

namespace cppmicroservices {

class TestBundleC1Activator
  : public BundleActivator
  , public ServiceTrackerCustomizer<void>
{
public:
  TestBundleC1Activator()
    : context()
    , stop(false)
    , count(0)
  {}

  ~TestBundleC1Activator() {}

  void Start(BundleContext context)
  {
    this->context = context;

    cppmicroservices::LDAPFilter filter(
      cppmicroservices::LDAPProp(Constants::OBJECTCLASS) ==
        "org.cppmicroservices.c1.*" &&
      cppmicroservices::LDAPProp("i") == 5);
    tracker.reset(new ServiceTracker<void>(context, filter, this));
    tracker->Open();

    std::promise<void> p;
    std::shared_future<void> f = p.get_future().share();

    // Start ten threads, each registering ten services
    for (int c = 0; c < 10; ++c) {
      threads.emplace_back(
        &TestBundleC1Activator::RegisterServices, this, f, c);
    }

    // Start a thread that unregisters the services
    unregThread = std::thread(&TestBundleC1Activator::UnRegister, this);

    p.set_value();
  }

  void Stop(BundleContext)
  {
    for (auto& th : threads) {
      th.join();
    }

    stop = true;
    unregThread.join();

    tracker->Close();

    if (count != 0)
      throw std::logic_error("Not unregistered all services");
  }

  void RegisterServices(std::shared_future<void> f, int c)
  {
    f.wait();

    for (int i = 0; i < 10; ++i) {
      // Register a service ten times with different properties
      InterfaceMap im;
      im[std::string("org.cppmicroservices.c1.") + util::ToString(c)] =
        std::make_shared<int>(1);
      ServiceProperties props;
      props["i"] = i;
      auto reg = context.RegisterService(
        std::make_shared<const InterfaceMap>(im), props);
      regs.Lock(), regs.v.emplace_back(std::move(reg));
      count++;
      if (i % 5 == 0)
        regs.NotifyAll();
    }
  }

  void UnRegister()
  {
    while (!(stop.load() && (regs.Lock(), regs.v.empty()))) {
      std::vector<ServiceRegistrationU> currRegs;
      regs.Lock(), std::swap(currRegs, regs.v);
      if (currRegs.empty()) {
        auto l = regs.Lock();
        regs.WaitFor(l, std::chrono::milliseconds(100), [this] {
          return !regs.v.empty();
        });
        std::swap(currRegs, regs.v);
      }

      for (auto& reg : currRegs) {
        if (reg.GetReference().GetProperty("i") == 5) {
          // change a property, unregister later
          ServiceProperties newProps;
          newProps["i"] = 15;
          reg.SetProperties(newProps);
          regs.Lock(), regs.v.push_back(reg);
        } else {
          reg.Unregister();
          count--;
        }
      }
    }
  }

  // -------------------- Service Tracker Customizer -------------------------

  InterfaceMapConstPtr AddingService(const ServiceReferenceU& reference)

    return context.GetService(reference);
  }

  void ModifiedService(const ServiceReferenceU& reference,
                       const InterfaceMapConstPtr& /*service*/)
  {
    if (reference.GetProperty("i") != 5)
      throw std::logic_error("modified end match: wrong property");
    context.GetService(reference);
  }

  void RemovedService(const ServiceReferenceU& /*reference*/,
                      const InterfaceMapConstPtr& /*service*/)
  {
    auto l = additionalReg.Lock();
    if (additionalReg.v)
      additionalReg.v.Unregister();
  }

private:
  BundleContext context;

  std::unique_ptr<ServiceTracker<void>> tracker;

  std::vector<std::thread> threads;
  std::thread unregThread;

  struct
    : detail::MultiThreaded<detail::MutexLockingStrategy<>,
                            detail::WaitCondition>
  {
    std::vector<ServiceRegistrationU> v;
  } regs;

  struct : detail::MultiThreaded<>
  {
    ServiceRegistrationU v;
  } additionalReg;

  std::atomic<bool> stop;
  std::atomic<int> count;
};

} // namespace cppmicroservices

CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(
  cppmicroservices::TestBundleC1Activator)


/*=============================================================================

  Library: CppMicroServices

  file at the top-level directory of this distribution and at
  http://example.com .

=============================================================================*/

#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"
#include "cppmicroservices/LDAPProp.h"
#include "cppmicroservices/ServiceRegistration.h"
#include "cppmicroservices/ServiceTracker.h"

#include "cppmicroservices/util/String.h"

#include <condition_variable>
#include <future>
#include <queue>
#include <thread>

namespace cppmicroservices {

class TestBundleC1Activator
  : public BundleActivator
  , public ServiceTrackerCustomizer<void>
{
public:
  TestBundleC1Activator()
    : context()
    , stop(false)
    , count(0)
  {}

  ~TestBundleC1Activator() {}

  void Start(BundleContext context)
  {
    this->context = context;

    cppmicroservices::LDAPFilter filter(
      cppmicroservices::LDAPProp(Constants::OBJECTCLASS) ==
        "org.cppmicroservices.c1.*" &&
      cppmicroservices::LDAPProp("i") == 5);
    tracker.reset(new ServiceTracker<void>(context, filter, this));
    tracker->Open();

    std::promise<void> p;
    std::shared_future<void> f = p.get_future().share();

    // Start ten threads, each registering ten services
    for (int c = 0; c < 10; ++c) {
      threads.emplace_back(
        &TestBundleC1Activator::RegisterServices, this, f, c);
    }

    // Start a thread that unregisters the services
    unregThread = std::thread(&TestBundleC1Activator::UnRegister, this);

    p.set_value();
  }

  void Stop(BundleContext)
  {
    for (auto& th : threads) {
      th.join();
    }

    stop = true;
    unregThread.join();

    tracker->Close();

    if (count != 0)
      throw std::logic_error("Not unregistered all services");
  }

  void RegisterServices(std::shared_future<void> f, int c)
  {
    f.wait();

    for (int i = 0; i < 10; ++i) {
      // Register a service ten times with different properties
      InterfaceMap im;
      im[std::string("org.cppmicroservices.c1.") + util::ToString(c)] =
        std::make_shared<int>(1);
      ServiceProperties props;
      props["i"] = i;
      auto reg = context.RegisterService(
        std::make_shared<const InterfaceMap>(im), props);
      regs.Lock(), regs.v.emplace_back(std::move(reg));
      count++;
      if (i % 5 == 0)
        regs.NotifyAll();
    }
  }

  void UnRegister()
  {
    while (!(stop.load() && (regs.Lock(), regs.v.empty()))) {
      std::vector<ServiceRegistrationU> currRegs;
      regs.Lock(), std::swap(currRegs, regs.v);
      if (currRegs.empty()) {
        auto l = regs.Lock();
        regs.WaitFor(l, std::chrono::milliseconds(100), [this] {
          return !regs.v.empty();
        });
        std::swap(currRegs, regs.v);
      }

      for (auto& reg : currRegs) {
        if (reg.GetReference().GetProperty("i") == 5) {
          // change a property, unregister later
          ServiceProperties newProps;
          newProps["i"] = 15;
          reg.SetProperties(newProps);
          regs.Lock(), regs.v.push_back(reg);
        } else {
          reg.Unregister();
          count--;
        }
      }
    }
  }

  // -------------------- Service Tracker Customizer -------------------------

  InterfaceMapConstPtr AddingService(const ServiceReferenceU& reference)
  {
    if (reference.GetProperty("i") == 5) {
      auto l = additionalReg.Lock();
      if (!additionalReg.v) {
        InterfaceMap im;
        im[std::string("org.cppmicroservices.c1.additional")] =
          std::make_shared<int>(2);
        additionalReg.v =
          context.RegisterService(std::make_shared<const InterfaceMap>(im));
      }
      return InterfaceMapConstPtr();
    }

  }

  void ModifiedService(const ServiceReferenceU& reference,
                       const InterfaceMapConstPtr& /*service*/)
  {
    if (reference.GetProperty("i") != 5)
      throw std::logic_error("modified end match: wrong property");
    context.GetService(reference);
  }

  void RemovedService(const ServiceReferenceU& /*reference*/,
                      const InterfaceMapConstPtr& /*service*/)
  {
    auto l = additionalReg.Lock();
    if (additionalReg.v)
      additionalReg.v.Unregister();
  }

private:
  BundleContext context;

  std::unique_ptr<ServiceTracker<void>> tracker;

  std::vector<std::thread> threads;
  std::thread unregThread;

  struct
    : detail::MultiThreaded<detail::MutexLockingStrategy<>,
                            detail::WaitCondition>
  {
    std::vector<ServiceRegistrationU> v;
  } regs;

  struct : detail::MultiThreaded<>
  {
    ServiceRegistrationU v;
  } additionalReg;

  std::atomic<bool> stop;
  std::atomic<int> count;
};

} // namespace cppmicroservices

CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(
  cppmicroservices::TestBundleC1Activator)