/***************************************************************************
 *   Copyright (C) 2004-2005 by Axel Katzur                                *
 *   software@katzur.de                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "premierecit.h"
#include "helpfunctions.h"
#include <iostream>
#include <sstream> 
#include <stdlib.h>
using namespace std;

std::string Number2String(unsigned long nNumber)
{
    std::ostringstream out;
	out << nNumber;
    return out.str();
};

std::string Number2String(unsigned int nNumber)
{
    std::ostringstream out;
	out << nNumber;
    return out.str();
};

std::string Number2String(int nNumber)
{
    std::ostringstream out;
	out << nNumber;
    return out.str();
};

std::string Number2String(time_t nNumber)
{
    std::ostringstream out;
	out << nNumber;
    return out.str();
};

// functiuon to copy unsigned char pointer to the destionation on std string....
std::string char2string(unsigned char* pchBuffer, unsigned int nLength)
{
	// get text
	char szBuffer[256];
	int nCounter;
	std::string strText;

	// prevent for to long strings (Buffer Overflow risk)
	if (nLength <= 255)
	{
		// copy loop
		for (nCounter = 0; nCounter < nLength; nCounter++)
		{
			szBuffer[nCounter] = pchBuffer[nCounter];
		}

		// end pointer, only for security
		szBuffer[nCounter] = '\0';
		
		// put in string
		strText.append(szBuffer, nCounter);

        // return string
		return strText;
	}

	// return no string, no success
	return NULL;
}

// function to replace string in string
std::string ReplaceString(const std::string& strSearchString, const std::string& strReplaceString, std::string strStringToReplace)
{
    std::string::size_type pos = strStringToReplace.find(strSearchString, 0);
    int nLengthSearch = strSearchString.length();

    while(std::string::npos != pos)
    {
            strStringToReplace.replace(pos, nLengthSearch, strReplaceString);
            pos = strStringToReplace.find(strSearchString, pos + nLengthSearch);
    }

    // return string
    return strStringToReplace;
}

// Premiere Content Information Table
CPremiereCit::CPremiereCit(unsigned char *pchBuffer, long nLength)
{
    unsigned int        nTableId;
    unsigned int        nSectionLength;
    
    // check for correct table id
    nTableId = getBits (pchBuffer, 0, 0, 8);
    if (0xa0 != nTableId)
    {
        // exit
        return;
    }

    // decode
    m_nSectionSyntaxIndicator = getBits (pchBuffer, 0, 8, 1);
    m_nPrivateIndicator = getBits (pchBuffer, 0, 9, 1);
    m_nReserved = getBits (pchBuffer, 0, 10, 2);
    m_nPrivateSectionLength = getBits (pchBuffer, 0, 12, 12);
 	m_nTableIdExtension = getBits (pchBuffer, 0, 24, 16);
 	m_nReserved2 = getBits (pchBuffer, 0, 40, 2);
 	m_nVersionNumber = getBits (pchBuffer, 0, 42, 5);
	m_nCurrentNextIndicator = getBits (pchBuffer, 0, 47, 1);
    m_nSectionNumber = getBits (pchBuffer, 0, 48, 8);
	m_nLastSectionNumber = getBits (pchBuffer, 0, 56, 8);

	pchBuffer += 8;
	nSectionLength = m_nPrivateSectionLength - 5;

    //
    // -- Premiere Content Information Table CIT
    // -- provided by  Peter.Pavlov (Premiere.de)
    // code from dvbsnoop
    // see http://dvbsnoop.sourceforge.net/ for more information about the best DVB Sniffer
    unsigned int nDescriptorSectionLength;

    m_nContentId  = getBits (pchBuffer, 0, 0, 32);
    m_nDuration  = getBits (pchBuffer, 0, 32,24);
    m_nReserved3  = getBits (pchBuffer, 0, 56, 4);
    nDescriptorSectionLength  = getBits (pchBuffer, 0, 60,12);

    pchBuffer += 9;
    nSectionLength -=9;

    // loop for descriptors (4 Bytes for crc)
    while (4 < nSectionLength)
    {
        int nDescriptorLength;

      	// Descriptor
      	nDescriptorLength = CitDescriptor(pchBuffer);
      	if (nSectionLength < nDescriptorLength)
            break;
      	pchBuffer += nDescriptorLength;
      	nSectionLength -= nDescriptorLength;
   }

    // CRC
   //outBit_Sx_NL (5,"CRC: ",		b, 0, 32);
   
   // Debug
//   cout << "Content Id: " << GetContentId() << endl;
//   cout << "Duration: " << GetDuration() << endl;
};


unsigned int CPremiereCit::CitDescriptor(unsigned char* pchBuffer)
{
	int nLength;
	int nId;

	nId		=  (int) pchBuffer[0];
	nLength	= ((int) pchBuffer[1]) + 2;

	// check for empty descriptor
	nLength = ((int) pchBuffer[1]) + 2;
	if (pchBuffer[1] == 0)
	{
		return nLength;
	}

	// check the correct descriptor (only CIT relevant)
	switch (pchBuffer[0])
	{
	case 0x4D:  
				{	
					CDescriptorShortEvent	DescriptorShortEvent;
					DescriptorShortEvent.DvbDescriptorShortEvent(pchBuffer); 
					m_vecDescriptorShortEvent.push_back(DescriptorShortEvent);
					break;
				}
	case 0x4E:  
				{	
					CDescriptorExtendedEvent	DescriptorExtendedEvent;
					DescriptorExtendedEvent.DvbDescriptorExtendedEvent(pchBuffer); 
					m_vecDescriptorExtendedEvent.push_back(DescriptorExtendedEvent);
					break;
				}
	case 0x54:
				{	
					CDescriptorContent	DescriptorContent;
					DescriptorContent.DvbDescriptorContent(pchBuffer); 
					m_vecDescriptorContent.push_back(DescriptorContent);
					break;
				}
	case 0xF2:
				{	
                    CDescriptorContentTransmition	DescriptorContentTransmition;
                    DescriptorContentTransmition.DvbDescriptorContentTransmisition(pchBuffer); 
                    m_vecDescriptorContentTransmition.push_back(DescriptorContentTransmition);
					break;
				}
 
	  default: 
		// not implemented, not CIT, that should not happend
		return nLength;
		break;
	} 
	return nLength;   // (descriptor total length)
};


std::vector<std::string> CPremiereCit::GetEpgForVdr()
{
    std::vector<std::string> vecstrEpgForVdr;
//       from vdr manpage 5:
//       The following tag characters are defined:
//
//       C   <channel id> <channel name>
//       E   <event id> <start time> <duration> <table id>
//       T   <title>
//       S   <short text>
//       D   <description>
//       X   <stream> <type> <language> <descr>
//       V   <vps time>
//       e
//       c

//       <channel id>      is the "channel ID", made up from the parameters defined in 'channels.conf'
//       S19.2E-1-1089-12003-0
//       <channel name>    is the "name" as in 'channels.conf' (for information only, may be left out)
//       <start time>      is the time (as a time_t integer) in UTC when this event starts
//       <duration>        is the time (in seconds) that this event will take
//       <table id>        is a hex number that indicates the table this event is contained in (if this is left empty or 0 this event will not be overwritten or modified by data that comes from the DVB stream)
//       <title>           is the title of the event
//       <short text>      is the short text of the event (typically the name of the episode etc.)
//       <description>     is the description of the event (any '|' characters will be interpreted as newlines)
//       <stream>          is the stream content (1 = video, 2 = audio)
//       <type>            is the stream type according to ETSI EN 300 468
//       <language>        is the three letter language code
//       <descr>           is the description of this stream component
//       <vps time>        is the Video Programming Service time of this event

    // now we begin to create the string
    // we must have an channel id ...
    int nMaxDescriptorContentTransmition;
    nMaxDescriptorContentTransmition = GetMaxDescriptorContentTransmition();
    if (0 < nMaxDescriptorContentTransmition)
    {
        // make for every an own string
        for (int nCounter = 0;nCounter <= nMaxDescriptorContentTransmition; nCounter++)
        {
            // variable for single EPG entry
            std::string strEpg;
            
            // add channel id
            CDescriptorContentTransmition DescriptorContentTransmition;
            DescriptorContentTransmition = GetDescriptorContentTransmition(nCounter);
            // source actuall hard coded....
            strEpg = "C S19.2E-";
	        strEpg += Number2String(DescriptorContentTransmition.GetOrginalNetworkId());
            strEpg += "-";
            strEpg += Number2String(DescriptorContentTransmition.GetTransportStreamId());
            strEpg += "-";
            strEpg += Number2String(DescriptorContentTransmition.GetServiceId());
            strEpg += "\n";

            // put the first part to string, other parts will be repated after this
            vecstrEpgForVdr.push_back(strEpg);
            
            // we can have more times, so we will split this
            int nMaxTimes;
            nMaxTimes = DescriptorContentTransmition.GetMaxTimes();
            for (int nTimeCounter = 0; nTimeCounter < nMaxTimes; nTimeCounter++)
            {
                // add event id (content id), time, ....
                std::string strEpgSplit;
                strEpgSplit += "E ";
                // content id is the same for events which are send later again....
                // we will calc the second and following event id
                int nContentId;
                nContentId = GetContentId();
                if (0 < nTimeCounter)
                {
                    // we will use the id space (ids will counted up) before the were 
                    nContentId = nContentId * 15 + nTimeCounter;
                }
                strEpgSplit += Number2String(nContentId);
                strEpgSplit += " ";
                time_t time;
                time = DescriptorContentTransmition.GetTime(nTimeCounter);
                strEpgSplit += Number2String(time);
                strEpgSplit += " ";

                // duration
                strEpgSplit += Number2String(GetEventDuration());

                // table id
                strEpgSplit += " A0\n";

                // T   <title>
                strEpgSplit += "T ";
                if (0 != GetMaxDescriptorShortEvent())
                {
                    CDescriptorShortEvent   DescriptorShortEvent;
                    // get first entry, other will be ignored
                    DescriptorShortEvent = GetDescriptorShortEvent(0);
                    strEpgSplit += DescriptorShortEvent.GetEventName();
    
                    // S   <short text>
                    if (0 != DescriptorShortEvent.GetText().size())
                    {
                        strEpgSplit += "\n";
                        strEpgSplit += "S ";
                        strEpgSplit += DescriptorShortEvent.GetText();
                    }
                }

                // D   <description> '|' characters will be interpreted as newlines)
                if (0 != GetExtendDescriptorText().size())
                {
                    strEpgSplit += "\n";
                    strEpgSplit += "D ";
                    strEpgSplit += ReplaceString(std::string("\n"), std::string("|"), GetExtendDescriptorText());
                }
                
                // create end of entry
                strEpgSplit += "\ne\n";
                
                // other epg data is currently not supported....

                // at least add string to vector
                vecstrEpgForVdr.push_back(strEpgSplit);
            }
            if (0 == nMaxTimes)
            {
                //Delete String
                vecstrEpgForVdr.pop_back();
            }
            else
            {
                // create end of entry
                vecstrEpgForVdr.push_back("c\n");
            }
        }
    }
    
    // return vector
    return vecstrEpgForVdr;
}

std::string CPremiereCit::GetExtendDescriptorText()
{
	std::string		strCompleteText;
	unsigned int	nIndex;

	for (nIndex = 0; nIndex < GetMaxDescriptorExtendedEvent(); nIndex++)
	{
		strCompleteText += m_vecDescriptorExtendedEvent[nIndex].GetText();
	}

	return strCompleteText;
}

unsigned long	CPremiereCit::GetEventDuration()
{
    unsigned long       nEventDuration;
	char				szDuration[10];
	char				szDurationSplit[10];
	int				nHours;
	int				nMinutes;
	int				nSeconds;

	// create basic duration string
	sprintf(szDuration, "%06x", m_nDuration);

	// get the different information (hours, minutes and seconeds
	szDurationSplit[0] = szDuration[0];
	szDurationSplit[1] = szDuration[1];
	szDurationSplit[2] = '\0';
	nHours = atoi(szDurationSplit);

	szDurationSplit[0] = szDuration[2];
	szDurationSplit[1] = szDuration[3];
	szDurationSplit[2] = '\0';
	nMinutes = atoi(szDurationSplit);

	szDurationSplit[0] = szDuration[4];
	szDurationSplit[1] = szDuration[5];
	szDurationSplit[2] = '\0';
	nSeconds = atoi(szDurationSplit);

	// collect duration as seconds
	nEventDuration = (nHours * 60 * 60) + (nMinutes * 60) + nSeconds;

	// retrun event duration in seconds
	return nEventDuration;
}



//  0x4D  Short Event  descriptor 
//  ETSI EN 300 468     6.2.xx
void CDescriptorShortEvent::DvbDescriptorShortEvent(unsigned char *pchBuffer)
{
	// declare vars
	int				nLength2;

	// get language Code
	m_strLanguage = char2string( pchBuffer+2, 3);
	pchBuffer += 5;

	// event name
	nLength2 =  getBits(pchBuffer, 0, 0, 8);
	pchBuffer += 1;

	if (0 < nLength2)
	{
		m_strEventName = char2string( pchBuffer, nLength2);
		pchBuffer += nLength2;
	}

	// text
	nLength2 =  getBits(pchBuffer, 0, 0, 8);
	pchBuffer += 1;
	if (0 < nLength2)
	{
		m_strText = char2string(pchBuffer, nLength2);
	}

//	cout << "Language: \t" << m_strLanguage.c_str() << endl;
//	cout << "Eventname: \t" << m_strEventName.c_str() << endl;
//	cout << "Text: \t" << m_strText.c_str() << endl;
}


//  0x4E  Extended Event  descriptor 
//  ETSI EN 300 468     6.2.xx
void CDescriptorExtendedEvent::DvbDescriptorExtendedEvent(unsigned char *pchBuffer)
{
	// declare vars
	int                  nLength1, nLengthB;
	unsigned int			nDescriptorTag;
	unsigned int			nDescriptorLength;
	unsigned int			nTextLength;
	unsigned int			nLengthOfItems;
 	unsigned int			nItemDescriptionLength;
	unsigned int			nItemLength;

	// get basic information
	nDescriptorTag			= pchBuffer[0];
	nDescriptorLength		= pchBuffer[1];

	// get desciptor infos
	m_nDescriptorNumber			= getBits (pchBuffer, 0, 16, 4);
	m_nLastDescriptorNumber		= getBits (pchBuffer, 0, 20, 4);
	m_strLanguage = char2string(pchBuffer+3, 3);
	nLengthOfItems					= getBits (pchBuffer, 0, 48, 8);

	// move pointer
	pchBuffer   += 7;
	nLengthB		= nDescriptorLength - 5;
	nLength1		= nLengthOfItems;

	while (0 < nLength1)
	{
		// define local std::string
		std::string strTemp;

		// get Item description
		nItemDescriptionLength		= getBits (pchBuffer, 0, 0, 8);
		strTemp = char2string( pchBuffer+1, nItemDescriptionLength-1);
		m_vecstrItemDescriptors.push_back(strTemp);

		// set new pointer position and remove string entry
		pchBuffer += 1 + nItemDescriptionLength;
		strTemp.erase();

		// get item
		nItemLength	 					= getBits (pchBuffer, 0, 0, 8);
		strTemp = char2string( pchBuffer+1, nItemLength-1);
		m_vecstrItem.push_back(strTemp);

		// set new pointer position
		pchBuffer += 1 + nItemLength;

		// remove bytes from counters
		nLength1	-= (2 + nItemDescriptionLength + nItemLength);
		nLengthB -= (2 + nItemDescriptionLength + nItemLength);

//	    cout << "ItdemDesc: \t" << m_vecstrItemDescriptors.back().c_str() << endl;
//  	cout << "ItdemChar: \t" << m_vecstrItem.back().c_str() << endl;
	}

	// get text length and move pointer /counter
	nTextLength		 = getBits (pchBuffer, 0, 0, 8);
	pchBuffer += 1;
	nLengthB	 -= 1;

	m_strText = char2string( pchBuffer, nTextLength);



//	cout << "Language: \t" << m_strLanguage.c_str() << endl;
//	cout << "DescNumber: \t" << m_nDescriptorNumber << endl;
//	cout << "LastDescNumber: \t" << m_nLastDescriptorNumber << endl;
//	cout << "Text: \t" << m_strText.c_str() << endl;
}


// 0x54  Content  descriptor 
// ETSI EN 300 468     6.2.xx
void CDescriptorContent::DvbDescriptorContent(unsigned char *pchBuffer)
{
	// decalre vars
	int				nLength;

	// get basic descriptor infos
	nLength       = pchBuffer[1];
	pchBuffer    += 2;

	while (0 < nLength)
	{
		m_vecnContentNibbleLevel1.push_back(getBits (pchBuffer,0, 0,4));
		m_vecnContentNibbleLevel2.push_back(getBits (pchBuffer,0, 4,4));
		m_vecnUserNibble1.push_back(getBits (pchBuffer,0, 8,4));
		m_vecnUserNibble2.push_back(getBits (pchBuffer,0,12,4));

		// debug
//        cout << "Conent Nibble 1: \t" << m_vecnContentNibbleLevel1.back() << endl;
//        cout << "Conent Nibble 2: \t" << m_vecnContentNibbleLevel2.back() << endl;
//        cout << "User Nibble 1: \t" << m_vecnUserNibble1.back() << endl;
//        cout << "User Nibble 2: \t" << m_vecnUserNibble2.back() << endl;
//        cout << "Content Class: \t" << DvbStringContentNibbleType(m_vecnContentNibbleLevel1.size()-1) << endl;

		// move pointe to next
		pchBuffer  += 2;
		nLength    -= 2;
	}
}


//  dvb.org 
//  -- ETSI EN 300 468   6.2.7
char* CDescriptorContent::DvbStringContentNibbleType(unsigned int nIndex)
{
	// check for valid index
	if (nIndex >= m_vecnContentNibbleLevel1.size())
	{
		return NULL;
	}

	// ContenNibble_1 << 8    |  ContentNibble_2
	//  4 bit                 |       4 bit
	switch (m_vecnContentNibbleLevel1[nIndex] << 8 | m_vecnContentNibbleLevel2[nIndex])
	{
   case 0x0000:
	case 0x000F: return "reserved";

     // Movie/Drama
   case 0x0100: return "movie/drama (general)";
   case 0x0101: return "detective/thriller";
   case 0x0102: return "adventure/western/war";
   case 0x0103: return "science fiction/fantasy/horror";
   case 0x0104: return "comedy";
   case 0x0105: return "soap/melodram/folkloric";
   case 0x0106: return "romance";
   case 0x0107: return "serious/classical/religious/historical movie/drama";
   case 0x0108: return "adult movie/drama";

   case 0x0109: return "reserved";
   case 0x010F: return "user defined";

     // News Current Affairs
   case 0x0200: return "news/current affairs (general)";
   case 0x0201: return "news/weather report";
   case 0x0202: return "news magazine";
   case 0x0203: return "documentary";
   case 0x0204: return "discussion/interview/debate";
   case 0x0205: return "reserved";
   case 0x020F: return "user defined";

     // Show Games show
   case 0x0300: return "show/game show (general)";
   case 0x0301: return "game show/quiz/contest";
   case 0x0302: return "variety show";
   case 0x0303: return "talk show";
   case 0x0304: return "reserved";
   case 0x030F: return "user defined";

     // Sports
   case 0x0400: return "sports (general)";
   case 0x0401: return "special events";
   case 0x0402: return "sports magazine";
   case 0x0403: return "football/soccer";
   case 0x0404: return "tennis/squash";
   case 0x0405: return "team sports";
   case 0x0406: return "athletics";
   case 0x0407: return "motor sport";
   case 0x0408: return "water sport";
   case 0x0409: return "winter sport";
   case 0x040A: return "equestrian";
   case 0x040B: return "martial sports";
   case 0x040C: return "reserved";
   case 0x040F: return "user defined";

     // Children/Youth
   case 0x0500: return "childrens's/youth program (general)";
   case 0x0501: return "pre-school children's program";
   case 0x0502: return "entertainment (6-14 year old)";
   case 0x0503: return "entertainment (10-16 year old)";
   case 0x0504: return "information/education/school program";
   case 0x0505: return "cartoon/puppets";
   case 0x0506: return "reserved";
   case 0x050F: return "user defined";

     // Music/Ballet/Dance 
   case 0x0600: return "music/ballet/dance (general)";
   case 0x0601: return "rock/pop";
   case 0x0602: return "serious music/classic music";
   case 0x0603: return "folk/traditional music";
   case 0x0604: return "jazz";
   case 0x0605: return "musical/opera";
   case 0x0606: return "ballet";
   case 0x0607: return "reserved";
   case 0x060F: return "user defined";

     // Arts/Culture
   case 0x0700: return "arts/culture (without music, general)";
   case 0x0701: return "performing arts";
   case 0x0702: return "fine arts";
   case 0x0703: return "religion";
   case 0x0704: return "popular culture/traditional arts";
   case 0x0705: return "literature";
   case 0x0706: return "film/cinema";
   case 0x0707: return "experimental film/video";
   case 0x0708: return "broadcasting/press";
   case 0x0709: return "new media";
   case 0x070A: return "arts/culture magazine";
   case 0x070B: return "fashion";
   case 0x070C: return "reserved";
   case 0x070F: return "user defined";

     // Social/Political/Economics
   case 0x0800: return "social/political issues/economics (general)";
   case 0x0801: return "magazines/reports/documentary";
   case 0x0802: return "economics/social advisory";
   case 0x0803: return "remarkable people";
   case 0x0804: return "reserved";
   case 0x080F: return "user defined";

     // Education/Science/...
   case 0x0900: return "education/science/factual topics (general)";
   case 0x0901: return "nature/animals/environment";
   case 0x0902: return "technology/natural science";
   case 0x0903: return "medicine/physiology/psychology";
   case 0x0904: return "foreign countries/expeditions";
   case 0x0905: return "social/spiritual science";
   case 0x0906: return "further education";
   case 0x0907: return "languages";
   case 0x0908: return "reserved";
   case 0x090F: return "user defined";

     // Leisure hobies
   case 0x0A00: return "leisure hobbies (general)";
   case 0x0A01: return "tourism/travel";
   case 0x0A02: return "handicraft";
   case 0x0A03: return "motoring";
   case 0x0A04: return "fitness & health";
   case 0x0A05: return "cooking";
   case 0x0A06: return "advertisement/shopping";
   case 0x0A07: return "gardening";
   case 0x0A08: return "reserved";
   case 0x0A0F: return "user defined";

   case 0x0B00: return "original language";
   case 0x0B01: return "black & white";
   case 0x0B02: return "unpublished";
   case 0x0B03: return "live broadcast";
   case 0x0B04: return "reserved";
   case 0x0B0F: return "user defined";

   case 0x0C00: return "reserved";
   case 0x0F00: return "user defined";
	}
	// nothing found
	return NULL;
}


// 	Premiere privat
//   0xF2  Content Transmition Descriptor
//   PTS 60 04 101    v 1.0.1 17.06.2004
void CDescriptorContentTransmition::DvbDescriptorContentTransmisition(unsigned char *pchBuffer)
{
	// decalre vars
	int				nLength;

	// get basic descriptor infos
	nLength       = pchBuffer[1];
	pchBuffer    += 2;

    // get ids for cit entry
    m_nTransportStreamId = getBits (pchBuffer,0, 0, 16);
    m_nOrginalNetworkId = getBits (pchBuffer,0, 16, 16);
    m_nServiceId = getBits (pchBuffer,0, 32, 16);

    // new length / pointer
	nLength      -= 6;
	pchBuffer    += 6;

//    cout << "Transport Stream ID: " << m_nTransportStreamId << endl;
//    cout << "Orginal Network ID: " << m_nOrginalNetworkId << endl;
//    cout << "Service ID: " << m_nServiceId << endl;

	while (0 < nLength)
	{
        // get start time
        unsigned long nStartDateMJD;
  	    nStartDateMJD = getBits (pchBuffer, 0, 0, 16);
  	    int nStartTimeLoopLength = getBits(pchBuffer, 0, 16, 8);

        // set new pointer / length
        pchBuffer += 3;
        nLength -= 3;

        // loop all times
     	for(; nStartTimeLoopLength>0; nStartTimeLoopLength-=3)
        {
            // save date
            m_vecnStartDateMJD.push_back(nStartDateMJD);
            // save start time
            m_vecnStartTime.push_back(getBits (pchBuffer, 0, 0, 24));

            // set new pointer / length
	    	pchBuffer += 3;
    		nLength -= 3;
        }
 	}
};

time_t CDescriptorContentTransmition::GetTime(int nIndex)
{
    // check for correct index
    if (nIndex < m_vecnStartTime.size())
    {
        // get start time and start date
        unsigned long nStartDate;
        unsigned long nStartTime;
        nStartDate = m_vecnStartDateMJD.at(nIndex);
        nStartTime = m_vecnStartTime.at(nIndex);
        
        // algo: ETSI EN 300 468 - ANNEX C
        long   y,m,d ,k;
        y =  (long) ((nStartDate  - 15078.2) / 365.25);
        m =  (long) ((nStartDate - 14956.1 - (long)(y * 365.25) ) / 30.6001);
        d =  (long) (nStartDate - 14956 - (long)(y * 365.25) - (long)(m * 30.6001));
        k =  (m == 14 || m == 15) ? 1 : 0;
        y = y + k + 1900;
        m = m - 1 - k*12;
        
        // put time to tm struct
        struct tm  timeinfo;
        timeinfo.tm_hour = ((nStartTime>>16) &0xFF) - (((nStartTime>>16) &0xFF) / 16) * 6; // from 0 - 23
        timeinfo.tm_min = ((nStartTime>>8) &0xFF) - (((nStartTime>>8) &0xFF) / 16) * 6;; // from 0 - 59
        timeinfo.tm_sec = ((nStartTime) &0xFF) - (((nStartTime) &0xFF) / 16) * 6;; // from 0 - 59
        
        // put data to tm struct
        timeinfo.tm_year = y - 1900; // less 1900
        timeinfo.tm_mon = m - 1;    // from 0 - 11
        timeinfo.tm_mday = d; // from 1 - 31
        timeinfo.tm_isdst = 0;

        // return with time_t
        return mktime(&timeinfo);
    }
    
    // return no time;
    return 0;
};
