#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

#include "csocket.h"
#include "cfile.h"
#include "cselect.h"
#include "cshared.h"
#include "clist.h"

//#define DEBUG_DATAFLOW

int main(int argc, char *argv[]) {
	cTBFile     video;
	cTBFile     output;
	cTBSocket   socket;
	cTBSelect   fiRead;
	cTBSelect   fiWrite;
	
	cTBList<int> *readyFiRead;
	cTBList<int> *readyFiWrite;

	cShared     rawbuf;
	cShared     mpgbuf;

	programName = argv[0];

	if (!video.Open("/video0/DivX/Enterprise/Enterprise 1x01 Broken Bow part 1.avi", O_RDONLY)) {
		LOGe << "Couldn't connect to server: " << strerror(errno) << endl;
		exit(1);
	}

	fiRead.AddDescriptor(video);

	if (!output.Open(1)) {
		LOGe << "Couldn't open standard output: " << strerror(errno) << endl;
		video.Close();
		exit(1);
	}

	fiWrite.AddDescriptor(output);

	if (!socket.Connect("192.168.1.2", 2003)) {
		LOGe << "Couldn't connect to server: " << strerror(errno) << endl;
		video.Close();
		exit(1);
	}

	fiRead.AddDescriptor(socket);
	fiWrite.AddDescriptor(socket);

#if defined(DEBUG_DATAFLOW)
	int count = 0;
#endif
	while (socket.IsOpen() && video.IsOpen()) {
		uint i;

		if ((readyFiRead = fiRead.CanRead()) == 0L) {
			LOGe << "Couldn't get file's or socket's status: " 
				 << strerror(errno) << endl;
			socket.Close();
			video.Close();
			exit(1);
		}

		for (i = 0; i < readyFiRead->count(); ++i) {
			if (readyFiRead->at(i) == video) {
				if (!video.Read(rawbuf, MAX_BUF_SIZE)) {
					LOGe << "Couldn't read video data: " << strerror(errno)
						 << endl;
					socket.Close();
					video.Close();
					exit(1);
				}
			} else {
				if (!socket.Read(mpgbuf, MAX_BUF_SIZE)) {
					LOGe << "Couldn't read mpeg data: " << strerror(errno)
						 << endl;
					socket.Close();
					video.Close();
					exit(1);
				}
			}
		}

		if ((readyFiWrite = fiWrite.CanWrite()) == 0L) {
			LOGe << "Couldn't get socket's status: " << strerror(errno) << endl;
			socket.Close();
			video.Close();
			exit(1);
		}
	
		for (i = 0; i < readyFiWrite->count(); ++i) {
			if (readyFiWrite->at(i) == socket) {
				if (rawbuf.size() && (!socket.Write(rawbuf))) {
					LOGe << "Couldn't write video data: " << strerror(errno) 
						 << endl;
					socket.Close();
					video.Close();
					exit(1);
				}
			} else {
#if !defined(DEBUG_DATAFLOW)
				if (mpgbuf.size() && (!output.Write(mpgbuf))) {
					LOGe << "Couldn't write mpeg data: " << strerror(errno)
						 << endl;
					socket.Close();
					video.Close();
					exit(1);
				}
#else
				mpgbuf.clear(); // We don't want the output to pollute the
								// DATAFLOW watcher
#endif
			}
		}

		if ((readyFiRead->count() == 0) && (readyFiWrite->count() == 0))
			usleep(50);

		delete readyFiRead;
		delete readyFiWrite;

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

