#include "visual_script_flow_control.h"
#include "os/keyboard.h"
#include "globals.h"


//////////////////////////////////////////
////////////////RETURN////////////////////
//////////////////////////////////////////

int VisualScriptReturn::get_output_sequence_port_count() const {

	return 0;
}

bool VisualScriptReturn::has_input_sequence_port() const{

	return true;
}

int VisualScriptReturn::get_input_value_port_count() const{

	return with_value?1:0;
}
int VisualScriptReturn::get_output_value_port_count() const{

	return 0;
}

String VisualScriptReturn::get_output_sequence_port_text(int p_port) const {

	return String();
}

PropertyInfo VisualScriptReturn::get_input_value_port_info(int p_idx) const{

	PropertyInfo pinfo;
	pinfo.name="result";
	pinfo.type=type;
	return pinfo;
}
PropertyInfo VisualScriptReturn::get_output_value_port_info(int p_idx) const{
	return PropertyInfo();
}

String VisualScriptReturn::get_caption() const {

	return "Return";
}

String VisualScriptReturn::get_text() const {

	return get_name();
}

void VisualScriptReturn::set_return_type(Variant::Type p_type) {

	if (type==p_type)
		return;
	type=p_type;
	ports_changed_notify();

}

Variant::Type VisualScriptReturn::get_return_type() const{

	return type;
}

void VisualScriptReturn::set_enable_return_value(bool p_enable) {
	if (with_value==p_enable)
		return;

	with_value=p_enable;
	ports_changed_notify();
}

bool VisualScriptReturn::is_return_value_enabled() const {

	return with_value;
}

void VisualScriptReturn::_bind_methods() {

	ObjectTypeDB::bind_method(_MD("set_return_type","type"),&VisualScriptReturn::set_return_type);
	ObjectTypeDB::bind_method(_MD("get_return_type"),&VisualScriptReturn::get_return_type);
	ObjectTypeDB::bind_method(_MD("set_enable_return_value","enable"),&VisualScriptReturn::set_enable_return_value);
	ObjectTypeDB::bind_method(_MD("is_return_value_enabled"),&VisualScriptReturn::is_return_value_enabled);

	String argt="Any";
	for(int i=1;i<Variant::VARIANT_MAX;i++) {
		argt+=","+Variant::get_type_name(Variant::Type(i));
	}

	ADD_PROPERTY(PropertyInfo(Variant::BOOL,"return_value/enabled"),_SCS("set_enable_return_value"),_SCS("is_return_value_enabled"));
	ADD_PROPERTY(PropertyInfo(Variant::INT,"return_value/type",PROPERTY_HINT_ENUM,argt),_SCS("set_return_type"),_SCS("get_return_type"));

}

class VisualScriptNodeInstanceReturn : public VisualScriptNodeInstance {
public:

	VisualScriptReturn *node;
	VisualScriptInstance *instance;
	bool with_value;

	virtual int get_working_memory_size() const { return 1; }
	//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
	//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }

	virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {

		if (with_value) {
			*p_working_mem = *p_inputs[0];
		} else {
			*p_working_mem = Variant();
		}

		return 0;
	}


};

VisualScriptNodeInstance* VisualScriptReturn::instance(VisualScriptInstance* p_instance) {

	VisualScriptNodeInstanceReturn * instance = memnew(VisualScriptNodeInstanceReturn );
	instance->node=this;
	instance->instance=p_instance;
	instance->with_value=with_value;
	return instance;
}

VisualScriptReturn::VisualScriptReturn() {

	with_value=false;
	type=Variant::NIL;
}

template<bool with_value>
static Ref<VisualScriptNode> create_return_node(const String& p_name) {

	Ref<VisualScriptReturn> node;
	node.instance();
	node->set_enable_return_value(with_value);
	return node;
}



//////////////////////////////////////////
////////////////CONDITION/////////////////
//////////////////////////////////////////

int VisualScriptCondition::get_output_sequence_port_count() const {

	return 3;
}

bool VisualScriptCondition::has_input_sequence_port() const{

	return true;
}

int VisualScriptCondition::get_input_value_port_count() const{

	return 1;
}
int VisualScriptCondition::get_output_value_port_count() const{

	return 0;
}

String VisualScriptCondition::get_output_sequence_port_text(int p_port) const {

	if (p_port==0)
		return "true";
	else if (p_port==1)
		return "false";
	else
		return "done";
}

PropertyInfo VisualScriptCondition::get_input_value_port_info(int p_idx) const{

	PropertyInfo pinfo;
	pinfo.name="cond";
	pinfo.type=Variant::BOOL;
	return pinfo;
}
PropertyInfo VisualScriptCondition::get_output_value_port_info(int p_idx) const{
	return PropertyInfo();
}

String VisualScriptCondition::get_caption() const {

	return "Condition";
}

String VisualScriptCondition::get_text() const {

	return "if (cond) is:  ";
}


void VisualScriptCondition::_bind_methods() {



}

class VisualScriptNodeInstanceCondition : public VisualScriptNodeInstance {
public:

	VisualScriptCondition *node;
	VisualScriptInstance *instance;

	//virtual int get_working_memory_size() const { return 1; }
	//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
	//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }

	virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {

		if (p_start_mode==START_MODE_CONTINUE_SEQUENCE)
			return 2;
		else if (p_inputs[0]->operator bool())
			return 0 | STEP_FLAG_PUSH_STACK_BIT;
		else
			return 1 | STEP_FLAG_PUSH_STACK_BIT;
	}


};

VisualScriptNodeInstance* VisualScriptCondition::instance(VisualScriptInstance* p_instance) {

	VisualScriptNodeInstanceCondition * instance = memnew(VisualScriptNodeInstanceCondition );
	instance->node=this;
	instance->instance=p_instance;
	return instance;
}

VisualScriptCondition::VisualScriptCondition() {

}



//////////////////////////////////////////
////////////////WHILE/////////////////
//////////////////////////////////////////

int VisualScriptWhile::get_output_sequence_port_count() const {

	return 2;
}

bool VisualScriptWhile::has_input_sequence_port() const{

	return true;
}

int VisualScriptWhile::get_input_value_port_count() const{

	return 1;
}
int VisualScriptWhile::get_output_value_port_count() const{

	return 0;
}

String VisualScriptWhile::get_output_sequence_port_text(int p_port) const {

	if (p_port==0)
		return "repeat";
	else
		return "exit";
}

PropertyInfo VisualScriptWhile::get_input_value_port_info(int p_idx) const{

	PropertyInfo pinfo;
	pinfo.name="cond";
	pinfo.type=Variant::BOOL;
	return pinfo;
}
PropertyInfo VisualScriptWhile::get_output_value_port_info(int p_idx) const{
	return PropertyInfo();
}

String VisualScriptWhile::get_caption() const {

	return "While";
}

String VisualScriptWhile::get_text() const {

	return "while (cond): ";
}


void VisualScriptWhile::_bind_methods() {



}

class VisualScriptNodeInstanceWhile : public VisualScriptNodeInstance {
public:

	VisualScriptWhile *node;
	VisualScriptInstance *instance;

	//virtual int get_working_memory_size() const { return 1; }
	//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
	//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }

	virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {

		bool keep_going = p_inputs[0]->operator bool();

		if (keep_going)
			return 0|STEP_FLAG_PUSH_STACK_BIT;
		else
			return 1;
	}


};

VisualScriptNodeInstance* VisualScriptWhile::instance(VisualScriptInstance* p_instance) {

	VisualScriptNodeInstanceWhile * instance = memnew(VisualScriptNodeInstanceWhile );
	instance->node=this;
	instance->instance=p_instance;
	return instance;
}
VisualScriptWhile::VisualScriptWhile() {

}



//////////////////////////////////////////
////////////////ITERATOR/////////////////
//////////////////////////////////////////

int VisualScriptIterator::get_output_sequence_port_count() const {

	return 2;
}

bool VisualScriptIterator::has_input_sequence_port() const{

	return true;
}

int VisualScriptIterator::get_input_value_port_count() const{

	return 1;
}
int VisualScriptIterator::get_output_value_port_count() const{

	return 1;
}

String VisualScriptIterator::get_output_sequence_port_text(int p_port) const {

	if (p_port==0)
		return "each";
	else
		return "exit";
}

PropertyInfo VisualScriptIterator::get_input_value_port_info(int p_idx) const{

	PropertyInfo pinfo;
	pinfo.name="input";
	pinfo.type=Variant::NIL;
	return pinfo;
}
PropertyInfo VisualScriptIterator::get_output_value_port_info(int p_idx) const{
	PropertyInfo pinfo;
	pinfo.name="elem";
	pinfo.type=Variant::NIL;
	return pinfo;
}
String VisualScriptIterator::get_caption() const {

	return "Iterator";
}

String VisualScriptIterator::get_text() const {

	return "for (elem) in (input): ";
}


void VisualScriptIterator::_bind_methods() {



}

class VisualScriptNodeInstanceIterator : public VisualScriptNodeInstance {
public:

	VisualScriptIterator *node;
	VisualScriptInstance *instance;

	virtual int get_working_memory_size() const { return 2; }
	//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
	//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }

	virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {

		if (p_start_mode==START_MODE_BEGIN_SEQUENCE) {
			p_working_mem[0]=*p_inputs[0];
			bool valid;
			bool can_iter = p_inputs[0]->iter_init(p_working_mem[1],valid);

			if (!valid) {
				r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
				r_error_str=RTR("Input type not iterable: ")+Variant::get_type_name(p_inputs[0]->get_type());
				return 0;
			}

			if (!can_iter)
				return 1; //nothing to iterate


			*p_outputs[0]=p_working_mem[0].iter_get( p_working_mem[1],valid);

			if (!valid) {
				r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
				r_error_str=RTR("Iterator became invalid");
				return 0;
			}


		} else { //continue sequence

			bool valid;
			bool can_iter = p_working_mem[0].iter_next(p_working_mem[1],valid);

			if (!valid) {
				r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
				r_error_str=RTR("Iterator became invalid: ")+Variant::get_type_name(p_inputs[0]->get_type());
				return 0;
			}

			if (!can_iter)
				return 1; //nothing to iterate


			*p_outputs[0]=p_working_mem[0].iter_get( p_working_mem[1],valid);

			if (!valid) {
				r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
				r_error_str=RTR("Iterator became invalid");
				return 0;
			}

		}

		return 0|STEP_FLAG_PUSH_STACK_BIT; //go around
	}


};

VisualScriptNodeInstance* VisualScriptIterator::instance(VisualScriptInstance* p_instance) {

	VisualScriptNodeInstanceIterator * instance = memnew(VisualScriptNodeInstanceIterator );
	instance->node=this;
	instance->instance=p_instance;
	return instance;
}

VisualScriptIterator::VisualScriptIterator() {

}



//////////////////////////////////////////
////////////////SEQUENCE/////////////////
//////////////////////////////////////////

int VisualScriptSequence::get_output_sequence_port_count() const {

	return steps;
}

bool VisualScriptSequence::has_input_sequence_port() const{

	return true;
}

int VisualScriptSequence::get_input_value_port_count() const{

	return 0;
}
int VisualScriptSequence::get_output_value_port_count() const{

	return 1;
}

String VisualScriptSequence::get_output_sequence_port_text(int p_port) const {

	return itos(p_port+1);
}

PropertyInfo VisualScriptSequence::get_input_value_port_info(int p_idx) const{
	return PropertyInfo();
}
PropertyInfo VisualScriptSequence::get_output_value_port_info(int p_idx) const{
	return PropertyInfo(Variant::INT,"current");
}
String VisualScriptSequence::get_caption() const {

	return "Sequence";
}

String VisualScriptSequence::get_text() const {

	return "in order: ";
}

void VisualScriptSequence::set_steps(int p_steps) {

	ERR_FAIL_COND(p_steps<1);
	if (steps==p_steps)
		return;

	steps=p_steps;
	ports_changed_notify();

}

int VisualScriptSequence::get_steps() const {

	return steps;
}

void VisualScriptSequence::_bind_methods() {

	ObjectTypeDB::bind_method(_MD("set_steps","steps"),&VisualScriptSequence::set_steps);
	ObjectTypeDB::bind_method(_MD("get_steps"),&VisualScriptSequence::get_steps);

	ADD_PROPERTY(PropertyInfo(Variant::INT,"steps",PROPERTY_HINT_RANGE,"1,64,1"),_SCS("set_steps"),_SCS("get_steps"));

}

class VisualScriptNodeInstanceSequence : public VisualScriptNodeInstance {
public:

	VisualScriptSequence *node;
	VisualScriptInstance *instance;
	int steps;

	virtual int get_working_memory_size() const { return 1; }
	//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
	//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }

	virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {

		if (p_start_mode==START_MODE_BEGIN_SEQUENCE) {

			p_working_mem[0]=0;
		}

		int step = p_working_mem[0];

		*p_outputs[0]=step;

		if (step+1==steps)
			return step;
		else {
			p_working_mem[0]=step+1;
			return step|STEP_FLAG_PUSH_STACK_BIT;
		}

	}


};

VisualScriptNodeInstance* VisualScriptSequence::instance(VisualScriptInstance* p_instance) {

	VisualScriptNodeInstanceSequence * instance = memnew(VisualScriptNodeInstanceSequence );
	instance->node=this;
	instance->instance=p_instance;
	instance->steps=steps;
	return instance;
}
VisualScriptSequence::VisualScriptSequence() {

	steps=1;
}


//////////////////////////////////////////
////////////////EVENT TYPE FILTER///////////
//////////////////////////////////////////

int VisualScriptSwitch::get_output_sequence_port_count() const {

	return case_values.size()+1;
}

bool VisualScriptSwitch::has_input_sequence_port() const{

	return true;
}

int VisualScriptSwitch::get_input_value_port_count() const{


	return 1;
}
int VisualScriptSwitch::get_output_value_port_count() const{

	return 0;
}

String VisualScriptSwitch::get_output_sequence_port_text(int p_port) const {

	if (p_port==case_values.size())
		return "done";

	if (case_values[p_port].value.get_type()==Variant::NIL)
		return "null";
	return case_values[p_port].value;
}

PropertyInfo VisualScriptSwitch::get_input_value_port_info(int p_idx) const{

	return PropertyInfo(Variant::NIL,"input");
}

PropertyInfo VisualScriptSwitch::get_output_value_port_info(int p_idx) const{

	return PropertyInfo();
}


String VisualScriptSwitch::get_caption() const {

	return "Switch";
}

String VisualScriptSwitch::get_text() const {

	return "'input' is:";
}


class VisualScriptNodeInstanceSwitch : public VisualScriptNodeInstance {
public:

	VisualScriptInstance* instance;
	Vector<Variant> case_values;

	//virtual int get_working_memory_size() const { return 0; }
	//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
	//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return false; }

	virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {

		if (p_start_mode==START_MODE_CONTINUE_SEQUENCE) {
			return case_values.size(); //exit
		}

		for(int i=0;i<case_values.size();i++) {

			if (*p_inputs[0]==case_values[i]) {
				return i|STEP_FLAG_PUSH_STACK_BIT;
			}
		}

		return case_values.size();
	}


};

VisualScriptNodeInstance* VisualScriptSwitch::instance(VisualScriptInstance* p_instance) {

	VisualScriptNodeInstanceSwitch * instance = memnew(VisualScriptNodeInstanceSwitch );
	instance->instance=p_instance;
	instance->case_values.resize(case_values.size());
	for(int i=0;i<case_values.size();i++) {
		instance->case_values[i]=case_values[i].value;
	}
	return instance;
}

bool VisualScriptSwitch::_set(const StringName& p_name, const Variant& p_value) {

	if (String(p_name)=="case_count") {
		case_values.resize(p_value);
		_change_notify();
		ports_changed_notify();
		return true;
	}

	if (String(p_name).begins_with("case/")) {

		int idx = String(p_name).get_slice("/",1).to_int();
		ERR_FAIL_INDEX_V(idx,case_values.size(),false);
		String what = String(p_name).get_slice("/",2);

		if (what=="type") {
			case_values[idx].type=Variant::Type(int(p_value));
			Variant::CallError ce;
			case_values[idx].value=Variant::construct(case_values[idx].type,NULL,0,ce);
			_change_notify();
			ports_changed_notify();

			return true;
		}

		if (what=="value") {
			case_values[idx].value=p_value;
			ports_changed_notify();
			return true;
		}
	}

	return false;
}

bool VisualScriptSwitch::_get(const StringName& p_name,Variant &r_ret) const {

	if (String(p_name)=="case_count") {
		r_ret=case_values.size();
		return true;
	}

	if (String(p_name).begins_with("case/")) {

		int idx = String(p_name).get_slice("/",1).to_int();
		ERR_FAIL_INDEX_V(idx,case_values.size(),false);
		String what = String(p_name).get_slice("/",2);

		if (what=="type") {
			r_ret=case_values[idx].type;
			return true;
		}

		if (what=="value") {
			r_ret=case_values[idx].value;
			return true;
		}
	}

	return false;

}
void VisualScriptSwitch::_get_property_list( List<PropertyInfo> *p_list) const {

	p_list->push_back(PropertyInfo(Variant::INT,"case_count",PROPERTY_HINT_RANGE,"0,128"));

	String argt="Any";
	for(int i=1;i<Variant::VARIANT_MAX;i++) {
		argt+=","+Variant::get_type_name(Variant::Type(i));
	}

	for(int i=0;i<case_values.size();i++) {
		p_list->push_back(PropertyInfo(Variant::INT,"case/"+itos(i)+"/type",PROPERTY_HINT_ENUM,argt));
		p_list->push_back(PropertyInfo(case_values[i].type,"case/"+itos(i)+"/value"));
	}
}


void VisualScriptSwitch::_bind_methods() {


}

VisualScriptSwitch::VisualScriptSwitch() {


}

//////////////////////////////////////////
////////////////EVENT ACTION FILTER///////////
//////////////////////////////////////////


int VisualScriptInputFilter::get_output_sequence_port_count() const {

	return filters.size();
}

bool VisualScriptInputFilter::has_input_sequence_port() const{

	return true;
}

int VisualScriptInputFilter::get_input_value_port_count() const{


	return 1;
}
int VisualScriptInputFilter::get_output_value_port_count() const{

	return 1;
}

String VisualScriptInputFilter::get_output_sequence_port_text(int p_port) const {

	String text;

	switch(filters[p_port].type) {
		case InputEvent::NONE: {
			text="None";
		} break;
		case InputEvent::KEY: {

			InputEventKey k = filters[p_port].key;

			if (k.scancode==0 && k.unicode==0) {
				text="No Key";
			} else {
				if (k.scancode!=0) {
					text="KeyCode: "+keycode_get_string(k.scancode);
				} else if (k.unicode!=0) {
					text="Uniode: "+String::chr(k.unicode);
				}

				if (k.pressed)
					text+=", Pressed";
				else
					text+=", Released";

				if (k.echo)
					text+=", Echo";
				if (k.mod.alt)
					text="Alt+"+text;
				if (k.mod.shift)
					text="Shift+"+text;
				if (k.mod.control)
					text="Ctrl+"+text;
				if (k.mod.meta)
					text="Meta+"+text;
			}

		} break;
		case InputEvent::MOUSE_MOTION: {
			InputEventMouseMotion mm = filters[p_port].mouse_motion;
			text="Mouse Motion";

			String b = "Left,Right,Middle,WheelUp,WheelDown,WheelLeft,WheelRight";

			for(int i=0;i<7;i++) {
				if (mm.button_mask&(1<<i)) {
					text=b.get_slice(",",i)+"+"+text;
				}
			}
			if (mm.mod.alt)
				text="Alt+"+text;
			if (mm.mod.shift)
				text="Shift+"+text;
			if (mm.mod.control)
				text="Ctrl+"+text;
			if (mm.mod.meta)
				text="Meta+"+text;
		} break;
		case InputEvent::MOUSE_BUTTON: {

			InputEventMouseButton mb = filters[p_port].mouse_button;

			String b = "Any,Left,Right,Middle,WheelUp,WheelDown,WheelLeft,WheelRight";

			text=b.get_slice(",",mb.button_index)+" Mouse Button";

			if (mb.pressed)
				text+=", Pressed";
			else
				text+=", Released";

			if (mb.doubleclick)
				text+=", DblClick";
			if (mb.mod.alt)
				text="Alt+"+text;
			if (mb.mod.shift)
				text="Shift+"+text;
			if (mb.mod.control)
				text="Ctrl+"+text;
			if (mb.mod.meta)
				text="Meta+"+text;


		} break;
		case InputEvent::JOYSTICK_MOTION: {

			InputEventJoystickMotion jm = filters[p_port].joy_motion;

			text="JoyMotion Axis "+itos(jm.axis>>1);
			if (jm.axis&1)
				text+=" > "+rtos(jm.axis_value);
			else
				text+=" < "+rtos(-jm.axis_value);

		} break;
		case InputEvent::JOYSTICK_BUTTON: {
			InputEventJoystickButton jb = filters[p_port].joy_button;

			text="JoyButton "+itos(jb.button_index);
			if (jb.pressed)
				text+=", Pressed";
			else
				text+=", Released";
		} break;
		case InputEvent::SCREEN_TOUCH: {
			InputEventScreenTouch sd = filters[p_port].screen_touch;

			text="Touch Finger "+itos(sd.index);
			if (sd.pressed)
				text+=", Pressed";
			else
				text+=", Released";
		} break;
		case InputEvent::SCREEN_DRAG: {
			InputEventScreenDrag sd = filters[p_port].screen_drag;
			text="Drag Finger "+itos(sd.index);
		} break;
		case InputEvent::ACTION: {


			List<PropertyInfo> pinfo;
			Globals::get_singleton()->get_property_list(&pinfo);
			int index=1;

			text="No Action";
			for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
				const PropertyInfo &pi=E->get();

				if (!pi.name.begins_with("input/"))
					continue;


				if (filters[p_port].action.action==index) {
					text="Action "+pi.name.substr(pi.name.find("/")+1,pi.name.length());
					break;
				}
				index++;
			}

			if (filters[p_port].action.pressed)
				text+=", Pressed";
			else
				text+=", Released";


		} break;
	}



	return text+" - "+itos(p_port);
}

PropertyInfo VisualScriptInputFilter::get_input_value_port_info(int p_idx) const{

	return PropertyInfo(Variant::INPUT_EVENT,"event");
}

PropertyInfo VisualScriptInputFilter::get_output_value_port_info(int p_idx) const{

	return PropertyInfo(Variant::INPUT_EVENT,"");
}


String VisualScriptInputFilter::get_caption() const {

	return "InputFilter";
}

String VisualScriptInputFilter::get_text() const {

	return "";
}



bool VisualScriptInputFilter::_set(const StringName& p_name, const Variant& p_value) {

	if (p_name=="filter_count") {
		filters.resize(p_value);
		_change_notify();
		ports_changed_notify();
		return true;
	}


	if (String(p_name).begins_with("filter_")) {

		int idx = String(p_name).replace_first("filters_","").get_slice("/",0).to_int();

		ERR_FAIL_INDEX_V(idx,filters.size(),false);

		String what = String(p_name).get_slice("/",1);


		if (what=="type") {
			filters[idx]=InputEvent();
			filters[idx].type=InputEvent::Type(int(p_value));
			if (filters[idx].type==InputEvent::JOYSTICK_MOTION) {
				filters[idx].joy_motion.axis_value=0.5; //for treshold
			} else if (filters[idx].type==InputEvent::KEY) {
				filters[idx].key.pressed=true; //put these as true to make it more user friendly
			} else if (filters[idx].type==InputEvent::MOUSE_BUTTON) {
				filters[idx].mouse_button.pressed=true;
			} else if (filters[idx].type==InputEvent::JOYSTICK_BUTTON) {
				filters[idx].joy_button.pressed=true;
			} else if (filters[idx].type==InputEvent::SCREEN_TOUCH) {
				filters[idx].screen_touch.pressed=true;
			} else if (filters[idx].type==InputEvent::ACTION) {
				filters[idx].action.pressed=true;
			}
			_change_notify();
			ports_changed_notify();

			return true;
		}
		if (what=="device") {
			filters[idx].device=p_value;
			ports_changed_notify();
			return true;
		}

		switch(filters[idx].type) {

			case InputEvent::KEY: {

				if (what=="scancode") {
					String sc = p_value;
					if (sc==String()) {
						filters[idx].key.scancode=0;
					} else {
						filters[idx].key.scancode=find_keycode(p_value);
					}

				} else if (what=="unicode") {

					String uc = p_value;

					if (uc==String()) {
						filters[idx].key.unicode=0;
					} else {
						filters[idx].key.unicode=uc[0];
					}

				} else if (what=="pressed") {

					filters[idx].key.pressed=p_value;
				} else if (what=="echo") {

					filters[idx].key.echo=p_value;

				} else if (what=="mod_alt") {
					filters[idx].key.mod.alt=p_value;

				} else if (what=="mod_shift") {
					filters[idx].key.mod.shift=p_value;

				} else if (what=="mod_ctrl") {
					filters[idx].key.mod.control=p_value;

				} else  if (what=="mod_meta") {
					filters[idx].key.mod.meta=p_value;
				} else {
					return false;
				}
				ports_changed_notify();

				return true;
			} break;
			case InputEvent::MOUSE_MOTION: {


				if (what=="button_mask") {
					filters[idx].mouse_motion.button_mask=p_value;

				} else if (what=="mod_alt") {
					filters[idx].mouse_motion.mod.alt=p_value;

				} else if (what=="mod_shift") {
					filters[idx].mouse_motion.mod.shift=p_value;

				} else if (what=="mod_ctrl") {
					filters[idx].mouse_motion.mod.control=p_value;

				} else  if (what=="mod_meta") {
					filters[idx].mouse_motion.mod.meta=p_value;
				} else {
					return false;
				}

				ports_changed_notify();
				return true;

			} break;
			case InputEvent::MOUSE_BUTTON: {

				if (what=="button_index") {
					filters[idx].mouse_button.button_index=p_value;
				} else if (what=="pressed") {
					filters[idx].mouse_button.pressed=p_value;
				} else if (what=="doubleclicked") {
					filters[idx].mouse_button.doubleclick=p_value;

				} else if (what=="mod_alt") {
					filters[idx].mouse_button.mod.alt=p_value;

				} else if (what=="mod_shift") {
					filters[idx].mouse_button.mod.shift=p_value;

				} else if (what=="mod_ctrl") {
					filters[idx].mouse_button.mod.control=p_value;

				} else  if (what=="mod_meta") {
					filters[idx].mouse_button.mod.meta=p_value;
				} else {
					return false;
				}
				ports_changed_notify();
				return true;

			} break;
			case InputEvent::JOYSTICK_MOTION: {

				if (what=="axis") {
					filters[idx].joy_motion.axis=int(p_value)<<1|filters[idx].joy_motion.axis;
				} else if (what=="mode") {
					filters[idx].joy_motion.axis|=int(p_value);
				} else if (what=="treshold") {
					filters[idx].joy_motion.axis_value=p_value;
				} else {
					return false;
				}
				ports_changed_notify();
				return true;


			} break;
			case InputEvent::JOYSTICK_BUTTON: {

				if (what=="button_index") {
					filters[idx].joy_button.button_index=p_value;
				} else if (what=="pressed") {
					filters[idx].joy_button.pressed=p_value;
				} else {
					return false;
				}
				ports_changed_notify();
				return true;

			} break;
			case InputEvent::SCREEN_TOUCH: {

				if (what=="finger_index") {
					filters[idx].screen_touch.index=p_value;
				} else if (what=="pressed") {
					filters[idx].screen_touch.pressed=p_value;
				} else {
					return false;
				}
				ports_changed_notify();
				return true;
			} break;
			case InputEvent::SCREEN_DRAG: {
				if (what=="finger_index") {
					filters[idx].screen_drag.index=p_value;
				} else {
					return false;
				}
				ports_changed_notify();
				return true;
			} break;
			case InputEvent::ACTION: {


				if (what=="action_name") {

					List<PropertyInfo> pinfo;
					Globals::get_singleton()->get_property_list(&pinfo);
					int index=1;

					for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
						const PropertyInfo &pi=E->get();

						if (!pi.name.begins_with("input/"))
							continue;

						String name = pi.name.substr(pi.name.find("/")+1,pi.name.length());
						if (name==String(p_value)) {

							filters[idx].action.action=index;
							ports_changed_notify();
							return true;
						}

						index++;
					}

					filters[idx].action.action=0;
					ports_changed_notify();

					return false;

				} else if (what=="pressed") {

					filters[idx].action.pressed=p_value;
					ports_changed_notify();
					return true;
				}


			} break;

		}
	}
	return false;
}

bool VisualScriptInputFilter::_get(const StringName& p_name,Variant &r_ret) const{

	if (p_name=="filter_count") {
		r_ret=filters.size();
		return true;
	}


	if (String(p_name).begins_with("filter_")) {

		int idx = String(p_name).replace_first("filters_","").get_slice("/",0).to_int();

		ERR_FAIL_INDEX_V(idx,filters.size(),false);

		String what = String(p_name).get_slice("/",1);


		if (what=="type") {
			r_ret=filters[idx].type;
			return true;
		}
		if (what=="device") {
			r_ret=filters[idx].device;
			return true;
		}

		switch(filters[idx].type) {

			case InputEvent::KEY: {

				if (what=="scancode") {
					if (filters[idx].key.scancode==0)
						r_ret=String();
					else {

						r_ret=keycode_get_string(filters[idx].key.scancode);
					}

				} else if (what=="unicode") {


					if (filters[idx].key.unicode==0) {
						r_ret=String();
					} else {
						CharType str[2]={ (CharType)filters[idx].key.unicode, 0};
						r_ret=String(str);
					}

				} else if (what=="pressed") {

					r_ret=filters[idx].key.pressed;
				} else if (what=="echo") {

					r_ret=filters[idx].key.echo;

				} else if (what=="mod_alt") {
					r_ret=filters[idx].key.mod.alt;

				} else if (what=="mod_shift") {
					r_ret=filters[idx].key.mod.shift;

				} else if (what=="mod_ctrl") {
					r_ret=filters[idx].key.mod.control;

				} else  if (what=="mod_meta") {
					r_ret=filters[idx].key.mod.meta;
				} else {
					return false;
				}

				return true;
			} break;
			case InputEvent::MOUSE_MOTION: {


				if (what=="button_mask") {
					r_ret=filters[idx].mouse_motion.button_mask;

				} else if (what=="mod_alt") {
					r_ret=filters[idx].mouse_motion.mod.alt;

				} else if (what=="mod_shift") {
					r_ret=filters[idx].mouse_motion.mod.shift;

				} else if (what=="mod_ctrl") {
					r_ret=filters[idx].mouse_motion.mod.control;

				} else  if (what=="mod_meta") {
					r_ret=filters[idx].mouse_motion.mod.meta;
				} else {
					return false;
				}

				return true;

			} break;
			case InputEvent::MOUSE_BUTTON: {

				if (what=="button_index") {
					r_ret=filters[idx].mouse_button.button_index;
				} else if (what=="pressed") {
					r_ret=filters[idx].mouse_button.pressed;
				} else if (what=="doubleclicked") {
					r_ret=filters[idx].mouse_button.doubleclick;

				} else if (what=="mod_alt") {
					r_ret=filters[idx].mouse_button.mod.alt;

				} else if (what=="mod_shift") {
					r_ret=filters[idx].mouse_button.mod.shift;

				} else if (what=="mod_ctrl") {
					r_ret=filters[idx].mouse_button.mod.control;

				} else  if (what=="mod_meta") {
					r_ret=filters[idx].mouse_button.mod.meta;
				} else {
					return false;
				}
				return true;

			} break;
			case InputEvent::JOYSTICK_MOTION: {

				if (what=="axis_index") {
					r_ret=filters[idx].joy_motion.axis>>1;
				} else if (what=="mode") {
					r_ret=filters[idx].joy_motion.axis&1;
				} else if (what=="treshold") {
					r_ret=filters[idx].joy_motion.axis_value;
				} else {
					return false;
				}
				return true;


			} break;
			case InputEvent::JOYSTICK_BUTTON: {

				if (what=="button_index") {
					r_ret=filters[idx].joy_button.button_index;
				} else if (what=="pressed") {
					r_ret=filters[idx].joy_button.pressed;
				} else {
					return false;
				}
				return true;

			} break;
			case InputEvent::SCREEN_TOUCH: {

				if (what=="finger_index") {
					r_ret=filters[idx].screen_touch.index;
				} else if (what=="pressed") {
					r_ret=filters[idx].screen_touch.pressed;
				} else {
					return false;
				}
				return true;
			} break;
			case InputEvent::SCREEN_DRAG: {
				if (what=="finger_index") {
					r_ret=filters[idx].screen_drag.index;
				} else {
					return false;
				}
				return true;
			} break;
			case InputEvent::ACTION: {


				if (what=="action_name") {

					List<PropertyInfo> pinfo;
					Globals::get_singleton()->get_property_list(&pinfo);
					int index=1;

					for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
						const PropertyInfo &pi=E->get();

						if (!pi.name.begins_with("input/"))
							continue;


						if (filters[idx].action.action==index) {
							r_ret=pi.name.substr(pi.name.find("/")+1,pi.name.length());
							return true;
						}
						index++;
					}

					r_ret="None"; //no index
					return false;

				} else if (what=="pressed") {

					r_ret=filters[idx].action.pressed;
					return true;
				}


			} break;

		}
	}
	return false;
}

static const char* event_type_names[InputEvent::TYPE_MAX]={
	"None",
	"Key",
	"MouseMotion",
	"MouseButton",
	"JoystickMotion",
	"JoystickButton",
	"ScreenTouch",
	"ScreenDrag",
	"Action"
};

void VisualScriptInputFilter::_get_property_list( List<PropertyInfo> *p_list) const {

	p_list->push_back(PropertyInfo(Variant::INT,"filter_count",PROPERTY_HINT_RANGE,"0,64"));

	String et;
	for(int i=0;i<InputEvent::TYPE_MAX;i++) {
		if (i>0)
			et+=",";

		et+=event_type_names[i];
	}

	String kc;
	String actions;



	for(int i=0;i<filters.size();i++) {

		String base = "filter_"+itos(i)+"/";
		p_list->push_back(PropertyInfo(Variant::INT,base+"type",PROPERTY_HINT_ENUM,et));
		p_list->push_back(PropertyInfo(Variant::INT,base+"device"));
		switch(filters[i].type) {

			case InputEvent::NONE: {

			} break;
			case InputEvent::KEY: {
				if (kc==String()) {
					int kcc = keycode_get_count();
					kc="None";
					for(int i=0;i<kcc;i++) {
						kc+=",";
						kc+=String(keycode_get_name_by_index(i));
					}
				}
				p_list->push_back(PropertyInfo(Variant::STRING,base+"scancode",PROPERTY_HINT_ENUM,kc));
				p_list->push_back(PropertyInfo(Variant::STRING,base+"unicode"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"pressed"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"echo"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_alt"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_shift"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_ctrl"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_meta"));


			} break;
			case InputEvent::MOUSE_MOTION: {
				p_list->push_back(PropertyInfo(Variant::INT,base+"button_mask",PROPERTY_HINT_FLAGS,"Left,Right,Middle,WheelUp,WheelDown,WheelLeft,WheelRight"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_alt"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_shift"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_ctrl"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_meta"));

			} break;
			case InputEvent::MOUSE_BUTTON: {
				p_list->push_back(PropertyInfo(Variant::INT,base+"button_index",PROPERTY_HINT_ENUM,"Any,Left,Right,Middle,WheelUp,WheelDown,WheelLeft,WheelRight"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"pressed"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"doubleclicked"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_alt"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_shift"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_ctrl"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_meta"));

			} break;
			case InputEvent::JOYSTICK_MOTION: {

				p_list->push_back(PropertyInfo(Variant::INT,base+"axis_index"));
				p_list->push_back(PropertyInfo(Variant::INT,base+"mode",PROPERTY_HINT_ENUM,"Min,Max"));
				p_list->push_back(PropertyInfo(Variant::REAL,base+"treshold",PROPERTY_HINT_RANGE,"0,1,0.01"));
			} break;
			case InputEvent::JOYSTICK_BUTTON: {
				p_list->push_back(PropertyInfo(Variant::INT,base+"button_index"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"pressed"));

			} break;
			case InputEvent::SCREEN_TOUCH: {
				p_list->push_back(PropertyInfo(Variant::INT,base+"finger_index"));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"pressed"));

			} break;
			case InputEvent::SCREEN_DRAG: {
				p_list->push_back(PropertyInfo(Variant::INT,base+"finger_index"));
			} break;
			case InputEvent::ACTION: {



				if (actions==String()) {

					actions="None";

					List<PropertyInfo> pinfo;
					Globals::get_singleton()->get_property_list(&pinfo);
					Vector<String> al;

					for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
						const PropertyInfo &pi=E->get();

						if (!pi.name.begins_with("input/"))
							continue;

						String name = pi.name.substr(pi.name.find("/")+1,pi.name.length());


						al.push_back(name);
					}

					for(int i=0;i<al.size();i++) {
						actions+=",";
						actions+=al[i];
					}
				}

				p_list->push_back(PropertyInfo(Variant::STRING,base+"action_name",PROPERTY_HINT_ENUM,actions));
				p_list->push_back(PropertyInfo(Variant::BOOL,base+"pressed"));

			} break;

		}
	}
}

class VisualScriptNodeInstanceInputFilter : public VisualScriptNodeInstance {
public:

	VisualScriptInstance* instance;
	Vector<InputEvent> filters;

	//virtual int get_working_memory_size() const { return 0; }
	//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
	//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return false; }

	virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {

		if (p_inputs[0]->get_type()!=Variant::INPUT_EVENT) {
			r_error_str="Input value not of type event";
			r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
			return 0;
		}

		InputEvent event = *p_inputs[0];


		for(int i=0;i<filters.size();i++) {

			const InputEvent &ie = filters[i];
			if (ie.type!=event.type)
				continue;

			bool match=false;

			switch(ie.type) {

				case InputEvent::NONE: {

					match=true;
				} break;
				case InputEvent::KEY: {

					InputEventKey k = ie.key;
					InputEventKey k2 = event.key;

					if (k.scancode==0 && k.unicode==0 && k2.scancode==0 && k2.unicode==0) {
						match=true;

					} else {

						if ( (k.scancode!=0 && k.scancode==k2.scancode) || (k.unicode!=0 && k.unicode==k2.unicode)) {
							//key valid

							if (
								k.pressed==k2.pressed &&
								k.echo==k2.echo &&
								k.mod == k2.mod
									) {
								match=true;
							}
						}

					}

				} break;
				case InputEvent::MOUSE_MOTION: {
					InputEventMouseMotion mm = ie.mouse_motion;
					InputEventMouseMotion mm2 = event.mouse_motion;

					if (	mm.button_mask==mm2.button_mask &&
						mm.mod==mm2.mod
						) {
						match=true;
					}

				} break;
				case InputEvent::MOUSE_BUTTON: {

					InputEventMouseButton mb = ie.mouse_button;
					InputEventMouseButton mb2 = event.mouse_button;

					if (	mb.button_index==mb2.button_index &&
						mb.pressed==mb2.pressed &&
						mb.doubleclick==mb2.doubleclick &&
						mb.mod==mb2.mod) {
						match=true;
					}


				} break;
				case InputEvent::JOYSTICK_MOTION: {

					InputEventJoystickMotion jm = ie.joy_motion;
					InputEventJoystickMotion jm2 = event.joy_motion;

					int axis = jm.axis>>1;

					if (axis==jm2.axis) {

						if (jm.axis&1) {
							//greater
							if (jm2.axis_value > jm.axis_value) {
								match=true;
							}
						} else {
							//less
							if (jm2.axis_value < -jm.axis_value) {
								match=true;
							}
						}
					}


				} break;
				case InputEvent::JOYSTICK_BUTTON: {
					InputEventJoystickButton jb = ie.joy_button;
					InputEventJoystickButton jb2 = event.joy_button;

					if (	jb.button_index==jb2.button_index &&
						jb.pressed == jb2.pressed
							) {
						match=true;
					}
				} break;
				case InputEvent::SCREEN_TOUCH: {
					InputEventScreenTouch st = ie.screen_touch;
					InputEventScreenTouch st2 = event.screen_touch;

					if (	st.index==st2.index &&
						st.pressed==st2.pressed) {
						match=true;
					}

				} break;
				case InputEvent::SCREEN_DRAG: {
					InputEventScreenDrag sd = ie.screen_drag;
					InputEventScreenDrag sd2 = event.screen_drag;

					if (sd.index==sd2.index) {
						match=true;
					}
				} break;
				case InputEvent::ACTION: {

					InputEventAction ia = ie.action;
					InputEventAction ia2 = event.action;

					if (	ia.action==ia2.action &&
						ia.pressed==ia2.pressed) {
						match=true;
					}
				} break;

			}

			*p_outputs[0] = event;

			if (match)
				return i; //go through match output

		}

		return STEP_NO_ADVANCE_BIT; //none found, don't advance


	}


};


VisualScriptNodeInstance* VisualScriptInputFilter::instance(VisualScriptInstance* p_instance) {

	VisualScriptNodeInstanceInputFilter * instance = memnew(VisualScriptNodeInstanceInputFilter );
	instance->instance=p_instance;
	instance->filters=filters;
	return instance;
}




VisualScriptInputFilter::VisualScriptInputFilter() {


}


//////////////////////////////////////////
////////////////TYPE CAST///////////
//////////////////////////////////////////


int VisualScriptTypeCast::get_output_sequence_port_count() const {

	return 2;
}

bool VisualScriptTypeCast::has_input_sequence_port() const{

	return true;
}

int VisualScriptTypeCast::get_input_value_port_count() const{


	return 1;
}
int VisualScriptTypeCast::get_output_value_port_count() const{

	return 1;
}

String VisualScriptTypeCast::get_output_sequence_port_text(int p_port) const {

	return p_port==0 ? "yes" : "no";
}

PropertyInfo VisualScriptTypeCast::get_input_value_port_info(int p_idx) const{

	return PropertyInfo(Variant::OBJECT,"instance");
}

PropertyInfo VisualScriptTypeCast::get_output_value_port_info(int p_idx) const{

	return PropertyInfo(Variant::OBJECT,"");
}


String VisualScriptTypeCast::get_caption() const {

	return "TypeCast";
}

String VisualScriptTypeCast::get_text() const {

	if (script!=String())
		return "Is "+script.get_file()+"?";
	else
		return "Is "+base_type+"?";
}

void VisualScriptTypeCast::set_base_type(const StringName& p_type) {

	if (base_type==p_type)
		return;

	base_type=p_type;
	_change_notify();
	ports_changed_notify();
}

StringName VisualScriptTypeCast::get_base_type() const{

	return base_type;
}

void VisualScriptTypeCast::set_base_script(const String& p_path){

	if (script==p_path)
		return;

	script=p_path;
	_change_notify();
	ports_changed_notify();

}
String VisualScriptTypeCast::get_base_script() const{

	return script;
}


class VisualScriptNodeInstanceTypeCast : public VisualScriptNodeInstance {
public:

	VisualScriptInstance* instance;
	StringName base_type;
	String script;

	//virtual int get_working_memory_size() const { return 0; }
	//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
	//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return false; }

	virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {

		Object *obj = *p_inputs[0];

		*p_outputs[0]=Variant();

		if (!obj) {
			r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
			r_error_str="Instance is null";
			return 0;
		}

		if (script!=String()) {

			Ref<Script> obj_script = obj->get_script();
			if (!obj_script.is_valid()) {
				return 1; //well, definitely not the script because object we got has no script.
			}

			if (!ResourceCache::has(script)) {
				//if the script is not in use by anyone, we can safely assume whathever we got is not casting to it.
				return 1;
			}
			Ref<Script> cast_script = Ref<Resource>(ResourceCache::get(script));
			if (!cast_script.is_valid()) {
				r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
				r_error_str="Script path is not a script: "+script;
				return 1;
			}

			while(obj_script.is_valid()) {

				if (cast_script==obj_script) {
					*p_outputs[0]=*p_inputs[0]; //copy
					return 0; // it is the script, yey
				}

				obj_script=obj_script->get_base_script();
			}

			return 1; //not found sorry
		}

		if (ObjectTypeDB::is_type(obj->get_type_name(),base_type)) {
			*p_outputs[0]=*p_inputs[0]; //copy
			return 0;
		} else
			return 1;

	}


};

VisualScriptNodeInstance* VisualScriptTypeCast::instance(VisualScriptInstance* p_instance) {

	VisualScriptNodeInstanceTypeCast * instance = memnew(VisualScriptNodeInstanceTypeCast );
	instance->instance=p_instance;
	instance->base_type=base_type;
	instance->script=script;
	return instance;
}



void VisualScriptTypeCast::_bind_methods() {

	ObjectTypeDB::bind_method(_MD("set_base_type","type"),&VisualScriptTypeCast::set_base_type);
	ObjectTypeDB::bind_method(_MD("get_base_type"),&VisualScriptTypeCast::get_base_type);

	ObjectTypeDB::bind_method(_MD("set_base_script","path"),&VisualScriptTypeCast::set_base_script);
	ObjectTypeDB::bind_method(_MD("get_base_script"),&VisualScriptTypeCast::get_base_script);


	List<String> script_extensions;
	for(int i=0;i>ScriptServer::get_language_count();i++) {
		ScriptServer::get_language(i)->get_recognized_extensions(&script_extensions);
	}

	String script_ext_hint;
	for (List<String>::Element *E=script_extensions.front();E;E=E->next()) {
		if (script_ext_hint!=String())
			script_ext_hint+=",";
		script_ext_hint+="*."+E->get();
	}

	ADD_PROPERTY(PropertyInfo(Variant::STRING,"function/base_type",PROPERTY_HINT_TYPE_STRING,"Object"),_SCS("set_base_type"),_SCS("get_base_type"));
	ADD_PROPERTY(PropertyInfo(Variant::STRING,"property/base_script",PROPERTY_HINT_FILE,script_ext_hint),_SCS("set_base_script"),_SCS("get_base_script"));

}

VisualScriptTypeCast::VisualScriptTypeCast() {

	base_type="Object";
}


void register_visual_script_flow_control_nodes() {

	VisualScriptLanguage::singleton->add_register_func("flow_control/return",create_return_node<false>);
	VisualScriptLanguage::singleton->add_register_func("flow_control/return_with_value",create_return_node<true>);
	VisualScriptLanguage::singleton->add_register_func("flow_control/condition",create_node_generic<VisualScriptCondition>);
	VisualScriptLanguage::singleton->add_register_func("flow_control/while",create_node_generic<VisualScriptWhile>);
	VisualScriptLanguage::singleton->add_register_func("flow_control/iterator",create_node_generic<VisualScriptIterator>);
	VisualScriptLanguage::singleton->add_register_func("flow_control/sequence",create_node_generic<VisualScriptSequence>);
	VisualScriptLanguage::singleton->add_register_func("flow_control/switch",create_node_generic<VisualScriptSwitch>);
	VisualScriptLanguage::singleton->add_register_func("flow_control/input_filter",create_node_generic<VisualScriptInputFilter>);
	VisualScriptLanguage::singleton->add_register_func("flow_control/type_cast",create_node_generic<VisualScriptTypeCast>);



}