// 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 CRenderFarm class, which encapsulates a virtual render farm
		\author Tim Shead (tshead@k-3d.com)
*/

#include "shader_collection.h"

#include <k3dsdk/ioptions.h>
#include <k3dsdk/result.h>
#include <k3dsdk/string_modifiers.h>
#include <k3dsdk/system_functions.h>

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

#include <fstream>

namespace
{

/// Inserts shaders into a collection
class shader_functor
{
public:
	shader_functor(k3d::ioptions& Options, k3d::shader_collection::message_signal_t& MessageSignal, sdpsl::shaders_t& Shaders) :
		m_options(Options),
		m_message_signal(MessageSignal),
		m_shaders(Shaders)
	{
	}

	bool operator()(const boost::filesystem::path& SourcePath)
	{
		// Skip files that aren't shader source code ...
		if(k3d::file_extension(SourcePath) != "sl")
			return true;

		m_message_signal.emit("Loading shader " + SourcePath.leaf());

		// Look for an existing metafile ...
		const boost::filesystem::path metafile_path(SourcePath.native_file_string() + ".slmeta", boost::filesystem::native);
		if(exists(metafile_path))
			{
				boost::filesystem::ifstream metafile(metafile_path);
				const sdpsl::shaders_t shaders(sdpsl::parse_metafile(metafile, SourcePath.native_file_string(), metafile_path.native_file_string()));
				m_shaders.insert(m_shaders.end(), shaders.begin(), shaders.end());
			}
		// Didn't find it, so parse the source code ...
		else
			{
				// Get the user's preferred shading-language preprocessor ...
				std::string preprocessor_command = m_options.sl_preprocessor();
				k3d::formatted_replace(preprocessor_command, '%', "p", SourcePath.native_file_string());

				// Make it happen ...
				std::stringstream preprocessed_shader;
				return_val_if_fail(k3d::system::run_process(preprocessor_command, preprocessed_shader), true);
				const sdpsl::shaders_t shaders(sdpsl::parse_source(preprocessed_shader, SourcePath.native_file_string()));
				m_shaders.insert(m_shaders.end(), shaders.begin(), shaders.end());
			}

		return true;
	}

private:
	k3d::ioptions& m_options;
	k3d::shader_collection::message_signal_t& m_message_signal;
	sdpsl::shaders_t& m_shaders;
};

/// Loads shaders from a series of delimited paths
void load_shaders(k3d::ioptions& Options, k3d::shader_collection::message_signal_t& MessageSignal, sdpsl::shaders_t& Shaders, const std::string Paths)
{
	k3d::system::paths_t paths;
	k3d::system::decompose_path_list(Paths, paths);

	for(k3d::system::paths_t::const_iterator path = paths.begin(); path != paths.end(); ++path)
		{
			MessageSignal.emit("Loading shaders from " + path->native_file_string());

			shader_functor functor(Options, MessageSignal, Shaders);

			for(boost::filesystem::directory_iterator file(*path); file != boost::filesystem::directory_iterator(); ++file)
				{
					if(!boost::filesystem::is_directory(*file))
						functor(*file);
				}
		}
}

} // namespace

namespace k3d
{

/////////////////////////////////////////////////////////////////////////////
// shader_collection

shader_collection::shader_collection()
{
}

shader_collection::message_signal_t& shader_collection::message_signal()
{
	return m_message_signal;
}

void shader_collection::load_shaders(k3d::ioptions& Options, const std::string SourcePaths)
{
	::load_shaders(Options, m_message_signal, m_shaders, SourcePaths);
}

const sdpsl::shaders_t& shader_collection::shaders()
{
	return m_shaders;
}

} // namespace k3d


