
#include "Process.h"

#include <signal.h>
#include <fcntl.h>

static void *startProcessThread(void *thisptr)
{
	Process *process = (Process*)thisptr;
	process->callThread();
	return 0;
}

Process::Process(std::string filename)
{
	this->filename = filename;
	pid = 0;
	inputfile = 0;
	outputfile = 0;
	errorfile = 0;
	argarray = 0;
}
Process::~Process()
{
}

bool Process::call(void)
{
	returnvalue = 0;
	stdoutdata.clear();
	stderrdata.clear();
	// Create pipes to redirect output
	if(pipe(inpipe))
	{
		printf("inpipe failed.\n");
		return false;
	}
	if(pipe(outpipe))
	{
		printf("outpipe failed.\n");
		return false;
	}
	int flags = fcntl(outpipe[0], F_GETFL);
	flags |= O_NONBLOCK;
	fcntl(outpipe[0], F_SETFL, flags);
	if(pipe(errpipe))
	{
		printf("errpipe failed.\n");
		return false;
	}
	fcntl(errpipe[0], F_GETFL);
	flags |= O_NONBLOCK;
	fcntl(errpipe[0], F_SETFL, flags);
	// Fork this process
	pid = fork();
	if (pid == -1)
	{
		printf("fork failed.\n");
		return false;
	}
	volatile bool initialized = false;
	if (pid)
	{
		// This is the original process
		// Open files and close unused pipe ends
		inputfile = fdopen(inpipe[1], "w");
		close(inpipe[0]);
		outputfile = fdopen(outpipe[0], "r");
		close(outpipe[1]);
		errorfile = fdopen(errpipe[0], "r");
		close(errpipe[1]);
		initialized = true;
		// Send input
		for (unsigned int i = 0; i < stdindata.size(); i++)
		{
			fputs(stdindata[i].c_str(), inputfile);
		}
		stdindata.clear();
		
		// Start thread
		usleep(100000);
		pthread_create(&thread, NULL, startProcessThread, this);
	}
	else
	{
		// This is the new process
		// Replace std* with pipes
		dup2(inpipe[0], 0);
		close(inpipe[1]);
		dup2(outpipe[1], 1);
		close(outpipe[0]);
		dup2(errpipe[1], 2);
		close(errpipe[0]);
		
		// Get arguments
		argarray = new char*[arguments.size() + 2];
		argarray[0] = strdup(filename.c_str());
		for (unsigned int i = 0; i < arguments.size(); i++)
		{
			argarray[i + 1] = strdup(arguments[i].c_str());
		}
		argarray[arguments.size() + 1] = 0;
		
		// Start program
		if(execv(filename.c_str(), argarray) == -1)
		{
			exit(1);
		}
	}
	return true;
}
bool Process::callSync(int *retval)
{
	if (!call())
	{
		return false;
	}
	join(retval);
	return true;
}

void Process::setFilename(std::string filename)
{
	this->filename = filename;
}
std::string Process::getFilename(void)
{
	return filename;
}

void Process::setArguments(std::vector<std::string> args)
{
	arguments = args;
}
std::vector<std::string> Process::getArguments(void)
{
	return arguments;
}
void Process::addArgument(std::string arg)
{
	arguments.push_back(arg);
}
void Process::clearArguments(void)
{
	arguments.clear();
}

bool Process::isRunning(void)
{
	return pid != 0;
}
void Process::kill(void)
{
	if (pid == 0)
	{
		return;
	}
	if (::kill(pid, SIGKILL) == -1)
	{
		printf("Could not send kill signal!\n");
		return;
	}
	join(0);
}
void Process::join(int *retval)
{
	if (pid != 0)
	{
		pthread_join(thread, NULL);
	}
	if (retval)
	{
		*retval = returnvalue;
	}
}

bool Process::stdin(std::string input)
{
	if (inputfile != 0)
	{
		// Write data to pipe
		fputs(input.c_str(), inputfile);
	}
	else
	{
		stdindata.push_back(input);
	}
	return true;
}
bool Process::stdout(std::string &output)
{
	if (stdoutdata.size() > 0)
	{
		output = stdoutdata[0];
		stdoutdata.erase(stdoutdata.begin());
		return true;
	}
	else
	{
		return false;
	}
}
bool Process::stderr(std::string &output)
{
	if (stderrdata.size() > 0)
	{
		output = stderrdata[0];
		stderrdata.erase(stderrdata.begin());
		return true;
	}
	else
	{
		return false;
	}
}

int Process::getReturnValue(void)
{
	return returnvalue;
}

void Process::callThread(void)
{
	fd_set outputset;
	FD_ZERO(&outputset);
	FD_SET(outpipe[0], &outputset);
	FD_SET(errpipe[0], &outputset);
	
	while (1)
	{
		// Read stdout
		int c;
		std::string data;
		while ((c = fgetc(outputfile)) > 0)
		{
			data += (char)c;
			if (c == '\n')
			{
				//printf("Got data from stdout: \"%s\".\n", data.c_str());
				stdoutdata.push_back(data);
				data = "";
			}
		}
		if (data.size() > 0)
		{
			//printf("Got data from stdout: \"%s\".\n", data.c_str());
			stdoutdata.push_back(data);
		}
		if (c == EOF)
		{
			break;
		}
		// Read stderr
		data = "";
		while ((c = fgetc(errorfile)) > 0)
		{
			data += (char)c;
			if (c == '\n')
			{
				//printf("Got data from stderr: \"%s\".\n", data.c_str());
				stderrdata.push_back(data);
				data = "";
			}
		}
		if (data.size() > 0)
		{
			//printf("Got data from stderr: \"%s\".\n", data.c_str());
			stderrdata.push_back(data);
		}
		if (c == EOF)
		{
			break;
		}
		// Wait for more data
		if (select(3, &outputset, NULL, NULL, 0) < 0)
		{
			break;
		}
	}
	// Clean everything up
	fclose(inputfile);
	inputfile = 0;
	fclose(outputfile);
	outputfile = 0;
	fclose(errorfile);
	errorfile = 0;
	if (argarray)
	{
		int i = 0;
		while (argarray[i])
		{
			delete argarray[i];
			i++;
		}
		delete argarray;
	}
}

