// 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
		\author Tim Shead (tshead@k-3d.com)
*/

#include "button.h"
#include "color_chooser.h"
#include "gtkml.h"
#include "k3ddialog.h"

#include <k3dsdk/application.h>
#include <k3dsdk/state_change_set.h>
#include <k3dsdk/ioptions.h>
#include <k3dsdk/string_modifiers.h>

#include <sdpgtk/sdpgtkloopevents.h>
#include <sdpgtk/sdpgtkutility.h>

namespace k3d
{

namespace color_chooser
{

namespace detail
{

/////////////////////////////////////////////////////////////////////////////
// pick_color

const std::string control_colorchanged = "colorchanged";
const std::string control_colorselection = "colorselection";

/// Provides a UI for picking a color
class pick_color :
	public k3dDialog
{
	typedef k3dDialog base;

public:
	pick_color(k3d::iunknown* const ParentCommandNode, k3d::color_chooser::idata_proxy& Data, k3d::istate_recorder* const StateRecorder, const std::string StateChangeName) :
		base(ParentCommandNode, "setcolor", new k3d::options_window_geometry_store()),
		m_data(Data),
		m_state_recorder(StateRecorder),
		m_state_change_name(StateChangeName)
	{
		std::istringstream uitemplate(
			"<gtkml>"
				"<window type=\"toplevel\" show=\"false\">"
					"<vbox homogeneous=\"false\">"
						"<colorselection name=\"colorselection\">"
							"<event signal=\"color-changed\" name=\"colorchanged\"/>"
						"</colorselection>"
						"<hbuttonbox layout=\"end\">"
							"<k3dbutton name=\"close\">Close</k3dbutton>"
						"</hbuttonbox>"
					"</vbox>"
				"</window>"
			"</gtkml>");

		return_if_fail(load_gtkml(uitemplate, "pick color builtin template", *this));

		if(get_button("close"))
			get_button("close")->signal_activate().connect(SigC::slot(*this, &pick_color::on_close));

		// Get current color
		m_current_color = m_data.value();
		ColorSelection(control_colorselection).SetColor(m_current_color.red, m_current_color.green, m_current_color.blue);
		ColorSelection(control_colorselection).SetUpdatePolicy(GTK_UPDATE_DISCONTINUOUS);

/*
		// Set our title ...
		std::string roottitle = "Set [";
		roottitle += dynamic_cast<iobject*>(m_Channel)->name();
		roottitle += "] Color";
		RootWindow().SetTitle(roottitle.c_str());
*/

		// Make ourselves visible ...
		RootWidget().Show();
	}

	bool execute_command(const std::string& Command, const std::string& Arguments)
	{
		if(Command == control_colorchanged)
			{
				const k3d::color color(k3d::from_string<k3d::color>(Arguments, k3d::color(1.0, 1.0, 1.0)));

				ColorSelection(control_colorselection).InteractiveSetColor(color.red, color.green, color.blue, k3d::application().options().tutorial_speed(), true);
				on_color_changed(color);

				return true;
			}

		return base::execute_command(Command, Arguments);
	}

private:
	void OnEvent(sdpGtkEvent* Event)
	{
		if(Event->Name() == control_colorchanged)
			raw_on_color_changed();
		else
			base::OnEvent(Event);
	}

	void on_close()
	{
		base::OnClose();
	}

	void raw_on_color_changed()
	{
		k3d::color newcolor;
		ColorSelection(control_colorselection).GetColor(newcolor.red, newcolor.green, newcolor.blue);
		if(newcolor != m_current_color)
			{
				m_current_color = newcolor;
				on_color_changed(newcolor);
			}
	}

	void on_color_changed(const k3d::color NewColor)
	{
		k3d::record_command(*this, k3d::icommand_node::command_t::USER_INTERFACE, control_colorchanged, k3d::to_string(NewColor));
//		k3d::record_state_change_set undo(object->document(), "Set [" + object->name() + "] Color");

/*	
		// Turn this into an undo/redo -able event ...
		if(m_state_recorder)
			m_state_recorder->StartRecording(k3d::create_state_change_set());
*/

		// Update everything with the new value ...
		m_data.set_value(NewColor);

/*
		// Turn this into an undo/redo -able event ...
		if(m_state_recorder)
			m_state_recorder->CommitChangeSet(m_StateRecorder->StopRecording(), newvalue ? m_StateChangeName + " \"On\"" : m_StateChangeName + " \"Off\"");
*/
	}

	k3d::color_chooser::idata_proxy& m_data;
	k3d::istate_recorder* const m_state_recorder;
	const std::string m_state_change_name;
	
	k3d::color m_current_color;
};

const std::string control_edit = "edit";

} // namespace detail

/////////////////////////////////////////////////////////////////////////////
// control

control::control(k3d::iunknown* const CommandNodeParent, const std::string CommandNodeName) :
	base(CommandNodeParent, CommandNodeName)
{
	// Create and load our UI template ...
	std::istringstream uitemplate(
		"<gtkml>"
			"<eventbox>"
				"<event signal=\"destroy\" name=\"destroy\"/>"
				"<button name=\"button\">"
					"<event signal=\"clicked\" name=\"edit\"/>"
					"<eventbox name=\"value\"/>"
				"</button>"
			"</eventbox>"
		"</gtkml>\n");

	return_if_fail(load_gtkml(uitemplate, "color chooser builtin template", *this));

	RootWidget().Show();
}

control::~control()
{
	// No more events from this point forward ...
	DisconnectAllEvents();

	// Clean-up the GTK+ tree ...
	if(Root())
		RootWidget().Destroy();
}

const std::string control::CustomType() const
{
	return std::string("k3dcolorchooser");
}

bool control::Create(sdpGtkIObjectContainer* const ObjectContainer, sdpxml::Document& Document, sdpxml::Element& Element)
{
	// Sanity checks ...
	assert_warning(ObjectContainer);
	assert_warning(Element.Name() == "k3dcolorchooser");

	return true;
}

bool control::attach(std::auto_ptr<idata_proxy> Data, k3d::istate_recorder* const StateRecorder, const std::string StateChangeName)
{
	// Sanity checks ...
	return_val_if_fail(Data.get(), false);

	// Take ownership of the data source ...
	m_data = Data;

	// Complete our own initialization ...
	return_val_if_fail(base::Attach(StateRecorder, StateChangeName), false);

	// Update the display ...
	update();

	// We want to be notified if the data source changes ...
	m_data->changed_signal().connect(SigC::slot(*this, &control::update));

	return true;
}

bool control::execute_command(const std::string& Command, const std::string& Arguments)
{
	if(detail::control_edit == Command)
		{
			InteractiveActivateButton("button", k3d::application().options().tutorial_speed(), true);
			return true;
		}

	return base::execute_command(Command, Arguments);
}

void control::update()
{
	// Sanity checks ...
	return_if_fail(m_data.get());

	// Attach the OpenGL drawing area for previewing the color ...
	if(!m_value_widget.Attached())
		{
			sdpGtkContainer container(Container("value"));
			if(!m_value_widget.Create(container, true, true, 8, 8, 8, 0))
				if(!m_value_widget.Create(container, true, true, 5, 5, 5, 0))
					if(!m_value_widget.Create(container, false, false, 4, 4, 4, 0))
						std::cerr << __PRETTY_FUNCTION__ << ": Could not find useable OpenGL visual" << std::endl;

			if(m_value_widget.Initialized())
				{
					MapEvent("configure-event", "configurevalue", false, m_value_widget, true);
					MapEvent("expose-event", "exposevalue", false, m_value_widget, true);
				}
		}

	Widget("value").QueueDraw();
}

void control::OnEvent(sdpGtkEvent* Event)
{
	// Sanity checks ...
	assert_warning(Event);

	if(Event->Name() == "configurevalue" || Event->Name() == "exposevalue")
		on_draw_value();
	else if(Event->Name() == detail::control_edit)
		on_edit_value();
	else if(Event->Name() == "destroy")
		on_destroy();
	else
		base::OnEvent(Event);
}

void control::on_draw_value()
{
	// Sanity checks ...
	return_if_fail(m_data.get());

	m_value_widget.Begin();

	const unsigned long width = m_value_widget.Width();
	const unsigned long height = m_value_widget.Height();
	const k3d::color value = m_data->value();

	glViewport(0, 0, width, height);
	glClearColor(value.red, value.green, value.blue, 0.0);
	glClear(GL_COLOR_BUFFER_BIT);

	m_value_widget.SwapBuffers();
	m_value_widget.End();
}

void control::on_edit_value()
{
	// Make sure we've got some storage, first!
	return_if_fail(m_data.get());

	k3d::record_command(*this, k3d::icommand_node::command_t::USER_INTERFACE, detail::control_edit);

	new detail::pick_color(this, *m_data, m_StateRecorder, m_StateChangeName);
}

void control::on_destroy()
{
	DisconnectAllEvents();
	Clear();
}

} // namespace color_chooser

} // namespace k3d


