/*
 * cdda_lib.c: The data mangle functions
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: cdda_lib.c,v 1.1.1.4 2005/06/27 13:34:02 cm Exp $
 */

#include <string.h>
#include "cdda_lib.h"

// --- cReferenceCounter ------------------------------------------------------

cReferenceCounter::cReferenceCounter() {
  refCount = 0;
}

cReferenceCounter::~cReferenceCounter() {
}

int cReferenceCounter::RefDecr(void) {
  return --refCount;
}

int cReferenceCounter::RefIncr(void) {
  return ++refCount;
}

// --- cEncapsulator ----------------------------------------------------------

cEncapsulator::cEncapsulator() {
  p_offset = NULL;
  pesStreamId = pesFlags = pcmFrequency = pcmChannels = 0x00;
}

cEncapsulator::~cEncapsulator() {
}

int cEncapsulator::Data(uint8_t *payload, int length) {
  memset(buffer, 0x00, PES_MAX_PACKSIZE);
  if (PES_MAX_PACKSIZE >= length + PES_HEADER_LEN + LPCM_HEADER_LEN) {
    p_offset = buffer + (PES_MAX_PACKSIZE - length);
    memcpy(p_offset, payload, length);
  } else
    return 0;

  return PES_MAX_PACKSIZE - (p_offset - buffer);
}

void cEncapsulator::SetPcmParameters(uint8_t channels, uint16_t frequency) {
  pcmChannels = channels - 1;  // n - 1 = n channels

  switch (frequency) {
    case 48000:
      pcmFrequency = 0x00;
      break;
    case 96000:
      pcmFrequency = 0x10;
      break;
    case 44100:
      pcmFrequency = 0x20;
      break;
    case 32000:
      pcmFrequency = 0x30;
      break;
  }
}

int cEncapsulator::ToPcm(uint8_t **packet = NULL) {
  // LINEAR PULSE CODE MODULATION Packet
  // lpcm->sub_stream_id
  *(p_offset-7) = 0xA0;
  // lpcm->number_of_frame_headers
  *(p_offset-6) = 0xFF;
  // lpcm->first_access_unit_pointer:16
  // *(p_offset-5) = 0x00;
  // *(p_offset-4) = 0x00;
  // lpcm->audio_emphasis_flag:1
  // lpcm->audio_mute_flag:1
  // lpcm->reserved1:1
  // lpcm->audio_frame_number:5
  // *(p_offset-3) = 0x00;
  // lpcm->quantisation_word_length:2
  // lpcm->audio_sample_frequency:2
  // lpcm->reserved2:1
  // lpcm->number_of_audio_channels:3
  *(p_offset-2) = pcmFrequency | pcmChannels;
  // lpcm->dynamic_range_x:3
  // lpcm->dynamic_range_y:5
  *(p_offset-1) = 0x80;

  p_offset-=LPCM_HEADER_LEN;
  if (packet)
    *packet = p_offset;

  return PES_MAX_PACKSIZE - (p_offset - buffer);
}

void cEncapsulator::SetPesParameters(uint8_t streamId, uint16_t flags) {
  pesStreamId = streamId;
  pesFlags = flags;
}

int cEncapsulator::ToPes(uint8_t **packet = NULL) {
  int l = PES_MAX_PACKSIZE - (p_offset - buffer);

  // PACKETIZED ELEMENTARY STREAM Packet
  // pes->packet_start_code_prefix[x]
  // *(p_offset-9) = *(p_offset-8) = 0x00;
  *(p_offset-7) = 0x01;
  // pes->stream_id
  *(p_offset-6) = pesStreamId;
  // pes->packet_length:16 ; AUDIO_PACKSIZE + LPCM_HEADER_LEN + 3 bytes of pes header ext
  *(p_offset-5) = (uint8_t)(l >> 8);
  // add 3 bytes for pes header extension
  *(p_offset-4) = (uint8_t)((l+3) & 0x00FF);
  // pes->header_flags:16
  *(p_offset-3) = 0x80 | pesFlags >> 8;
  *(p_offset-2) = pesFlags & 0x00FF;
  // pes->header_length
  // *(p_offset-1) = 0x00;

  p_offset -= PES_HEADER_LEN;
  if (packet)
    *packet = p_offset;

  return PES_MAX_PACKSIZE - (p_offset - buffer);
}

// --- cDataMangler ------------------------------------------------------------

cDataMangler::cDataMangler() {
}

cDataMangler::~cDataMangler() {
}

bool cDataMangler::Swap16(uint8_t data[], uint32_t length) {
  uint32_t i = 0;

  if (NULL != data)
    for (i = 0; i < length; i+=2) {
        *((uint16_t *)(data+i)) = ((uint16_t) (((uint16_t) *((uint16_t *)(data+i)) & (uint16_t) 0x00ffU) << 8) |
                                            (((uint16_t) *((uint16_t *)(data+i)) & (uint16_t) 0xff00U) >> 8) );
    }

  return NULL != data;
}

bool cDataMangler::Conv16_sign(uint8_t data[], uint32_t length, bool bigendian) {
  uint32_t i = 0;

  if (NULL != data)
    for (i = (bigendian ? 0 : 1); i < length; i+=2) {
      data[i] ^= 0x80;
    }

  return NULL != data;
}
