/*
 * See the README file for copyright information and how to reach the author.
 *
 */

//#include <transform.h>

#include "common.h"

#define PES_BUFFER_SIZE 2*1024

cRMReader::cRMReader(cRMDevice* _parent)
{
    parent=_parent;
    active=true;

    video_counter=0;
    audio_counter=0;
    text_counter=0;
    pes_offset=0;
    pes_stream_id=0;
    pes_length=0;
    pes_tmp=0;

    Start();
}

cRMReader::~cRMReader()
{
    active=false;
    Cancel(2);
}

void cRMReader::Action()
{
    cPvrUsb2Plugin::LogDebug("Start reader");
    while(active && Running()) {
        if(!GetPESPacket()) {
            active=false;
        }
        if(active && parent->IsReady2Close()) {
            active=false;
        }
    }
    if(parent) {
        parent->CloseVideoDevice();
    }
    parent=NULL;
    cPvrUsb2Plugin::LogDebug("Stopped reader:");
}

bool cRMReader::GetPESPacket()
{
    if(!parent->OpenVideoDevice()) {
        return false;
    }

    unsigned char pesBuffer[PES_BUFFER_SIZE];
    int bytesRead=read(parent->VideoDeviceFD(), pesBuffer, PES_BUFFER_SIZE);
    if(!active) {
        return false;
    }
    if(bytesRead<0) {
        cPvrUsb2Plugin::LogError("Unable to read from video device: '%s'.",
                parent->VideoDeviceName());
        return false;
    }
    if(bytesRead>0) {
        ParseProgramStream(pesBuffer, bytesRead);
        return true;
    }
    return false;
}

void cRMReader::PesToTs(uint8_t* Data, uint32_t Length)
{
    uint8_t stream_id;
    bool first=true;
    uint32_t i;
    uint32_t rest;

    stream_id=Data[3];
    if(stream_id>=0xE0 && stream_id<=0xEF) {
        for (i=0; i<Length/184; i++) {
            ts_buffer[0]=0x47;
            ts_buffer[1]=first ? 0x40 : 0x00;
            ts_buffer[1]|=kVideoPid >> 8;
            ts_buffer[2]=kVideoPid & 0xFF;
            ts_buffer[3]=0x10 | video_counter;
            memcpy(ts_buffer+4, Data+i*184, 184);
            parent->PutTSData(ts_buffer, 188);
            video_counter=(video_counter+1) & 15;
            first=false;
        }
        rest=Length%184;
        if(rest>0) {
            ts_buffer[0]=0x47;
            ts_buffer[1]=first ? 0x40 : 0x00;
            ts_buffer[1]|=kVideoPid >> 8;
            ts_buffer[2]=kVideoPid & 0xFF;
            ts_buffer[3]=0x30 | video_counter;
            ts_buffer[4]=183-rest;
            if(ts_buffer[4]>0) {
                ts_buffer[5]=0x00;
                memset(ts_buffer+6, 0xFF, ts_buffer[4]-1);
            }
            memcpy(ts_buffer+5+ts_buffer[4], Data+i*184, rest);
            parent->PutTSData(ts_buffer, 188);
            video_counter=(video_counter+1) & 15;
            first=false;
        }
    } else if(stream_id>=0xC0 && stream_id<=0xDF) {
        for (i=0; i<Length/184; i++) {
            ts_buffer[0]=0x47;
            ts_buffer[1]=first ? 0x40 : 0x00;
            ts_buffer[1]|=kAudioPid >> 8;
            ts_buffer[2]=kAudioPid & 0xFF;
            ts_buffer[3]=0x10 | audio_counter;
            memcpy(ts_buffer+4, Data+i*184, 184);
            parent->PutTSData(ts_buffer, 188);
            audio_counter=(audio_counter+1) & 15;
            first=false;
        }
        rest=Length%184;
        if(rest>0) {
            ts_buffer[0]=0x47;
            ts_buffer[1]=first ? 0x40 : 0x00;
            ts_buffer[1]|=kAudioPid >> 8;
            ts_buffer[2]=kAudioPid & 0xFF;
            ts_buffer[3]=0x30 | audio_counter;
            ts_buffer[4]=183-rest;
            if(ts_buffer[4]>0) {
                ts_buffer[5]=0x00;
                memset(ts_buffer+6, 0xFF, ts_buffer[4]-1);
            }
            memcpy(ts_buffer+5+ts_buffer[4], Data+i*184, rest);
            parent->PutTSData(ts_buffer, 188);
            audio_counter=(audio_counter+1) & 15;
            first=false;
        }
    /*
    } else if(stream_id==0xBD) {
        uint8_t* payload_data;
        uint16_t payload_length;
        uint32_t pos=0;
        uint32_t ts_pos=0;

        memset(ts_buffer, 0, 188);
        ts_buffer[0]=0x47;
        ts_buffer[1]=kTeletextPid >> 8;
        ts_buffer[2]=kTeletextPid;
        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;
                    parent->PutTSData(ts_buffer, 188);
                    text_counter=(text_counter+1) & 15;
                    memset(ts_buffer, 0, 188);
                    ts_buffer[0]=0x47;
                    ts_buffer[1]=kTeletextPid >> 8;
                    ts_buffer[2]=kTeletextPid;
                    ts_buffer[3]=0x10 | text_counter;
                }
            }
            pos+=43;
        }
        if(ts_pos>0) {
            parent->PutTSData(ts_buffer, 188);
            text_counter=(text_counter+1) & 15;
        }
        */
    }
}

void cRMReader::ParseProgramStream(uint8_t* Data, uint32_t Length)
{
    uint32_t pos;

    pos=0;
    while (pos<Length) {
        if(pes_offset==0 || pes_offset==1) {
            if(Data[pos]==0x00) {
                pes_offset++;
            } else {
                pes_offset=0;
            }
            pos++;
        } else if(pes_offset==2) {
            if(Data[pos]==0x01) {
                pes_offset++;
                pos++;
            } else {
                pes_offset=0;
            }
        } else if(pes_offset==3) {
            pes_stream_id=Data[pos];
            pes_offset++;
            pos++;
        } else {
            if(pes_stream_id==0xBA) {
                if(pes_offset<13) {
                    pes_offset++;
                    pos++;
                } else if(pes_offset==13) {
                    pes_tmp=Data[pos] & 7;
                    pes_offset++;
                    pos++;
                } else {
                    if(pes_tmp>0) {
                        pes_tmp--;
                        pos++;
                    } else {
                        pes_offset=0;
                    }
                }
            } else if(pes_stream_id==0xBB) {
                if(pes_offset<12) {
                    pes_offset++;
                    pos++;
                } else {
                    if(pes_tmp>0) {
                        pes_tmp--;
                        pos++;
                    } else if(Data[pos] & 0x80) {
                        pes_tmp=3;
                    } else {
                        pes_offset=0;
                    }
                }
            } else if(pes_stream_id>=0xE0 && pes_stream_id<=0xEF ||
                    pes_stream_id>=0xC0 && pes_stream_id<=0xDF ||
                    pes_stream_id==0xBE ||
                    pes_stream_id==0xBD)
            {
                if(pes_offset==4) {
                    pes_length=Data[pos] << 8;
                    pes_offset++;
                    pos++;
                } else if(pes_offset==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++;
                } else {
                    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;
                    }
                }
            } else {
                printf("id=%02X\n", pes_stream_id);
                usleep(100000);
            }
        }
    }
}

