/***************************************************************************
 *   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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include "cit2vdr.h"
#include <sstream> 
#if HAVE_CONFIG_H 
#include <config.h> 
#endif

#define BUF_SIZ	4096

// define verbose level (default)
// 0 = no output
// 1 = normal output
// 2 = some more information
// 4 = most steps
// 5 = Debug mode
int nVerboseLevel=1;


class CSvdrClient
{
public:    
    int     Connect();
    int     SendRequest(std::string strSend);
    int     GetResponse();
    int     Close()                 {return close(m_nSocket);};

private:
	struct hostent*      m_host;
	struct sockaddr_in   m_addr;
    int                  m_nSocket;

};


int CSvdrClient::SendRequest(std::string strSend)
{
	if (send(m_nSocket, strSend.c_str(), strSend.size(), 0) == -1)
	{
		cout << "send() failed" << endl;
		return 1;
	}

	return 0;
}

int CSvdrClient::GetResponse()
{
    char chReceive[BUF_SIZ];
    int nResponse;
	int nBytes;
    int nTimeout = 0;
    
    // debug
    if (5 <= nVerboseLevel)
    {
        cout << "Waitng for Data" << endl;
    }

    // receive data and get responsecode
	nBytes = recv(m_nSocket, chReceive, BUF_SIZ, 0);
    nResponse = atoi(chReceive);
    
    // debug
    if (5 <= nVerboseLevel)
    {
        cout << "Response: " << nResponse << endl;
        cout << "Bytes: " << nBytes << endl;
        cout << "Data: " << chReceive << endl;
    }
        
	if (0 > nBytes)
	{
		cout << "recv() failed" << endl;
		return -1;
	}

    // get rest of the data if nessecary
    while ('\n' != chReceive[strlen(chReceive)-1])
    {
        // debug
        if (5 <= nVerboseLevel)
        {
            cout << "no newline , wait for more data" << endl;
        }
        
        for (int nCounter = 0;nCounter <= BUF_SIZ; nCounter++)
        {
            chReceive[nCounter] = '\0';
        }
        
        nBytes = recv(m_nSocket, chReceive, BUF_SIZ, 0);

        // debug
        if (5 <= nVerboseLevel)
        {
            cout << "Bytes: " << nBytes << endl;
            cout << "Data: " << chReceive << endl;
        }
    }

    for (int nCounter = 0;nCounter <= BUF_SIZ; nCounter++)
    {
        chReceive[nCounter] = '\0';
    }

    // return response
	return nResponse;
}

int CSvdrClient::Connect()
{
    // hostname
	if (!inet_aton("localhost", &m_addr.sin_addr))
	{
		m_host = gethostbyname("localhost");
		if (!m_host)
		{
            // error
            cout << "gethostbyname() failed" << endl;
			return 2;
		}
		m_addr.sin_addr = *(struct in_addr*)m_host->h_addr;
	}

	m_nSocket = socket(PF_INET, SOCK_STREAM, 0);
	if (-1 == m_nSocket)
	{
        cout << "socket() failed" << endl;
		return 3;
	}

    // port
	m_addr.sin_port = htons(2001);
	m_addr.sin_family = AF_INET;

	if (connect(m_nSocket, (struct sockaddr*)&m_addr, sizeof(m_addr)) == -1)
	{
        // debug
		cout << "Faild to connect to localhost 2001" << endl;
		return 4;
	}

	return 1;
}

std::vector<std::string>  GetData(int nAdapter, int nDemux, int nPid)
{
    std::vector<std::string> vecstrEpgData;
    int     fd;
    unsigned char  chBuffer[READ_BUF_SIZE]; 		/* data buffer */
    long    count;
    int     openMode;
    int     dmxMode;
    long    nDmxBufferSize = SECT_BUF_SIZE;

    // set device options
    str:string strDevice;
    strDevice = "/dev/dvb/adapter";
    strDevice += Number2String(nAdapter);
    strDevice += "/demux";
    strDevice += Number2String(nDemux);

  	openMode = O_RDWR;
    dmxMode  = 1;

    // open device
    if((fd = open(strDevice.c_str(),openMode)) < 0)
    {
        // exit on error
        return vecstrEpgData;
    }

    // init demux
    if (dmxMode)
    {
        long nRdBufferSize = 0L;	// use default read buffersize
        struct dmx_sct_filter_params flt;
        
        // alloc dmx buffer for SECTION
        if (0 < nRdBufferSize)
        {
            nDmxBufferSize = nRdBufferSize;
        }

        // set new buffer size
        if (0 > ioctl(fd,DMX_SET_BUFFER_SIZE, nDmxBufferSize))
        {
            // failed => close and exit
            close (fd);
            return vecstrEpgData;
        }
    
        memset (&flt, 0, sizeof (struct dmx_sct_filter_params));
    
        // futur get pid from search
        flt.pid = nPid;
        flt.filter.filter[0] = 0;
        flt.filter.mask[0] = 0;
        flt.timeout = 1000; // 1000 ms, not defined in DVB Standard, but this should the max time for data...
        flt.flags = DMX_IMMEDIATE_START;
        // flt.flags |= DMX_CHECK_CRC;
    
        // set filter
        if (0 < ioctl (fd, DMX_SET_FILTER, &flt))
        {
            // faild => close file and exit
            close(fd);
            return vecstrEpgData;
        }
    }

    // declartion for content id
    unsigned int nContentId = 0;
    
    // read SECTION packets for pid
    count = 0;
    while (1)
    {
        long   nLenght;
        nLenght = sect_read(fd,chBuffer,sizeof(chBuffer));

        // -- error or eof?
        if (0 > nLenght)
        {
            //    	err = IO_error("read"); future how to eime the error
        	if (errno == ETIMEDOUT) 
            {
                break;		// Timout, abort
            }
        	continue;
        }

        if (0 == nLenght)
        {
            if (dmxMode) continue;	// dmxmode = no eof!
            else break;		// filemode eof 
        }
        
        // increment counter
        count++;

        // decode protocol
        // decodeSections_buf (buf,nLenght);
        // Premiere CIT section
       	if (0xA0 == chBuffer[0])
        {
            // make a "bar"
            if (1 <= nVerboseLevel)
            {
                cout << "." << flush;
            }
            
            // decode here cit
            CPremiereCit PremiereCit(chBuffer, nLenght);
            // debug
            if (5 <= nVerboseLevel)
            {
                cout << "Found Content Id: " << PremiereCit.GetContentId() << endl;
            }

            // check for first double entry
            if (nContentId == PremiereCit.GetContentId())
            {
                break;
            }
            
            // create string for vdr
            std::vector<std::string> vecstrEpg;
            vecstrEpg = PremiereCit.GetEpgForVdr();
            // add to vecstrEpgData
            for (int nCounter = 0;nCounter < vecstrEpg.size();nCounter++)
            {
                vecstrEpgData.push_back(vecstrEpg.at(nCounter));
            }
            
            // get first content id
            if (0 == nContentId)
            {
                nContentId = PremiereCit.GetContentId();
            }
        }

        // Clean Buffer
        // if (n > 0 && n < sizeof(buf)) memset (buf,0,n+1); 

    }

    // info
    if (3 <= nVerboseLevel)
    {
        cout << "Found " << count << " different Content Ids" << endl;
    }
    
    // Stop Demux
    if (dmxMode)
    {
        ioctl (fd, DMX_STOP, 0);
    }
    
    // close
    close(fd);
    
    // default return
    return vecstrEpgData;
};

// code from dvbsnoop
// see http://dvbsnoop.sourceforge.net/ for more information about the best DVB Sniffer
static long  sect_read (int fd, u_char *buf, long buflen)
{
    int    n;
    int    sect_len;


    n = read(fd,buf,3);				// read section header
    if (n <= 0) return n;			// error or strange, abort

    // section size
    // -- table_id 	8  uimsbf
    // -- some stuff   	4  bits
    // -- sectionlength 12 uimsbf

    sect_len = ((buf[1] & 0x0F) << 8) + buf[2];	// get section size
    if (sect_len > (buflen-3)) return -1;	// something odd?

    n = read(fd,buf+3,sect_len);		// read 1 section
    if (n >=0) n += 3;				// we already read header bytes

    return n;
}


void SendToVdr(std::vector<std::string> vecstrEpgData)
{
    CSvdrClient     SvdrClient;
    int nResponse;
    
    // debug
    if (3 <= nVerboseLevel)
    {
        cout << "Connect to localhost 2001" << endl;
    }
    
    // at first connect to svdrp
    SvdrClient.Connect();
    // get responsecode and data
    nResponse = SvdrClient.GetResponse();

    // check for okay
    if (220 != nResponse)
    {
        cout << "no svdr found" << endl;
        SvdrClient.Close();
        return;
    }
    
    // debug
    if (3 <= nVerboseLevel)
    {
        cout << "Send request for PUTE" << endl;
    }

    // Send request for PUTE and get response
    SvdrClient.SendRequest(std::string("PUTE\n"));
    nResponse = SvdrClient.GetResponse();
    
    // check for okay
    if (354 != nResponse)
    {
        cout << "Svdr not ready for epg data" << endl;
        SvdrClient.Close();
        return;
    }

    // Debug
    if (3 <= nVerboseLevel)
    {
        cout << "Transfer data to vdr" << endl;
    }

    // Send request for PUTE and get response
    for (int nCounter = 0;nCounter < vecstrEpgData.size();nCounter++)
    {
        // debug
        if (4 <= nVerboseLevel)
        {
            cout << "Transfer data " << nCounter + 1 << " of " << vecstrEpgData.size() << endl;
        }
        
        if (5 <= nVerboseLevel)
        {
            cout << "Data:" << endl << vecstrEpgData.at(nCounter) << endl;
        }

        std::string strEpgSingleData;
        strEpgSingleData = vecstrEpgData.at(nCounter);
        SvdrClient.SendRequest(strEpgSingleData);
    }
    // send terminating char
    if (4 <= nVerboseLevel)
    {
        cout << "send terminating char" << endl;
    }
    SvdrClient.SendRequest(".\n");
    nResponse = SvdrClient.GetResponse();
    
    // check for okay
    if (250 != nResponse)
    {
        cout << "Data not okay for vdr" << endl;
        SvdrClient.Close();
        return;
    }

    // Debug
    if (1 <= nVerboseLevel)
    {
        cout << "Data from VDR accecptet" << endl;
    }

    // terminate session
    SvdrClient.SendRequest("quit\n");
    SvdrClient.Close();
};


void SwitchVdrChannel(char* pchChannel)
{
    CSvdrClient     SvdrClient;
    int nResponse;
    
    // debug
    if (3 <= nVerboseLevel)
    {
        cout << "Connect to localhost 2001" << endl;
    }
    
    // at first connect to svdrp
    SvdrClient.Connect();
    // get responsecode and data
    nResponse = SvdrClient.GetResponse();

    // check for okay
    if (220 != nResponse)
    {
        cout << "no svdr found" << endl;
        SvdrClient.Close();
        return;
    }
    
    // debug
    if (3 <= nVerboseLevel)
    {
        cout << "Send request for chan " << pchChannel << endl;
    }

    // Send request for PUTE and get response
    std::string strRequest = "chan ";
    strRequest += pchChannel;
    strRequest += "\n";
    SvdrClient.SendRequest(strRequest);
    nResponse = SvdrClient.GetResponse();
    
    // check for okay
    if (250 != nResponse)
    {
        cout << "error switching channel" << endl;
        SvdrClient.Close();
        return;
    }

    // Debug
    if (2 <= nVerboseLevel)
    {
        cout << "Channel switched" << endl;
    }

    // terminate session
    SvdrClient.SendRequest("quit\n");
    SvdrClient.Close();
};


void Help()
{
	cout << "Usage:" << endl;
    cout << "	-a #     Number of dvb card to use (0,1,...)" << endl;
	cout << "	-p #     Number of demux device to use (normally 0)" << endl;
	cout << "	-d #     PID where the cit is (normally 0xb12)" << endl;
	cout << "	-c #     Channel to switch vdr to (example PREMIERE\\ SPORT\\ PORTAL)" << endl;
	cout << "	-h       Display this help" << endl;
	cout << endl;
	cout << "Example:" << endl;
	cout << "	" << endl
	<< endl;
}


// main function
int main(int argc, char **argv)
{
	int nAdapter=0;
	int nDemux=0;
	int nPid=0xb12;
    char chChannel[1001];
    chChannel[0] = '\0';

	char chOpt;
	
	while ((chOpt = getopt(argc, argv, "a:d:p:c:qvh")) != -1) {
		switch (chOpt) {
		case 'a':
			nAdapter = atoi(optarg);
			break;
		case 'd':
			nDemux = atoi(optarg);
			break;
		case 'p':
			nPid = atoi(optarg);
			break;
		case 'c':
            strncpy(chChannel, optarg, 1000);
			break;
		case 'q':
			nVerboseLevel--;
			break;
		case 'v':
			nVerboseLevel++;
			break;
		case 'h':
			Help();
			return 0;
			break;	
		}
	}
    
	if (1 <= nVerboseLevel)
    {
        cout << PACKAGE_NAME << " Version " << PACKAGE_VERSION << endl << "Report bugs to " << PACKAGE_BUGREPORT << endl << endl;
    }

    // debug
    // zap to channel 
    if (0 != strlen(chChannel))
    {
    	if (5 <= nVerboseLevel)
        {
            cout << "switch to channel " << chChannel << endl;
        }
        SwitchVdrChannel(chChannel);
    }
    
    // future: find correct pid on Transponder

    // get data from cit
	if (1 <= nVerboseLevel)
    {
        cout << "Start Data Receving" << endl << endl;
    }
    std::vector<std::string>    vecstrEpgData;
    vecstrEpgData = GetData(nAdapter, nDemux, nPid);

    // Info
	if (1 <= nVerboseLevel)
    {
        cout << endl << "Found " << vecstrEpgData.size() << " Events" << endl;
    }
    
    // send data to vdr
    if (0 != vecstrEpgData.size())
    {
        SendToVdr(vecstrEpgData);
    }
    else
    {
        cout << "no Events Found, are you on the correct Channel and correct PID?" << endl;
    }
    
    for (int nCounter = 0;nCounter < vecstrEpgData.size();nCounter++)
    {
        std::string strEpgSingleData;
        strEpgSingleData = vecstrEpgData.at(nCounter);
    	if (5 <= nVerboseLevel)
        {
            cout << strEpgSingleData << endl;
        }
    }
    
    // exit
    return 0;
}
