// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// 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

/** \file
		\brief Implements the renderjob application, which runs jobs scheduled with the virtual render farm
		\author Tim Shead (tshead@k-3d.com)
		\author Romain Behar (romainbehar@yahoo.com)
*/

#include <k3dsdk/log.h>
#include <k3dsdk/logbufs.h>

#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>

#include <iostream>
#include <vector>

namespace
{

typedef std::vector<std::string> string_array;

typedef boost::shared_ptr<std::streambuf> logbuf;
std::vector<logbuf> g_logging;

bool g_show_timestamps = true;
bool g_show_process = true;
bool g_syslog = false;
k3d::log_level_t g_minimum_log_level = k3d::DEBUG;

/////////////////////////////////////////////////////////////////////////////
// render_job

bool render_job(const boost::filesystem::path& GlobalOptionsPath, const boost::filesystem::path& JobDirectory)
{
	// Sanity checks ...	
	if(!boost::filesystem::exists(GlobalOptionsPath))
		{
			std::cerr << error << "Options file " << GlobalOptionsPath.native_file_string() << " does not exist" << std::endl;
			return false;
		}

	if(!boost::filesystem::exists(JobDirectory))
		{
			std::cerr << error << "Job directory " << JobDirectory.native_file_string() << " does not exist" << std::endl;
			return false;
		}

	if(!boost::filesystem::is_directory(JobDirectory))
		{
			std::cerr << error << "Job directory " << JobDirectory.native_file_string() << " is not a directory" << std::endl;
			return false;
		}
		
	// Skip the job if it's complete ...
	if(boost::filesystem::exists(JobDirectory / "complete"))
		return true;

	// Skip the job if it errored out ...
	if(boost::filesystem::exists(JobDirectory / "error"))
		return true;

	// Skip the job if it's running ...
	if(boost::filesystem::exists(JobDirectory / "running"))
		return true;

	// Make sure the job is ready ...
	if(!boost::filesystem::exists(JobDirectory / "ready"))
		{
			std::cerr << error << "Job " << JobDirectory.native_file_string() << " is not ready" << std::endl;
			return false;
		}

	// Standard logging ...
	std::cerr << info << "Starting Job " << JobDirectory.native_file_string() << std::endl;

	// Switch the job status to running ...
	boost::filesystem::rename(JobDirectory / "ready", JobDirectory / "running");

	// For each directory in the job directory (non-recursive) ...
	for(boost::filesystem::directory_iterator frame(JobDirectory); frame != boost::filesystem::directory_iterator(); ++frame)
		{
			if(!is_directory(*frame))
				continue;

			const std::string commandline("k3d-renderframe \"" + GlobalOptionsPath.native_file_string() + "\" \"" + frame->native_file_string() + "\"");
			system(commandline.c_str());
		}

	// Switch the job status to complete ...
	boost::filesystem::rename(JobDirectory / "running", JobDirectory / "complete");

	// Standard logging ...
	std::cerr << info << "Completed Job " << JobDirectory.native_file_string() << std::endl;

	return true;
}

//////////////////////////////////////////////////////////////////////////////////////////////
// usage

/// Prints usage info
void usage(const std::string& Name, std::ostream& Stream)
{
	Stream << "usage: " << Name << " [options]" << std::endl;
	Stream << "       " << Name << " [optionspath] [directory ...]" << std::endl;
	Stream << std::endl;
	Stream << "  -h, --help               prints this help information and exits" << std::endl;
	Stream << "      --version            prints program version information and exits" << std::endl;
	Stream << std::endl;
}

///////////////////////////////////////////////////////////////////////////////////////////////
// version

/// Prints version info
void print_version(std::ostream& Stream)
{
	Stream << "K-3D Version " << VERSION << std::endl;
	Stream << "Copyright (c) 1995-2004, Timothy M. Shead.  See the AUTHORS file for contributors." << std::endl;
	Stream << "Licensed by the GNU General Public License.  See the COPYING file for details." << std::endl;
	Stream << "K-3D Home Page: http://www.k-3d.org" << std::endl;
	Stream << std::endl;
}

/////////////////////////////////////////////////////////////////////////////
// setup_logging

/// Sets-up options for logging our output
void setup_logging(const std::string& ProcessName)
{
	g_logging.push_back(logbuf(new k3d::reset_level_buf(std::cerr)));
	
	if(g_show_timestamps)
		g_logging.push_back(logbuf(new k3d::timestamp_buf(std::cerr)));
	
	if(g_show_process)
		g_logging.push_back(logbuf(new k3d::tag_buf("[" + ProcessName + "]", std::cerr)));
	
	g_logging.push_back(logbuf(new k3d::show_level_buf(std::cerr)));
	
#ifndef	WIN32	
	if(g_syslog)
		g_logging.push_back(logbuf(new k3d::syslog_buf(std::cerr)));
#endif	//WIN32
		
	g_logging.push_back(logbuf(new k3d::filter_by_level_buf(g_minimum_log_level, std::cerr)));
}

} // namespace

/// Program main
int main(int argc, char* argv[])
{
	// Put our command-line arguments in a more useable form ...
	string_array options(&argv[1], &argv[argc]);

	// Print a "help" message ...
	if(std::count(options.begin(), options.end(), "-h") || std::count(options.begin(), options.end(), "--help"))
		{
			usage(argv[0], std::cout);
			return 0;
		}

	// Print version data ...
	if(std::count(options.begin(), options.end(), "--version"))
		{
			print_version(std::cout);
			return 0;
		}

	// Otherwise we should have a minimum of two arguments ...
	if(options.size() < 2)
		{
			usage(argv[0], std::cerr);
			return 1;
		}

	// Setup logging right away ...
	setup_logging(argv[0]);

	// Each remaining argument should be a job path to render ...
	int result = 0;
	for(unsigned long j = 1; j < options.size(); j++)
		{
			if(!render_job(
				boost::filesystem::path(options[0], boost::filesystem::native),
				boost::filesystem::path(options[j], boost::filesystem::native)))
				result = 1;
		}

	return result;
}


