// This file is part of the Coral project.
// Please read the copyright notice in the LICENSE file.

#include "compression_support.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>

#include <sstream>
#include <stdlib.h>
#include <coral/lib/filedescriptor.h>

#include <coral/lib/debugstream.h>

#include <iostream>

NAMESPACE_BEGIN(coral);
NAMESPACE_BEGIN(io);

CompressionSupport::CompressionSupport(const char* _filename) {
	childpid = -1;
	filename = _filename;
	if (filename.size() > 5 && filename.substr(filename.length()-5) == ".zuml") {
		fake = true;
		const char* arg_file = filename.c_str();
		coral::filedescriptor outputfd(this->getTempFile());
		if (outputfd.getFD() == -1) throw std::exception();
		pid_t f = fork();
		if (f == -1) throw std::exception();
		if (f > 0) {
			/*
			 * parent
			 */
			childpid = f;
			wait_for_child();
			return;
		}
		/*
		 * child
		 */
		const char* const argv[5] = { "unzip",
					      "-p",
					      arg_file,
					      "*.xmi",
					      NULL
		};
		int tries = 0;
		int w = outputfd.getFD();
		int err = 0;
		if (w != STDOUT_FILENO) {
			do {
				if (++tries > 10) _exit(1);
				// Duplicate to standard output fd
				err = dup2(w, STDOUT_FILENO);
			} while (err != STDOUT_FILENO && (err == EINTR || err == EBUSY));
			if (err != STDOUT_FILENO) _exit(1);
		}
		(void) close(STDIN_FILENO);
		err = execvp("unzip", const_cast<char* const*>(argv));
		_exit(1);
	} else {
		fake = false;
		loadable_filename = filename;
	}
}

int CompressionSupport::getTempFile() {
	loadable_filename = "/tmp";
	char* tmpdir = getenv("TMPDIR");
	if (tmpdir) {
		loadable_filename = tmpdir;
	}
	loadable_filename += "/.coral-tmp-for-poseidon-XXXXXX";
	return mkstemp(const_cast<char*>(loadable_filename.c_str()));
}

CompressionSupport::~CompressionSupport() throw () {
	wait_for_child();
	if (fake) {
		(void) remove(loadable_filename.c_str());
	}
}

void CompressionSupport::wait_for_child() {
	if (childpid != -1) {
		int status;
		int err = waitpid(childpid, &status, 0);
		if ((err == childpid)
		    && (WIFEXITED(status))
		    && (WEXITSTATUS(status) == 0)) {
			;
		} else {
			dlog << DEBUGLEVEL_DEBUG
			     << "DEBUG Error status " << status
			     << " in waitpid(), pid " << err << ", child should be " << childpid << "." << std::endl;
			throw std::exception();
		}
	}
	childpid = -1;
}

bool CompressionSupport::wasCompressed() throw () {
	return fake;
}

std::string CompressionSupport::getLoadableFilename() {
	return loadable_filename;
}

NAMESPACE_END();
NAMESPACE_END();

