/*
 * radiotransfer.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

#include "radiotransfer.h"
#include <vdr/remote.h>
#include "i18n.h"

#define TRANSFERBUFSIZE MEGABYTE(2)
#define POLLTIMEOUTS_BEFORE_DEVICECLEAR 6

//Radiotext
int RTP_ItemToggle = 1;
bool RT_MsgShow = false, RT_PlusShow = false, RT_ReDispl = false, RTP_TToggle = false;

// --- cRadioTransfer -------------------------------------------------------------

cRadioTransfer::cRadioTransfer(int VPid, const int *APids, const int *DPids, const int *SPids, const char *Image)
:cReceiver(0, -1, VPid, APids, DPids, SPids), cThread("radiotransfer")
{
  dsyslog("new RadioTransfer");
  ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "RadioTransfer");
  remux = new cRemux(VPid, APids, DPids, SPids);
#if VDRVERSNUM < 10329
  active = false;
#endif
  imagepath = Image;
  audio_type = ttNone;
  audio_id = 0;
}

cRadioTransfer::~cRadioTransfer()
{
  dsyslog("delete RadioTransfer");
  cReceiver::Detach();
  cPlayer::Detach();
  delete remux;
  delete ringBuffer;
}

void cRadioTransfer::Activate(bool On)
{
#if VDRVERSNUM < 10329
  if (On) {
     if (!active)
        Start();
     }
  else if (active) {
     active = false;
     Cancel(3);
     }
#else
  if (On) {
    Start();
    }
  else
    Cancel(3);
#endif
}

void cRadioTransfer::Receive(uchar *Data, int Length)
{
#if VDRVERSNUM < 10329
  if (IsAttached() && active) {
     int p = ringBuffer->Put(Data, Length);
     if (p != Length && active)
#else
  if (IsAttached() && Running()) {
     int p = ringBuffer->Put(Data, Length);
     if (p != Length && Running())
#endif
        ringBuffer->ReportOverflow(Length - p);
#if VDRVERSNUM < 10331
     else if (S_RtFunc >= 1)	//check Radiotext-TS
        RadiotextCheckTS(Data, Length);
#endif
     return;
     }
}

void cRadioTransfer::Action(void)
{
  int PollTimeouts = 0;
  active = true;
  ShowImage(imagepath);
  uchar *p = NULL;
  int Result = 0;
  static int delay = 0;
  RT_Info = 0;
  
  bool GotBufferReserve = false;
//  int RequiredBufferReserve = KILOBYTE(16);

#if VDRVERSNUM < 10329
  while (active) {
#else
  while (Running()) {
#endif
/*        if (!GotBufferReserve) {
           if (ringBuffer->Available() < RequiredBufferReserve) {
              cCondWait::SleepMs(20); // allow the buffer to collect some reserve
              PlayPes(NULL, 0);
              continue;
              }
           else
              GotBufferReserve = true;
	      DeviceClear();
           }
*/
  	int Count;
        uchar *b = ringBuffer->Get(Count);
        if (b) {
           if (ringBuffer->Available() > TRANSFERBUFSIZE * 9 / 10) {
              // If the buffer runs full, we have no chance of ever catching up
              // since the data comes in at the same rate as it goes out (it's "live").
              // So let's clear the buffer instead of suffering from permanent
              // overflows.
              dsyslog("clearing transfer buffer to avoid overflows");
              DeviceClear();
              ringBuffer->Clear();
              remux->Clear();
              PlayPes(NULL, 0);
              p = NULL;

              GotBufferReserve = false;
              continue;
              }
           Count = remux->Put(b, Count);
           if (Count)
              ringBuffer->Del(Count);
           }
        if (!p)
           p = remux->Get(Result);
        if (p) {
           cPoller Poller;
           if (DevicePoll(Poller, 100)) {
              PollTimeouts = 0;
              int w = PlayPes(p, Result);
#if VDRVERSNUM >= 10331
	      //check Radiotext-PES
	      if (S_RtFunc >= 1)
	        RadiotextCheckPES(p, Result);
#endif
              if (w > 0) {
                 p += w;
                 Result -= w;
                 remux->Del(w);
                 if (Result <= 0)
                    p = NULL;
                 }
              else if (w < 0 && FATALERRNO)
                 LOG_ERROR;
              }
           else {
              PollTimeouts++;
              if (PollTimeouts == POLLTIMEOUTS_BEFORE_DEVICECLEAR) {
                 dsyslog("clearing device because of consecutive poll timeouts");
                 DeviceClear();
                 ringBuffer->Clear();
                 remux->Clear();
                 PlayPes(NULL, 0);
                 p = NULL;

                 GotBufferReserve = false;
                 }
              }
	    }
	//Radiotext-Autodisplay
	if (S_RtFunc >= 1 && S_RtDispl == 2 && RT_ReDispl && RT_MsgShow && !Skins.IsOpen() && !cOsd::IsOpen()) {
	    if (delay++ > 20) {
	    	RT_ReDispl = false;
#if VDRVERSNUM < 10332
	    	cRemote::Put(RT_Userkey);
#else
		cRemote::CallPlugin("radio");
#endif
		}
	    }
        else
	    delay = 0;
	}
#if VDRVERSNUM < 10329
  active = false;
#endif
}

void cRadioTransfer::ShowImage (const char *file)
{
	uchar *buffer;
	int fd;
	struct stat st;
	struct video_still_picture sp;
	if ((fd = open (file, O_RDONLY)) >= 0)
	{
		fstat (fd, &st);
		sp.iFrame = (char *) malloc (st.st_size);
		if (sp.iFrame)
		{
			sp.size = st.st_size;
			if (read (fd, sp.iFrame, sp.size) > 0)
			{
				buffer = (uchar *) sp.iFrame;  
#ifdef USE_STILLPICTURE
				DeviceStillPicture(buffer, sp.size);
#else
				for (int i = 1; i <= 25; i++)
				{
					send_pes_packet (buffer, sp.size, i);
				}
#endif
			}
			free (sp.iFrame);
		}
		else
		{
		}
		close (fd);
	}
	else
	{
	}
}

void cRadioTransfer::send_pes_packet(unsigned char *data, int len, int timestamp)
{
#define PES_MAX_SIZE 2048
	int ptslen = timestamp ? 5 : 1;
	static unsigned char pes_header[PES_MAX_SIZE];
	pes_header[0] = pes_header[1] = 0;
	pes_header[2] = 1;
	pes_header[3] = 0xe0;
	while(len > 0)
	{
		int payload_size = len;
		if(6 + ptslen + payload_size > PES_MAX_SIZE)
			payload_size = PES_MAX_SIZE - (6 + ptslen);
		pes_header[4] = (ptslen + payload_size) >> 8;
		pes_header[5] = (ptslen + payload_size) & 255;
		if(ptslen == 5)
		{
			int x;
			x = (0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1;
			pes_header[8] = x;
			x = ((((timestamp >> 15) & 0x7fff) << 1) | 1);
			pes_header[7] = x >> 8;
			pes_header[8] = x & 255;
			x = ((((timestamp) & 0x7fff) < 1) | 1);
			pes_header[9] = x >> 8;
			pes_header[10] = x & 255;
		} else
		{
			pes_header[6] = 0x0f;
		}
		memcpy(&pes_header[6 + ptslen], data, payload_size);
		PlayPes(pes_header, 6 + ptslen + payload_size);
		len -= payload_size;
		data += payload_size;
		ptslen = 1;
	}
}

void cRadioTransfer::SetAudioTrack(eTrackType Type, const tTrackId *TrackId)
{
    if (S_Verbose >= 2)
	fprintf(stderr, "vdr-radio: SetAudioTrack Type/Id = %d/%d\n", Type, TrackId->id);

    audio_type = Type;
    audio_id = TrackId->id;
}

// check Radiotext-TS, outdated, for vdr < 1.3.31
void cRadioTransfer::RadiotextCheckTS(unsigned char *data, int len)
{
    const int mframel = 263;  // max. 255(MSG)+4(ADD/SQC/MFL)+2(CRC)+2(Start/Stop) of RDS-data
    static unsigned char mtext[mframel+1], lastframe[25];
    static bool rt_start = false, rt_bstuff = false;
    static int index;
    static int mec = 0;
    
    /* Checking TS-Frames isn't the best way, the RDS ancillary-data are at the end of a Audio-frame. */
    if (len != TS_SIZE) return;

    int offset = len - 1;
    int rdsl = 0, afdl = 0;
    // RDS DataSync = 0xfd @ audio-end
    if ((0xf0 & data[3]) == 0x30 && data[offset] == 0xfd)	// no encryption & adaptation, e.g. last TS-Frame
	rdsl = data[offset-1];
    else if (!(data[1] & 0x40)) {				// not PES/Payload-Start, e.g. first TS-Frame
	afdl = (data[3] & 0x20) ? data[4] + 1 : 0; 		// AdaptationField-Length
	for (int i = 3+afdl; i < TS_SIZE-4; i++) {
	    if (data[i] == 0xfd && data[i+1] == 0xff && (data[i+2] & 0xf0) && (data[i+3] & 0x04) && !(data[i+4] & 0x0f)) {
		offset = i;
		rdsl = data[offset-1];
		break;
		}
	    }
	}
    if (rdsl <= 0) {	// save rest of last frame
	for (int i = TS_SIZE-1, ii = 0; i > TS_SIZE-25; i--)
	    lastframe[ii++] = data[i];	
        return;
	}
    // print RawData with 0xfd
    if (S_Verbose >= 3) {
	printf("\n\nRawData(%d): ", len);
	for (int a=0; a<len; a++)
	    printf("%02x ", data[a]);
	printf("(End)\n\n");
	}

    for (int i = offset-2, val, ii = 0; i > offset-2-rdsl; i--) {		// <-- data reverse, from end to start
	if (i > 3+afdl)
	    val = data[i];
	else if (ii < 24) 
	    val = lastframe[ii++];
	else
	    return;

	if (val == 0xfe) {	// Start
	    index = -1;
	    rt_start = true;
	    rt_bstuff = false;
	    mec = 0;
	    if (S_Verbose >= 2)
		printf("\nRDS-Start: ");
	    }

	if (rt_start) {
	    if (S_Verbose >= 2)
		printf("%02x ", val);
	    // byte-stuffing reverse: 0xfd00->0xfd, 0xfd01->0xfe, 0xfd02->0xff
	    if (rt_bstuff) {
		switch (val) {
		    case 0x00: mtext[index] = 0xfd; break;
		    case 0x01: mtext[index] = 0xfe; break;
		    case 0x02: mtext[index] = 0xff; break;
		    default: mtext[++index] = val;	// should never be
		    }
		rt_bstuff = false;
		if (S_Verbose >= 2)
		    printf("(Bytestuffing -> %02x) ", mtext[index]);
		}
	    else
		mtext[++index] = val;
	    if (val == 0xfd && index > 0)	// stuffing found
		rt_bstuff = true;
	    // early check for PTY/RT(plus) = Typ '0x0a/0x46/0x07'
	    if (index == 5) {
		mec = val;
		if (mec != 0x0a && mec != 0x46 && mec != 0x07) {
    	    	    rt_start = false;
		    if (S_Verbose >= 2)
			printf("(RDS-MEC '%02x' not '0x0a/0x46/0x07' -> throw away)\n",val);
		    }
		}
	    if (index >= mframel) {	// max. rdslength, garbage ?
		if (S_Verbose >= 2)
		    printf("(RDS-Error: too long, garbage ?)\n");
		rt_start = false;
		}
	    }

	if (rt_start && val == 0xff) {	// End
	    if (S_Verbose >= 2)
		printf("(RDS-End)\n");
	    rt_start = false;
	    // ToDo: crc16-check
	    if (mec == 0x0a || mec == 0x46) 
    		RadiotextDecode(mtext, index);
	    else if (mec == 0x07) {	//PTY Program type
	        RT_PTY = mtext[8];
		}
    
	    }
	}
}

// check Radiotext-PES, if we have comlete Audioframes
void cRadioTransfer::RadiotextCheckPES(unsigned char *data, int len)
{
  const int mframel = 263;  // max. 255(MSG)+4(ADD/SQC/MFL)+2(CRC)+2(Start/Stop) of RDS-data
  static unsigned char mtext[mframel+1];
  static bool rt_start = false, rt_bstuff=false;
  static int index;
  static int mec = 0;

  int offset = 0;
  int aid = (audio_type >= ttDolbyFirst && audio_type <= ttDolbyLast) ? 0xc0 + (audio_type-ttDolbyFirst) : audio_id;
  while (data[3] == aid) {	// check active Track && only Mpeg1-Layer2 Audio have RDS

    int pesl = (offset+5 < len) ? (data[offset+4] << 8) + data[offset+5] + 6 : -1;
    if (pesl <= 0 || offset+pesl > len)
	return;

    offset += pesl;
    int rdsl = data[offset-2];	// RDS DataFieldLength
    // RDS DataSync = 0xfd @ end
    if (data[offset-1] == 0xfd && rdsl > 0) {
	// print RawData with RDS-Info
	if (S_Verbose >= 3) {
    	    printf("\n\nPES-Data(%d/%d): ", pesl, len);
    	    for (int a=offset-pesl; a<offset; a++)
		printf("%02x ", data[a]);
	    printf("(End)\n\n");
	    }
    
	for (int i = offset-3, val; i > offset-3-rdsl; i--) {	// <-- data reverse, from end to start
    	    val = data[i];

	    if (val == 0xfe) {	// Start
		index = -1;
		rt_start = true;
		rt_bstuff = false;
		if (S_Verbose >= 2)
		    printf("\nRDS-Start: ");
		}

	    if (rt_start) {
		if (S_Verbose >= 2)
		    printf("%02x ", val);
		// byte-stuffing reverse: 0xfd00->0xfd, 0xfd01->0xfe, 0xfd02->0xff
		if (rt_bstuff) {
		    switch (val) {
			case 0x00: mtext[index] = 0xfd; break;
			case 0x01: mtext[index] = 0xfe; break;
			case 0x02: mtext[index] = 0xff; break;
			default: mtext[++index] = val;	// should never be
			}
		    rt_bstuff = false;
		    if (S_Verbose >= 2)
			printf("(Bytestuffing -> %02x) ", mtext[index]);
		    }
		else
		    mtext[++index] = val;
		if (val == 0xfd && index > 0)	// stuffing found
		    rt_bstuff = true;
		// early check for PTY/RT(plus) = Typ '0x0a/0x46/0x07'
		if (index == 5) {
		    mec = val;
		    if (mec != 0x0a && mec != 0x46 && mec != 0x07) {
    	    		rt_start = false;
			if (S_Verbose >= 2)
			    printf("(RDS-MEC '%02x' not '0x0a/0x46/0x07' -> throw away)\n",val);
			}
		    }
		if (index >= mframel) {		// max. rdslength, garbage ?
		    if (S_Verbose >= 2)
			printf("(RDS-Error: too long, garbage ?)\n");
		    rt_start = false;
		    }
		}

	    if (rt_start && val == 0xff) {	// End
		if (S_Verbose >= 2)
		    printf("(RDS-End)\n");
		rt_start = false;
		// ToDo: crc16-check
		if (mec == 0x0a || mec == 0x46) 
    		    RadiotextDecode(mtext, index);
		else if (mec == 0x07) {		// PTY Program type
	    	    RT_PTY = mtext[8];
		    }
		}
	    }
	}
    }
}

// RDS-Chartranslation: 0x80..0xff
unsigned char rds_addchar[128] = {
  0xe1, 0xe0, 0xe9, 0xe8, 0xed, 0xec, 0xf3, 0xf2, 
  0xfa, 0xf9, 0xd1, 0xc7, 0x8c, 0xdf, 0x8e, 0x8f, 
  0xe2, 0xe4, 0xea, 0xeb, 0xee, 0xef, 0xf4, 0xf6, 
  0xfb, 0xfc, 0xf1, 0xe7, 0x9c, 0x9d, 0x9e, 0x9f, 
  0xaa, 0xa1, 0xa9, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 
  0xa8, 0xa9, 0xa3, 0xab, 0xac, 0xad, 0xae, 0xaf, 
  0xba, 0xb9, 0xb2, 0xb3, 0xb1, 0xa1, 0xb6, 0xb7, 
  0xb5, 0xbf, 0xf7, 0xb0, 0xbc, 0xbd, 0xbe, 0xa7, 
  0xc1, 0xc0, 0xc9, 0xc8, 0xcd, 0xcc, 0xd3, 0xd2, 
  0xda, 0xd9, 0xca, 0xcb, 0xcc, 0xcd, 0xd0, 0xcf, 
  0xc2, 0xc4, 0xca, 0xcb, 0xce, 0xcf, 0xd4, 0xd6, 
  0xdb, 0xdc, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 
  0xc3, 0xc5, 0xc6, 0xe3, 0xe4, 0xdd, 0xd5, 0xd8, 
  0xde, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xf0,
  0xe3, 0xe5, 0xe6, 0xf3, 0xf4, 0xfd, 0xf5, 0xf8, 
  0xfe, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};

// Radiotext-Decode (RDS-UECP)
void cRadioTransfer::RadiotextDecode(unsigned char *mtext, int len)
{
    static bool rtp_itoggle = false;
    static int rtp_counter, rtp_idiffs = 0;
    static cTimeMs rtp_itime;

    // byte 1+2 = ADD (10bit SiteAdress + 6bit EncoderAdress)
    // byte 3   = SQC (Sequence Counter 0x00 = not used)
    int leninfo = mtext[4];	// byte 4 = MFL (Message Field Length)
    if (len == leninfo+7) {	// check complete length

	// byte 5 = MEC (Message Element Code, 0x0a for RT, 0x46 for RTplus)
	if (mtext[5] == 0x0a) {
	    // byte 6+7 = DSN+PSN (DataSetNumber+ProgramServiceNumber, 
	    //		       	   ignore here, always 0x00 ?)
	    // byte 8   = MEL (MessageElementLength, max. 64+1 byte @ RT)
	    if (mtext[8] == 0 || mtext[8] > RT_MEL || mtext[8] > leninfo-4) {
		if (S_Verbose >= 2)
		    printf("RT-Error: Length = 0 or not correct !");
		return;
		}
	    // byte 9 = RT-Status bitcodet (0=AB-flagcontrol, 1-4=Transmission-Number, 5+6=Buffer-Config,
	    //				    ingnored, always 0x01 ?)
	    char temptext[RT_MEL];
	    memset(temptext, 0x20, RT_MEL-1);
	    for (int i = 1, ii = 0; i < mtext[8]; i++) {
	        if (mtext[9+i] <= 0xfe)
		    // additional rds-character, see RBDS-Standard, Annex E
	            temptext[ii++] = (mtext[9+i] >= 0x80) ? rds_addchar[mtext[9+i]-0x80] : mtext[9+i];
		}
	    // check repeats
	    bool repeat = false;
	    for (int ind = 0; ind < S_RtOsdRows; ind++) {
	    	if (memcmp(RT_Text[ind], temptext, RT_MEL-1) == 0) {
	    	    repeat = true;
		    if (S_Verbose >= 1)
		    	printf("RText-Rep[%d]: %s\n", ind, RT_Text[ind]);
		    }
		}
	    if (!repeat) {
	    	memcpy(RT_Text[RT_Index], temptext, RT_MEL-1);
	    	if (S_Verbose >= 1)
		    printf("Radiotext[%d]: %s\n", RT_Index, RT_Text[RT_Index]);
	    	RT_Index +=1; if (RT_Index >= S_RtOsdRows) RT_Index = 0;
		}
	    RT_MsgShow = RTP_TToggle = true;
	    if (!RT_Info) RT_Info = 1;
	    }

	else if (mtext[5] == 0x46 && S_RtFunc >= 2 && RTP_TToggle) {	// RTplus tags
	    RTP_TToggle = false;
	    if (mtext[6] > leninfo-2 || mtext[6] != 8) { // byte 6 = MEL, only 8 byte for 2 tags
		if (S_Verbose >= 2)
		    printf("RTp-Error: Length not correct !");
		return;
		}
	    uint rtp_typ[2], rtp_start[2], rtp_len[2];
	    // byte 7+8 = ApplicationID, always 0x4bd7
	    // byte 9   = Applicationgroup Typecode / PTY ?
	    // bit 10#4 = Item Togglebit
	    // Tag1: bit 10#3..11#6 = Contenttype, 11#5..11#0 = Startmarker, 12#7..12#2 = Length
	    rtp_typ[0]   = (0x3c & mtext[10]<<2) | mtext[11]>>6;
	    rtp_start[0] = 0x3f & mtext[11];
	    rtp_len[0]   = 0x3f & mtext[12]>>2;
	    // Tag2: bit 12#1..13#4 = Contenttype, 13#3..14#6 = Startmarker, 14#5..14#0 = Length
	    rtp_typ[1]   = (0x30 & mtext[12]<<4) | mtext[13]>>4;
	    rtp_start[1] = (0x3c & mtext[13]<<2) | mtext[14]>>6;
	    rtp_len[1]   = 0x3f & mtext[14];
	    if (S_Verbose >= 2)
    	        printf("RTplus (Typ/Start/Len):  tag#1 = %d/%d/%d,  tag#2 = %d/%d/%d\n", 
	               rtp_typ[0], rtp_start[0], rtp_len[0], rtp_typ[1], rtp_start[1], rtp_len[1]);
	    
	    // save info, just Artist/Title
	    int ind = (RT_Index == 0) ? S_RtOsdRows - 1 : RT_Index - 1;
	    for (int i = 0; i < 2; i++) {
		if (rtp_typ[i] == 0 && rtp_len[i] != 0) {
		    RT_Info = 2;
		    if ((mtext[10] & 0x10) != RTP_ItemToggle) {
			memset(RTP_Title, 0x20, RT_MEL-1);
			if (rtp_start[i]+rtp_len[i] < RT_MEL)
			    memmove(RTP_Title, RT_Text[ind]+rtp_start[i]-1, rtp_len[i]);
			else { // error start+length > RT-MEL
			    sprintf(RTP_Title, "---");
			    if (S_Verbose >= 1)
    		    		printf("  RTp-Error (Title tag#%d = Typ/Start/Len): %d/%d/%d (Start+Length > 'RT-MEL' !)\n",
		            	       i+1, rtp_typ[i], rtp_start[i], rtp_len[i]);
			    }
			if (RT_PlusShow && rtp_itime.Elapsed() > 1000)
			    rtp_idiffs = (int) rtp_itime.Elapsed()/1000;
		        time_t t = time(NULL); RTP_TitStart = localtime(&t);
			rtp_itime.Set(0);
			RT_PlusShow = RT_MsgShow = rtp_itoggle = true;
			}
			rtp_counter = 0;
		    }
		else if (rtp_typ[i] == 3 && rtp_len[i] != 0) {
		    RT_Info = 2;
		    if ((mtext[10] & 0x10) != RTP_ItemToggle) {
			memset(RTP_Artist, 0x20, RT_MEL-1);
			if (rtp_start[i]+rtp_len[i] < RT_MEL)
			    memmove(RTP_Artist, RT_Text[ind]+rtp_start[i]-1, rtp_len[i]);
			else { // error start+length > RT-MEL
			    sprintf(RTP_Artist, "---");
			    if (S_Verbose >= 1)
    		    	        printf("  RTp-Error (Artist tag#%d = Typ/Start/Len): %d/%d/%d (Start+Length > 'RT-MEL' !)\n",
		                       i+1, rtp_typ[i], rtp_start[i], rtp_len[i]);
			    }
			if (RT_PlusShow && rtp_itime.Elapsed() > 1000)
			    rtp_idiffs = (int) rtp_itime.Elapsed()/1000;
		        time_t t = time(NULL); RTP_TitStart = localtime(&t);
			rtp_itime.Set(0);
			RT_PlusShow = RT_MsgShow = rtp_itoggle = true;
			}
    		    rtp_counter = 0;
		    }
		else {	// no Title-endmechanism, what to do ?
		    /* first we try tag-counting/repeating &  min-timer=180s,
		       max-timer (240s) may be additonal way ? */
		    rtp_counter += 1;
		    if (rtp_counter >= 3 && rtp_itime.Elapsed() > 180000) {
		        rtp_counter = 0;
		        sprintf(RTP_Title, "---");
		        sprintf(RTP_Artist, "---");
			if (RT_PlusShow) {
			    RT_PlusShow = false;
			    rtp_itoggle = true;
			    rtp_idiffs = (int) rtp_itime.Elapsed()/1000;
			    time_t t = time(NULL); RTP_TitStart = localtime(&t);
			    }
			RT_MsgShow = true;
			}
		    }
		}

	    if (rtp_itoggle) {
		if (S_Verbose >= 1) {
		    if (rtp_idiffs > 0)
			printf("  StartTime : %02d:%02d:%02d  (last Title elapsed = %d s)\n",
			       RTP_TitStart->tm_hour, RTP_TitStart->tm_min, RTP_TitStart->tm_sec, rtp_idiffs);
		    else
			printf("  StartTime : %02d:%02d:%02d\n", RTP_TitStart->tm_hour, RTP_TitStart->tm_min, RTP_TitStart->tm_sec);
	    	    printf("  RTp-Title : %s\n  RTp-Artist: %s\n", RTP_Title, RTP_Artist);
		    }
	        RTP_ItemToggle = mtext[10] & 0x10;
		rtp_itoggle = false;
		rtp_idiffs = 0;
		}
	    }
	}		

    else {
	if (S_Verbose >= 2)
	    printf("RDS-Error: Length not correct !\n");
	}
}

// --- cRadioTransferControl ------------------------------------------------------

cDevice *cRadioTransferControl::receiverDevice = NULL;

cRadioTransferControl::cRadioTransferControl(cDevice *ReceiverDevice, int VPid, const int *APids, const int *DPids, const int *SPids, const char *Image)
:cControl(transfer = new cRadioTransfer(VPid, APids, DPids, SPids, Image), true)
{
  cDevice::PrimaryDevice()->AttachPlayer(transfer);
  ReceiverDevice->AttachReceiver(transfer);
  receiverDevice = ReceiverDevice;

  //Radiotext init
  if (S_RtFunc >= 1) {
    RT_MsgShow = RT_PlusShow = RTP_TToggle = false;
    RT_Info = RT_Index = RT_PTY = 0;
    RTP_ItemToggle = 1;
    for (int i = 0; i < 5; i++)
	memset(RT_Text[i], 0x20, RT_MEL-1);
    sprintf(RTP_Title, "---");
    sprintf(RTP_Artist, "---");
    time_t t = time(NULL); RTP_TitStart = localtime(&t);
    }
}

cRadioTransferControl::~cRadioTransferControl()
{
  receiverDevice = NULL;
  delete transfer;

  // Radiotext
  RT_Info = -1;
}


// --- cRadioTextOsd ------------------------------------------------------

cRadioTextOsd::cRadioTextOsd()
{
    osd = NULL;
    chosd = NULL;
    counter = 0;
}

cRadioTextOsd::~cRadioTextOsd()
{
    if (osd != NULL) 
	delete osd;
    if (chosd != NULL)
        delete chosd; 

    RT_OsdOpen = false;
    RT_ReDispl = !RT_OsdTO;
    cRemote::Put(LastKey);
}

void cRadioTextOsd::Show(void)
{
    LastKey = kNone;
    RT_ReDispl = RT_OsdTO = false;
    osdtimer.Set();

    ftitel = cFont::GetFont(fontOsd);
    ftext = cFont::GetFont(fontSml);
    fheight = ftext->Height() + 4;
    int rowoffset = (S_RtOsdTitle == 1) ? 1 : 0;
    bheight = (S_RtOsdTags >=1 ) ? fheight * (S_RtOsdRows+rowoffset+2) : fheight * (S_RtOsdRows+rowoffset);
    (S_RtOsdTitle == 1) ? bheight += 20 : bheight += 12;
    channel = Channels.GetByNumber(cDevice::CurrentChannel());

    // Channelswitch-Autostart
    if (S_RtDispl == 3) {
	S_RtDispl = 2;
	autostart = true;
	chosd = Skins.Current()->DisplayChannel(Setup.ShowInfoOnChSwitch);
	chosd->SetChannel(channel, 0);
	if (Setup.ShowInfoOnChSwitch) {
	    cSchedulesLock schedLock;
	    const cSchedules *scheds = cSchedules::Schedules(schedLock);
	    if (scheds != NULL) {
		const cSchedule *sched = scheds->GetSchedule(channel->GetChannelID());
		if (sched != NULL) {
		    const cEvent *present = sched->GetPresentEvent(true);
		    const cEvent *follow  = sched->GetFollowingEvent(true);
		    chosd->SetEvents(present, follow);
		    }
		}
	    }
	char infotext[80];
	if (S_RtOsdTO > 0)
	    sprintf(infotext, "%s, %d %s", tr("RDS-Text AutoOSD active"), S_RtOsdTO, tr("min. timeout"));
	else
	    sprintf(infotext, "%s, %s", tr("RDS-Text AutoOSD active"), tr("no timeout"));
	chosd->SetMessage(mtInfo, infotext);
	chosd->Flush();
	return;
	}

    if (S_RtDispl >= 1)
	ShowText();
}

void cRadioTextOsd::Hide(void)
{
    if (osd != NULL) {
	delete osd;
	osd = NULL;
	}
    if (chosd != NULL) {
        delete chosd; 
	chosd = NULL;
	}
}

const char* ptynr2string(int nr)
{
    switch (nr) {
        // Source: http://www.ebu.ch/trev_255-beale.pdf
        case  0: return tr("unknown program type");
	case  1: return tr("News"); 
	case  2: return tr("Current affairs"); 
	case  3: return tr("Information"); 
	case  4: return tr("Sport"); 
	case  5: return tr("Education"); 
	case  6: return tr("Drama"); 
	case  7: return tr("Culture"); 
	case  8: return tr("Science"); 
	case  9: return tr("Varied"); 
	case 10: return tr("Pop music"); 
	case 11: return tr("Rock music"); 
	case 12: return tr("M.O.R. music"); 
	case 13: return tr("Light classical"); 
	case 14: return tr("Serious classical"); 
	case 15: return tr("Other music"); 
	// 16-30 "Spares"
	case 31: return tr("Alarm");
	default: return "?";
	}
}

void cRadioTextOsd::ShowText(void)
{
  char stext[3][100];
  int yoffset = 8, ii = 0;

   if (!osd && !Skins.IsOpen() && !cOsd::IsOpen()) {
	if (S_RtOsdPos == 1)
	    osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop+Setup.OSDHeight-bheight);
   	else
	    osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop);
	tArea Area = {0, 0, Setup.OSDWidth-1, bheight-1, 4};
	osd->SetAreas(&Area, 1);
	}

   if (osd) {
	osd->DrawRectangle(0, 0, Setup.OSDWidth-1, bheight-1, (0x00FFFFFF | S_RtBgTra<<24) & rt_color[S_RtBgCol]);
	if (S_RtOsdTitle == 1) {
	    sprintf(stext[0], "%s - %s (%s)%s", 
				channel->Name(), 
				tr("Radiotext"), 
				ptynr2string(RT_PTY),
				RT_MsgShow ? ":" : tr("  [waiting ...]")
	    );
    	    osd->DrawText(3, 5, stext[0], rt_color[S_RtFgCol], clrTransparent, ftitel, Setup.OSDWidth-4, ftitel->Height());
	    yoffset = 17;
	    ii = 1;
	    }
	if (RT_MsgShow) {
    	    // RT-Text roundloop
	    int ind = (RT_Index == 0) ? S_RtOsdRows - 1 : RT_Index - 1;
	    if (S_RtOsdLoop == 1) {	// latest bottom
	        for (int i = ind+1; i < S_RtOsdRows; i++)
		    osd->DrawText(3, yoffset+fheight*(ii++), RT_Text[i], rt_color[S_RtFgCol], clrTransparent, ftext, Setup.OSDWidth-4, ftext->Height());
    		for (int i = 0; i <= ind; i++)
		    osd->DrawText(3, yoffset+fheight*(ii++), RT_Text[i], rt_color[S_RtFgCol], clrTransparent, ftext, Setup.OSDWidth-4, ftext->Height());
		}
	    else {			// latest top
	        for (int i = ind; i >= 0; i--)
		    osd->DrawText(3, yoffset+fheight*(ii++), RT_Text[i], rt_color[S_RtFgCol], clrTransparent, ftext, Setup.OSDWidth-4, ftext->Height());
    		for (int i = S_RtOsdRows-1; i > ind; i--)
   		    osd->DrawText(3, yoffset+fheight*(ii++), RT_Text[i], rt_color[S_RtFgCol], clrTransparent, ftext, Setup.OSDWidth-4, ftext->Height());
		}
	    // + RT-Plus = 2 rows
	    if ((S_RtOsdTags == 1 && RT_PlusShow) || S_RtOsdTags >= 2) {
		sprintf(stext[1], "> %s  %s", tr("Title  :"), RTP_Title);
   		osd->DrawText(3, 6+yoffset+fheight*(ii++), stext[1], rt_color[S_RtFgCol], clrTransparent, ftext, Setup.OSDWidth-4, ftext->Height());
   		//osd->DrawText(120, 6+yoffset+fheight*(ii++), RTP_Title, rt_color[S_RtFgCol], clrTransparent, ftext, Setup.OSDWidth-4, ftext->Height());
		sprintf(stext[2], "> %s  %s", tr("Artist :"), RTP_Artist);
   		osd->DrawText(3, 3+yoffset+fheight*(ii++), stext[2], rt_color[S_RtFgCol], clrTransparent, ftext, Setup.OSDWidth-4, ftext->Height());
   		//osd->DrawText(120, 3+yoffset+fheight*(ii++), RTP_Artist, rt_color[S_RtFgCol], clrTransparent, ftext, Setup.OSDWidth-4, ftext->Height());
		}
	    }
	osd->Flush();
	RT_OsdOpen = true;
	}

    RT_MsgShow = false;    
}

eOSState cRadioTextOsd::ProcessKey(eKeys Key)
{
  //fprintf(stderr, "RT-OSDKey, RT-OSDKeyState: %d, %d\n", Key, cOsdObject::ProcessKey(Key));
  // Channelswitch-Autostart ?
  if (autostart && Key == kNone) {
    if (++counter > Setup.ChannelInfoTime) {
	autostart = false;
	if (chosd != NULL) {
	    delete chosd; 
	    chosd = NULL;
	    }
	}
    return osContinue;
    }

  if (RT_OsdTO || RT_Info < 0) {
    Hide();
    return osEnd;
    }

  if (Key != kNone) {
    Hide();
    LastKey = (Key == kChanUp || Key == kChanDn) ? kNone : Key;
    return osEnd;
    }
  else if (S_RtOsdTO > 0 && osdtimer.Elapsed()/1000/60 >= (uint)S_RtOsdTO) {
    Hide();
    RT_OsdTO = true;
    return osEnd;
    }
  else if (RT_MsgShow)
    ShowText();
  
  return osContinue;
}

//--------------- End -----------------------------------------------------------------
