#include "common.h"

#define BUFFSIZE_INPUT 4096 * 32

const short kVideoPid    = 301;
const short kAudioPid    = 300;
const short kTeletextPid = 305;

const unsigned char kInvTab[256] =
{
  0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
  0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
  0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
  0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
  0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
  0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
  0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
  0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
  0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
  0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
  0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
  0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
  0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
  0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
  0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
  0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
  0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
  0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
  0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
  0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
  0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
  0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
  0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
  0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
  0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
  0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
  0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
  0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
  0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
  0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
  0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
  0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};

cPvrReadThread::cPvrReadThread(int VideoDevice, cRingBufferLinear * TsBuffer, \
                               cMutex * Mutex, cPvrDevice* _parent)
: active(false),
  video_fd(VideoDevice),
  tsBuffer(TsBuffer),
  mutex(Mutex),
  video_counter(0),
  audio_counter(0),
  text_counter(0),
  pes_stream_id(0),
  pes_offset(0),
  pes_length(0),
  pes_tmp(0)
{ log(3,"cPvrReadThread");
  parent=_parent;
  Start();
}

cPvrReadThread::~cPvrReadThread(void) {
  log(4,"~cPvrReadThread");
  active = false;
  Cancel(2);
}

int cPvrReadThread::PutData(const unsigned char * Data, int Count) {
  cMutexLock mutexLock(mutex);
  if (!tsBuffer) {
    log(0, "cPvrReadThread::PutData() Unable to put data into RingBuffer.");
    return 0;
    }
  if (tsBuffer->Free() < Count)
    return 0;
  return tsBuffer->Put(Data, Count);
}

void cPvrReadThread::PesToTs(uint8_t * Data, uint32_t Length) {
  uint8_t stream_id;
  bool first = true;
  uint32_t i;
  const short PayloadSize = TS_SIZE - 4;    
  uint32_t Payload_Count  = Length / PayloadSize;
  uint32_t Payload_Rest   = Length % PayloadSize;
  stream_id = Data[3];
  switch (stream_id) {
    case 0xE0 ... 0xEF:
      for (i = 0; i < Payload_Count; i++) {
        ts_buffer[0] = TS_SYNC_BYTE;
        ts_buffer[1] = (first ? 0x40 : 0x00) || (kVideoPid >> 8);
        ts_buffer[2] = kVideoPid & 0xFF;
        ts_buffer[3] = 0x10 | video_counter;
        memcpy(ts_buffer + 4, Data + i * PayloadSize, PayloadSize);
        PutData(ts_buffer, TS_SIZE);
        video_counter = (video_counter + 1) & 15; //uint8_t
        first = false;
        } // end: for (i = 0; i < Payload_Count; i++)
      if (Payload_Rest > 0) {
        ts_buffer[0] = TS_SYNC_BYTE;
        ts_buffer[1] = (first ? 0x40 : 0x00) || (kVideoPid >> 8);
        ts_buffer[2] = kVideoPid & 0xFF;
        ts_buffer[3] = 0x30 | video_counter;
        ts_buffer[4] = 183 - Payload_Rest;
        if (ts_buffer[4] > 0) {
          ts_buffer[5] = 0x00;
          memset(ts_buffer + 6, 0xFF, ts_buffer[4] - 1);
          } //end: if (ts_buffer[4] > 0)
        memcpy(ts_buffer + 5 + ts_buffer[4], Data + i * 184, Payload_Rest);
        PutData(ts_buffer, TS_SIZE);
        video_counter = (video_counter + 1) & 15;
        first = false;
        } // end: if (Payload_Rest > 0)
      break; // end: case 0xE0..0xEF:
    case 0xBD: {
      uint8_t * payload_data;
      uint16_t payload_length;
      uint32_t pos = 0;
      uint32_t ts_pos = 0;
      memset(ts_buffer, 0, TS_SIZE);
      ts_buffer[0] = TS_SYNC_BYTE;
      ts_buffer[1] = kTeletextPid >> 8;
      ts_buffer[2] = kTeletextPid & 0xFF;
      ts_buffer[3] = 0x10 | text_counter;

      payload_length = Length - 9 - Data[8];
      payload_data = Data + 9 + Data[8];
      if (memcmp(payload_data, "itv0", 4) == 0)
        pos = 12;
      else
        if (memcmp(payload_data, "ITV0", 4) == 0)
          pos = 4;
        else
          return;
      while (pos + 43 <= payload_length) {
        if ((payload_data[pos] & 0x0F) == 0x01)  {
          ts_buffer[4+ts_pos*46] = 0x02; // data_unit_id
          ts_buffer[5+ts_pos*46] = 0x2C; // data_unit_length
          ts_buffer[6+ts_pos*46] = 0x00; // field_parity, line_offset
          ts_buffer[7+ts_pos*46] = 0xE4; // framing_code
          for (int j = 0; j < 42; j++) {
            ts_buffer[8 + ts_pos * 46 + j] = kInvTab[payload_data[pos + 1 + j]];
            }
          ts_pos++;
          if (ts_pos == 4) {
            ts_pos = 0;
            PutData(ts_buffer, TS_SIZE);
            text_counter = (text_counter + 1) & 15;
            memset(ts_buffer, 0, TS_SIZE);
            ts_buffer[0] = TS_SYNC_BYTE;
            ts_buffer[1] = kTeletextPid >> 8;
            ts_buffer[2] = kTeletextPid & 0xFF;
            ts_buffer[3] = 0x10 | text_counter;
            }
          }
        pos += 43;
        }
      if (ts_pos > 0) {
        PutData(ts_buffer, TS_SIZE);
        text_counter = (text_counter + 1) & 15;
        }
      break; // end: case 0xBD:
      }
    case 0xC0 ... 0xDF: 
      for (i = 0; i < Payload_Count; i++) {
        ts_buffer[0] = TS_SYNC_BYTE;
        ts_buffer[1] = (first ? 0x40 : 0x00) || (kAudioPid >> 8);
        ts_buffer[2] = kAudioPid & 0xFF;
        ts_buffer[3] = 0x10 | audio_counter;
        memcpy(ts_buffer + 4, Data + i * PayloadSize, PayloadSize);
        PutData(ts_buffer, TS_SIZE);
        audio_counter = (audio_counter + 1) & 15;
        first = false;
        } // end: for (i = 0; i < Payload_Count; i++)
      if (Payload_Rest > 0) {
        ts_buffer[0] = TS_SYNC_BYTE;
        ts_buffer[1] = (first ? 0x40 : 0x00) || (kAudioPid >> 8);
        ts_buffer[2] = kAudioPid & 0xFF;
        ts_buffer[3] = 0x30 | audio_counter;
        ts_buffer[4] = 183 - Payload_Rest;
        if (ts_buffer[4] > 0) {
          ts_buffer[5] = 0x00;
          memset(ts_buffer + 6, 0xFF, ts_buffer[4] - 1);
          } //end: if (ts_buffer[4] > 0)
        memcpy(ts_buffer + 5 + ts_buffer[4], Data + i * PayloadSize, Payload_Rest);
        PutData(ts_buffer, TS_SIZE);
        audio_counter = (audio_counter + 1) & 15;
        first = false;
        } // end: if (Payload_Rest > 0)
      break; // end: case 0xC0..0xDF:
    } // end: switch (stream_id)
}

void cPvrReadThread::ParseProgramStream(uint8_t * Data, uint32_t Length) {
  uint32_t pos = 0;
  while (pos < Length) {
    switch(pes_offset)  {
      case 0:
      case 1:
        if (Data[pos] == 0x00)
          pes_offset++;
        else
          pes_offset = 0;
        pos++;
        break;
      case 2:
        if (Data[pos] == 0x01)  {
          pes_offset++;
          pos++;
          }
        else
          pes_offset = 0;
        break;
      case 3:
        pes_stream_id = Data[pos];
        pes_offset++;
        pos++;
        break;
      default:
        switch (pes_stream_id) {
          case 0xBA:
            switch (pes_offset) {
              case 4 ... 12:
                pes_offset++;
                pos++;
                break;
              case 13:
                pes_tmp = Data[pos] & 7;
                pes_offset++;
                pos++;
                break;
              default:
                if (pes_tmp > 0) {
                  pes_tmp--;
                  pos++;
                  }
                else
                  pes_offset = 0;
                break;              
              }
            break;
          case 0xBB:
            switch (pes_offset) {
              case 4 ... 11:
                pes_offset++;
                pos++;
                break;
              default:
                if (pes_tmp > 0) {
                  pes_tmp--;
                  pos++;
                  }
                else
                  if (Data[pos] & 0x80) {
                    pes_tmp = 3;
                    }
                  else {
                    pes_offset = 0;
                    }
                break; 
              } // end: switch (pes_offset) 
            break; // end: case 0xBB
          case 0xBD ... 0xEF:
            switch (pes_offset) {
              case 4:
                pes_length = Data[pos] << 8;
                pes_offset++;
                pos++;
                break;
              case 5:
                pes_length += Data[pos];
                pes_buffer[0] = 0x00;
                pes_buffer[1] = 0x00;
                pes_buffer[2] = 0x01;
                pes_buffer[3] = pes_stream_id;
                pes_buffer[4] = pes_length >> 8;
                pes_buffer[5] = pes_length & 0xFF;
                pes_length += 6;
                pes_offset++;
                pos++;
                break;
              default: {
                uint32_t rest = pes_length - pes_offset;
                if (pos + rest <= Length) {
                  memcpy(pes_buffer + pes_offset, Data + pos, rest);
                  pos += rest;
                  PesToTs(pes_buffer, pes_length);
                  pes_offset = 0;
                  }
                else {
                  memcpy(pes_buffer + pes_offset, Data + pos, Length - pos);
                  pes_offset += Length - pos;
                  pos += Length - pos;
                  }
                }
                break;
              }
            break;
          default:
            // unexpected PES Stream id, most probably garbage data.
            pes_offset = 0;
            return;
            break;
          } // end: switch (pes_stream_id)
        } // end: switch(pes_offset)
    }  // end: while (pos < Length)
}


void cPvrReadThread::Action(void) {
  unsigned char buffer[BUFFSIZE_INPUT];
  int r;
  active = true;
  log(3,"cPvrReadThread::Action(): Entering Action()"); 
  // A derived cThread class must check Running()
  // repeatedly to see whether it's time to stop.
  // see VDR/thread.h
  while (active && Running()) {
    r = read(video_fd, buffer, BUFFSIZE_INPUT);
    if (r == -1) {
      log(0, "cPvrReadThread::Action():error reading from video device, %d:%s",
        errno, strerror(errno));
      break;
      }
    if (r > 0)  {
      ParseProgramStream(buffer, r);
      }
    }
  log(3,"cPvrReadThread::Action(): leaving Action()"); 
}
