/*************************************************************************/
/*  call_dialog.cpp                                                      */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur.                 */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#include "call_dialog.h"

#include "scene/gui/label.h"
#include "object_type_db.h"
#include "print_string.h"


class CallDialogParams : public Object {

	OBJ_TYPE( CallDialogParams, Object );
public:

	bool _set(const StringName& p_name, const Variant& p_value) {
	
		values[p_name]=p_value;
                return true;
	}
	
	bool _get(const StringName& p_name,Variant &r_ret) const {
	
            if (values.has(p_name)) {
                        r_ret=values[p_name];
                        return true;
                    }
                return false;
	}
	
	void _get_property_list( List<PropertyInfo> *p_list) const {
	
		for(int i=0;i<method.arguments.size();i++)
			p_list->push_back(method.arguments[i]);
	}

	MethodInfo method;
	HashMap<String,Variant> values;

	CallDialogParams() {}
};


void CallDialog::_notification(int p_what) {
	
	if (p_what==NOTIFICATION_READY) {
		
		call->connect("pressed", this,"_call");
		cancel->connect("pressed", this,"_cancel");
		//filter->get_path()->connect("text_changed", this,"_text_changed");
		_update_method_list();
	}

	if (p_what==NOTIFICATION_EXIT_TREE) {

		call->disconnect("pressed", this,"_call");
		cancel->disconnect("pressed", this,"_cancel");

		//filter->get_path()->connect("text_changed", this,"_text_changed");
		_update_method_list();
	}

	if (p_what==NOTIFICATION_DRAW) {
		
		RID ci = get_canvas_item();
		get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size()));		
	}	
}


void CallDialog::_call() {
	
	if (!tree->get_selected())
		return;
		
	TreeItem* item=tree->get_selected();
	ERR_FAIL_COND(!item);
	int idx=item->get_metadata(0);
	ERR_FAIL_INDEX(idx,methods.size());
	MethodInfo &m = methods[idx];
	
	Variant args[VARIANT_ARG_MAX];
	
	for(int i=0;i<VARIANT_ARG_MAX;i++) {
	
		if (i>=m.arguments.size())
			continue;
		
		if (call_params->values.has(m.arguments[i].name))
			args[i]=call_params->values[m.arguments[i].name];
	}
	
	Variant ret = object->call(m.name,args[0],args[1],args[2],args[3],args[4]);
	if (ret.get_type()!=Variant::NIL)
		return_value->set_text(ret);
	else
		return_value->set_text("");
}

void CallDialog::_cancel() {
	
	hide();
}


void CallDialog::_item_selected() {

	TreeItem* item=tree->get_selected();
	ERR_FAIL_COND(!item);
	
	if (item->get_metadata(0).get_type()==Variant::NIL) {
	
		call->set_disabled(true);
		return;
	}
	
	call->set_disabled(false);
	
	int idx=item->get_metadata(0);
	ERR_FAIL_INDEX(idx,methods.size());
	
	MethodInfo &m = methods[idx];

	call_params->values.clear();
	call_params->method=m;
	
	property_editor->edit(call_params);
	property_editor->update_tree();


}

void CallDialog::_update_method_list() {
	
	tree->clear();
	if (!object)
		return;
		
	TreeItem *root = tree->create_item();
	
	List<MethodInfo> method_list;
	object->get_method_list(&method_list);
	method_list.sort();
	methods.clear();
	
	List<String> inheritance_list;
	
	String type = object->get_type();
	
	while(type!="") {		
		inheritance_list.push_back( type );
		type=ObjectTypeDB::type_inherits_from(type);
	}
	
	TreeItem *selected_item=NULL;
	
	for(int i=0;i<inheritance_list.size();i++) {
	
		String type=inheritance_list[i];
		String parent_type=ObjectTypeDB::type_inherits_from(type);

		TreeItem *type_item=NULL;
	
		List<MethodInfo>::Element *N,*E=method_list.front();
		
		while(E) {

			N=E->next();

			if (parent_type!="" && ObjectTypeDB::get_method(parent_type,E->get().name)!=NULL) {
				E=N;
				continue;
			}
				
			if (!type_item) {
				type_item=tree->create_item(root);
				type_item->set_text(0,type);
				if (has_icon(type,"EditorIcons"))
					type_item->set_icon(0,get_icon(type,"EditorIcons"));
			}
				
			TreeItem *method_item = tree->create_item(type_item);
			method_item->set_text(0,E->get().name);
			method_item->set_metadata(0,methods.size());
			if (E->get().name==selected)
				selected_item=method_item;
			methods.push_back( E->get() );
			
			method_list.erase(E);
			E=N;
		}
	}
	
	
	
	if (selected_item)
		selected_item->select(0);
}

void CallDialog::_bind_methods() {
	
	ObjectTypeDB::bind_method("_call",&CallDialog::_call);
	ObjectTypeDB::bind_method("_cancel",&CallDialog::_cancel);
	ObjectTypeDB::bind_method("_item_selected", &CallDialog::_item_selected);
	
}

void CallDialog::set_object(Object *p_object,StringName p_selected) {

	object=p_object;
	selected=p_selected;
	property_editor->edit(NULL);
	call->set_disabled(true);
	return_value->clear();

	_update_method_list();
	method_label->set_text("Method List For ' "+p_object->get_type()+" ':");
}

CallDialog::CallDialog() {

	object=NULL;

	call = memnew( Button );
	call->set_anchor( MARGIN_LEFT, ANCHOR_END );
	call->set_anchor( MARGIN_TOP, ANCHOR_END );
	call->set_anchor( MARGIN_RIGHT, ANCHOR_END );
	call->set_anchor( MARGIN_BOTTOM, ANCHOR_END );
	call->set_begin( Point2( 70, 29 ) );
	call->set_end( Point2( 15, 15 ) );	
	call->set_text("Call");
	
	add_child(call);
	
	cancel = memnew( Button );
	cancel->set_anchor( MARGIN_TOP, ANCHOR_END );
	cancel->set_anchor( MARGIN_BOTTOM, ANCHOR_END );
	cancel->set_begin( Point2( 15, 29 ) );
	cancel->set_end( Point2( 70, 15 ) );	
	cancel->set_text("Close");
	
	add_child(cancel);
	
	tree = memnew( Tree );
	
	tree->set_anchor( MARGIN_RIGHT, ANCHOR_RATIO );
	tree->set_anchor( MARGIN_BOTTOM, ANCHOR_END );
	tree->set_begin( Point2( 20,50 ) );
	tree->set_margin(MARGIN_BOTTOM, 44 );
	tree->set_margin(MARGIN_RIGHT, 0.5 );
	tree->set_select_mode( Tree::SELECT_ROW );
	add_child(tree);
	
	tree->connect("item_selected", this,"_item_selected");
	tree->set_hide_root(true);
	
	property_editor = memnew( PropertyEditor );
	
	property_editor->set_anchor_and_margin( MARGIN_RIGHT, ANCHOR_END, 15 );
	property_editor->set_anchor_and_margin( MARGIN_TOP, ANCHOR_BEGIN, 50 );
	property_editor->set_anchor_and_margin( MARGIN_LEFT, ANCHOR_RATIO, 0.55 );
	property_editor->set_anchor_and_margin( MARGIN_BOTTOM, ANCHOR_END, 90 );
	property_editor->get_scene_tree()->set_hide_root( true );
	property_editor->hide_top_label();
	
	add_child(property_editor);
	method_label = memnew( Label );
	method_label->set_pos( Point2( 15,25) );
	method_label->set_text("Method List:");
	
	add_child(method_label);
	
	Label *label = memnew( Label );
	label->set_anchor_and_margin( MARGIN_LEFT, ANCHOR_RATIO, 0.53 );
	label->set_anchor_and_margin( MARGIN_TOP, ANCHOR_BEGIN, 25 );
	label->set_text("Arguments:");
	
	add_child(label);
	
	return_label = memnew( Label );
	return_label->set_anchor_and_margin( MARGIN_LEFT, ANCHOR_RATIO, 0.53 );
	return_label->set_anchor_and_margin( MARGIN_TOP, ANCHOR_END, 85 );
	return_label->set_text("Return:");
	
	add_child(return_label);
	
	return_value = memnew( LineEdit );
	return_value->set_anchor_and_margin( MARGIN_LEFT, ANCHOR_RATIO, 0.55 );
	return_value->set_anchor_and_margin( MARGIN_RIGHT, ANCHOR_END, 15 );	
	return_value->set_anchor_and_margin( MARGIN_TOP, ANCHOR_END, 65 );
	
	add_child(return_value);
	
	/*
	label = memnew( Label );
	label->set_anchor( MARGIN_TOP, ANCHOR_END );
	label->set_anchor( MARGIN_BOTTOM, ANCHOR_END );
	
	label->set_begin( Point2( 15,54) );
	label->set_end( Point2( 16,44) );
	label->set_text("Parameters:");
	
	add_child(label);
	*/

	
	call_params = memnew( CallDialogParams );
	set_as_toplevel(true);
}


CallDialog::~CallDialog()
{
	memdelete(call_params);
}