/*
 * Copyright 2011, Blender Foundation.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __UTIL_PROGRESS_H__
#define __UTIL_PROGRESS_H__

/* Progress
 *
 * Simple class to communicate progress status messages, timing information,
 * update notifications from a job running in another thread. All methods
 * except for the constructor/destructor are thread safe. */

#include "util_function.h"
#include "util_string.h"
#include "util_thread.h"

CCL_NAMESPACE_BEGIN

class Progress {
public:
	Progress()
	{
		pass = 0;
		total_time = 0.0f;
		pass_time = 0.0f;
		status = "Initializing";
		substatus = "";
		update_cb = NULL;
		cancel = false;
		cancel_message = "";
		cancel_cb = NULL;
	}

	Progress(Progress& progress)
	{
		*this = progress;
	}

	Progress& operator=(Progress& progress)
	{
		thread_scoped_lock lock(progress.progress_mutex);

		progress.get_pass(pass, total_time, pass_time);
		progress.get_status(status, substatus);

		return *this;
	}

	/* cancel */
	void set_cancel(const string& cancel_message_)
	{
		thread_scoped_lock lock(progress_mutex);
		cancel_message = cancel_message_;
		cancel = true;
	}

	bool get_cancel()
	{
		if(!cancel && cancel_cb)
			cancel_cb();

		return cancel;
	}

	string get_cancel_message()
	{
		thread_scoped_lock lock(progress_mutex);
		return cancel_message;
	}

	void set_cancel_callback(boost::function<void(void)> function)
	{
		cancel_cb = function;
	}

	/* pass and timing information */

	void set_pass(int pass_, double total_time_, double pass_time_)
	{
		thread_scoped_lock lock(progress_mutex);

		pass = pass_;
		total_time = total_time_;
		pass_time = pass_time_;
	}

	void get_pass(int& pass_, double& total_time_, double& pass_time_)
	{
		thread_scoped_lock lock(progress_mutex);

		pass_ = pass;
		total_time_ = total_time;
		pass_time_ = pass_time;
	}

	/* status messages */

	void set_status(const string& status_, const string& substatus_ = "")
	{
		{
			thread_scoped_lock lock(progress_mutex);
			status = status_;
			substatus = substatus_;
		}

		set_update();
	}

	void set_substatus(const string& substatus_)
	{
		{
			thread_scoped_lock lock(progress_mutex);
			substatus = substatus_;
		}

		set_update();
	}

	void get_status(string& status_, string& substatus_)
	{
		thread_scoped_lock lock(progress_mutex);
		status_ = status;
		substatus_ = substatus;
	}

	/* callback */

	void set_update()
	{
		if(update_cb)
			update_cb();
	}

	void set_update_callback(boost::function<void(void)> function)
	{
		update_cb = function;
	}

protected:
	thread_mutex progress_mutex;
	boost::function<void(void)> update_cb;
	boost::function<void(void)> cancel_cb;

	int pass;

	double total_time;
	double pass_time;

	string status;
	string substatus;

	volatile bool cancel;
	string cancel_message;
};

CCL_NAMESPACE_END

#endif /* __UTIL_PROGRESS_H__ */

