#include <iostream>

#include "transceiver.h"
#include "keys.h"
#include "setup.h"
#include "common.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>

#include <vdr/tools.h>

#include "cfile.h"
#include "csocket.h"
#include "cselect.h"
#include "cbuffer.h"

using namespace std;

//#define DEBUG_DATAFLOW

cClusterTransceiver::cClusterTransceiver(const cString &Filename) {
	m_Active = false;
	m_Filename = Filename;
	m_CmdBufferFill = 0;
}

cClusterTransceiver::~cClusterTransceiver() {
}

void cClusterTransceiver::Activate(bool On) {
	if (On)
		Start();
	else
		Stop();
}

void cClusterTransceiver::Stop(void) {
	m_Active = false;
	Cancel(3);
}

void cClusterTransceiver::Action(void) {
	cTBFile   video;
	cTBSelect fiSelect;
	
	uint16_t  commandPort;
	uint16_t  filenameLen;
	char     *initPacket;
	size_t    initPacketFill;

	cString   remoteFilename;

	bool      readRawData = true;
	bool      writeRawData = true;
	bool      writeCmdData = true;

	char      rawBuffer[KILOBYTE(16)];

	size_t    rawBufferFill = 0;

	ssize_t   bytes;

	isyslog("mplayercluster transceiver thread started");
	
	m_Active = true;
	
	if (m_Active && !video.Open(m_Filename, O_RDONLY)) {
		esyslog("Couldn't open input file: %s", strerror(errno));
		m_Active = false;
	}

	if (m_Active && !m_DataSocket.Connect(ClusterSetup.RecodeIp, 
			ClusterSetup.RecodePort)) {
		esyslog("Couldn't connect to server: %s", strerror(errno));
		m_Active = false;
	}
	
	if (m_Active) {
		remoteFilename="";
		if (ClusterSetup.RemoteNFS) {
			cString localNFS("/");
			cString remoteNFS(ClusterSetup.NFSPrefix);
			
			int SplitPos=remoteNFS.Find('~');
			if (SplitPos>=0) {
				localNFS=remoteNFS.left(SplitPos);
				remoteNFS=remoteNFS.mid(SplitPos+1);
			}
			
			if (localNFS[localNFS.length()-1]!='/') localNFS.append('/');
			if (remoteNFS[remoteNFS.length()-1]!='/') remoteNFS.append('/');
			
			isyslog("local NFS: \"%s\" remote NFS: \"%s\"",(const char*)localNFS,(const char*)remoteNFS);
			
			if (m_Filename.left(localNFS.length())==localNFS) {
				remoteFilename=remoteNFS + m_Filename.mid(localNFS.length());
			}
		}
	}


	
	initPacket = new char[PKT_HEADERLEN + remoteFilename.length()];
		
	cout << "cretincreting " << (int)initPacket << endl;

	initPacketFill = 0;
	while (m_Active && (initPacketFill < PKT_HEADERLEN)) {
		fiSelect.Clear();
		fiSelect.Add(m_DataSocket, false);

		if (fiSelect.Select(-1) <= 0) {
			esyslog("Couldn't listen for greeting packet: %s", strerror(errno));
			m_Active = false;
			continue;
		}

		if (!m_DataSocket.Read(initPacket, PKT_HEADERLEN, initPacketFill)) {
			esyslog("Couldn't listen for greeting packet: %s", strerror(errno));
			m_Active = false;
			continue;
		}
	}

	if (m_Active && memcmp(initPacket, "MPCL", 4) != 0) {
		esyslog("mplayercluster: corrupt greeting from peer");
		m_Active = false;
	}

	if (m_Active) {
		memcpy(&commandPort, initPacket + 4, 2);
		commandPort = ntohs(commandPort);

		isyslog("mplayercluster: command connection to port %hu", commandPort);

		if (!m_CommandSocket.Connect(ClusterSetup.RecodeIp, commandPort)) {
			esyslog("Couldn't connect to command port: %s", strerror(errno));
			m_Active = false;
		}
	}

	cout << "sende initpaket" << endl;

	if (m_Active) {
		if (remoteFilename.length()>0) {
			filenameLen = htons(remoteFilename.length());
			memcpy(initPacket + 6, remoteFilename, remoteFilename.length());

			readRawData = false;
			writeRawData = false;
			
		} else
			filenameLen = 0;

		memcpy(initPacket + 4, &filenameLen, 2);
	}

	initPacketFill = PKT_HEADERLEN + filenameLen;
	while (m_Active && (initPacketFill > 0)) {
		fiSelect.Clear();
		fiSelect.Add(m_DataSocket, true);

		if (fiSelect.Select(-1) <= 0) {
			esyslog("Couldn't send greeting packet: %s", strerror(errno));
			m_Active = false;
			continue;
		}

		if (!m_DataSocket.Write(initPacket, initPacketFill)) {
			esyslog("Couldn't send greeting packet: %s", strerror(errno));
			m_Active = false;
			continue;
		}
	}
	
	cout << "ende initpaket" << endl;

	if (m_Active) {
		cout << "deleting " << (int)initPacket << endl;
		//delete[] initPacket;
	}

	cout << "nach delete" << endl;

#if defined(DEBUG_DATAFLOW)
	int count=0;
#endif
	while (m_Active) {
		fiSelect.Clear();

		if (readRawData && rawBufferFill < sizeof(rawBuffer))
			fiSelect.Add(video, false);

		if (writeRawData && rawBufferFill > 0)
			fiSelect.Add(m_DataSocket, true);

		if (writeCmdData && m_CmdBufferFill > 0)
			fiSelect.Add(m_CommandSocket, true);
		
		if (fiSelect.Select(10) < 0) {
			esyslog("Couldn't get file's or m_DataSocket's status: %s", 
					strerror(errno));
			m_Active = false;
			continue;
		}

		if (readRawData && fiSelect.CanRead(video)) {
			if (((bytes = video.Read(rawBuffer, sizeof(rawBuffer), 
					rawBufferFill)) == -1) && (errno != EAGAIN)) {
				esyslog("Couldn't read video data: %s", strerror(errno));
				m_Active = false;
				continue;
			}

			if (bytes == 0) {
				LOGi << "Video file just ended..." << endl;
				readRawData = false;
			}
		} 
			
		if (writeRawData && fiSelect.CanWrite(m_DataSocket)) {
			if (((bytes = m_DataSocket.Write(rawBuffer, rawBufferFill)) == -1)
					&& (errno != EAGAIN)) {
				esyslog("Couldn't write video data: %s", strerror(errno));
				m_Active = false;
				continue;
			}
		}

		if (writeRawData && !readRawData && rawBufferFill == 0) {
			LOGi << "All video data sent..." << endl;
			writeRawData = false;
			m_DataSocket.Shutdown(SHUT_WR);
		} 

		if (writeCmdData && fiSelect.CanWrite(m_CommandSocket)) {
			m_BufferAccess.Lock();

			cout << "Sending Remote Command" << endl;
			
			if (((bytes = m_CommandSocket.Write(m_CmdBuffer, m_CmdBufferFill)) == -1)
					&& (errno != EAGAIN)) {
				esyslog("Couldn't write command: %s", strerror(errno));
				m_Active = false;
				continue;
			}

			m_BufferAccess.Unlock();
		}

#if defined(DEBUG_DATAFLOW)
		if (++count == 10) {
			LOGi << "RAW(R): " << video.BytesRead() / KILOBYTE(1) 
				 << "kB, RAW(W): " << m_DataSocket.BytesWritten() / KILOBYTE(1)
				 << "kB, MPG(R): " << m_DataSocket.BytesRead() / KILOBYTE(1)
				 << "kB            \r";
			fflush(stdout);
			count = 0;
		}
#endif
	}

	cout << "schliesse alles" << endl;

	if (video.IsOpen())
		video.Close();

	if (m_DataSocket.IsOpen())
		m_DataSocket.Close();
	
	if (m_CommandSocket.IsOpen())
		m_CommandSocket.Close();

	isyslog("mplayercluster transceiver thread ended");
}

ssize_t cClusterTransceiver::Get(char *Buffer, size_t Length) {
	ssize_t bytes = 0;
	size_t fill = 0;

	bytes = m_DataSocket.Read(Buffer, Length, fill);

	if (bytes == -1)
		return -1;
	return fill;
}

#define MPLAYER_YELLOW "seek 60 type=0\n"
#define MPLAYER_DOWN "pause\n"

eOSState cClusterTransceiver::PutRemote(eKeys Key) {
	cString command;

	eOSState ret = osContinue;

	m_BufferAccess.Lock();

	command = ClusterKeys.Get(Key);

	cout << "Issuing remote command " << (const char*)command << endl;

	if (!command.isNull()) {
		memcpy(m_CmdBuffer + m_CmdBufferFill, command, command.length());
		m_CmdBufferFill += command.length();
	}

	m_BufferAccess.Unlock();

	return ret;
}
