/////////////////////////////////////////////////////////////////////////////
// Name:        base_widget_component.h
// Purpose:     
// Author:      César Mauri Loba
// Modified by: 
// Created:     26/05/2011 21:44:47
// RCS-ID:      
// Copyright:   (C) 2011 Cesar Mauri Loba - CREA Software Systems
// 
//  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 3 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, see <http://www.gnu.org/licenses/>.    
/////////////////////////////////////////////////////////////////////////////

#ifndef _base_widget_component_H_
#define _base_widget_component_H_

#include "spcore/component.h"
#include "spcore/pinimpl.h"
#include "spcore/basictypes.h"

#include <wx/thread.h>

namespace mod_widgets {

/**
	Common base class for all widget components
*/

template<class PANEL_CLASS, class DERIVED>
class BaseWidgetComponent : public spcore::CComponentAdapter {
public:
	//
	// Component methods
	//
	BaseWidgetComponent(const char * name, int argc, const char * argv[]) 
	: spcore::CComponentAdapter(name, argc, argv)
	, m_enabled(true)
	, m_panel(NULL)
	{
		//
		// Create pin
		//
		spcore::CComponentAdapter::RegisterInputPin(
			*SmartPtr<spcore::IInputPin>(new InputPinEnable(*this), false));
		
		//
		// Process arguments
		//
		std::string error_msg(name);
		if (argc) {
			for (int i= 0; i< argc; ++i) {
				if (argv[i] && strcmp ("-l", argv[i])== 0) {
					// Label
					++i;

					if (i== argc || argv[i]== NULL) {
						error_msg+= ". Missing value for -l argument.";
						throw std::runtime_error(error_msg.c_str());
					}
					m_label= argv[i];
					
					// Remove arguments
					argv[i-1]= NULL;
					argv[i]= NULL;
				}
				else if (argv[i] && strcmp ("-e", argv[i])== 0) {
					// Initially enabled
					++i;

					if (i== argc || argv[i]== NULL) {
						error_msg+= ". Missing value for -e argument.";
						throw std::runtime_error(error_msg.c_str());
					}

					if (argv[i][0]== '1' || strcmp(argv[i], "true"))
						m_enabled= true;
					else if (argv[i][0]== '0' || strcmp(argv[i], "false"))
						m_enabled= false;
					else {
						error_msg+= ". Wrong value for -e argument. Syntax: -e [0|1|false|true].";
						throw std::runtime_error(error_msg.c_str());
					}
					
					// Remove arguments
					argv[i-1]= NULL;
					argv[i]= NULL;
				}
			}			
		}
	}
	
	virtual int DoInitialize()= 0;

	virtual wxWindow* GetGUI(wxWindow * parent) {
		assert (wxIsMainThread());
		if (this->m_panel) {
			// Already created
			assert (false);
			spcore::getSpCoreRuntime()->LogMessage(spcore::ICoreRuntime::LOG_ERROR, "panel already open", 
				this->GetTypeName());
			return NULL;
		}
		else {
			this->m_panel= new PANEL_CLASS();
			this->m_panel->SetComponent(static_cast<DERIVED*>(this));
			this->m_panel->Create(parent);
		}

		return m_panel;
	}

	// 
	// Called from GUI
	//
	bool GetEnabled() const { return this->m_enabled; }
	const std::string& GetLabel() const { return this->m_label; }	
	void OnPanelDestroyed () {
		assert (wxIsMainThread());
		this->m_panel= NULL;
	}

private:
	//
	// Data members
	//
	bool m_enabled;
protected:
	PANEL_CLASS* m_panel;
private:
	std::string m_label;

protected:
	//
	// Private methods
	//
	virtual ~BaseWidgetComponent() {
		assert (wxIsMainThread());
		if (this->m_panel) {
			this->m_panel->SetComponent(NULL);
			this->m_panel->Close();
			this->m_panel= NULL;
		}
	}

private:	
	void OnPinEnable (const spcore::CTypeBool & msg) {
		assert (wxIsMainThread());
		if (wxIsMainThread()) {
			if (m_panel) m_panel->Enable(msg.getValue());
		}
		else {
			spcore::getSpCoreRuntime()->LogMessage(spcore::ICoreRuntime::LOG_ERROR, 
				"Pin \"enable\" can only receive messages from the main thread.", GetTypeName());
		}
	}

	//
	// Input pin classes
	//
	class InputPinEnable
		: public spcore::CInputPinWriteOnly<spcore::CTypeBool, BaseWidgetComponent> {
	public:
		InputPinEnable (BaseWidgetComponent & component)
		: spcore::CInputPinWriteOnly<spcore::CTypeBool, BaseWidgetComponent>("enable", component) { }

		virtual int DoSend(const spcore::CTypeBool & msg) {
			this->m_component->OnPinEnable(msg);
			return 0;
		}
	};
};

/**
	Additional base class for all widget panel
*/
template<class COMPONENT_CLASS>
class BaseWidgetPanel {
public:
	// Constructor
	BaseWidgetPanel() : m_component(NULL) {}

	// Destructor
	virtual ~BaseWidgetPanel() {
		if (m_component!= NULL) {
			m_component->OnPanelDestroyed();
			m_component= NULL;
		}
	}

	// Set destruction callback. NULL removes.
	void SetComponent(COMPONENT_CLASS* component) { m_component= component; }

protected:
	COMPONENT_CLASS* m_component;
};

};	// namespace

#endif

