#include "visual_script_builtin_funcs.h"
#include "math_funcs.h"
#include "object_type_db.h"
#include "reference.h"
#include "func_ref.h"
#include "os/os.h"
#include "variant_parser.h"
#include "io/marshalls.h"

const char* VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX]={
	"sin",
	"cos",
	"tan",
	"sinh",
	"cosh",
	"tanh",
	"asin",
	"acos",
	"atan",
	"atan2",
	"sqrt",
	"fmod",
	"fposmod",
	"floor",
	"ceil",
	"round",
	"abs",
	"sign",
	"pow",
	"log",
	"exp",
	"is_nan",
	"is_inf",
	"ease",
	"decimals",
	"stepify",
	"lerp",
	"dectime",
	"randomize",
	"randi",
	"randf",
	"rand_range",
	"seed",
	"rand_seed",
	"deg2rad",
	"rad2deg",
	"linear2db",
	"db2linear",
	"max",
	"min",
	"clamp",
	"nearest_po2",
	"weakref",
	"funcref",
	"convert",
	"typeof",
	"type_exists",
	"str",
	"print",
	"printerr",
	"printraw",
	"var2str",
	"str2var",
	"var2bytes",
	"bytes2var",
};



int VisualScriptBuiltinFunc::get_output_sequence_port_count() const {

	return has_input_sequence_port() ? 1 : 0;
}

bool VisualScriptBuiltinFunc::has_input_sequence_port() const{

	switch(func) {

		case MATH_RANDOMIZE:
		case TEXT_PRINT:
		case TEXT_PRINTERR:
		case TEXT_PRINTRAW:
			return true;
		default:
			return false;

	}

}

int VisualScriptBuiltinFunc::get_input_value_port_count() const{


	switch(func) {

		case MATH_RANDOMIZE:
		case MATH_RAND:
		case MATH_RANDF:
			return 0;
		case MATH_SIN:
		case MATH_COS:
		case MATH_TAN:
		case MATH_SINH:
		case MATH_COSH:
		case MATH_TANH:
		case MATH_ASIN:
		case MATH_ACOS:
		case MATH_ATAN:
		case MATH_SQRT:
		case MATH_FLOOR:
		case MATH_CEIL:
		case MATH_ROUND:
		case MATH_ABS:
		case MATH_SIGN:
		case MATH_LOG:
		case MATH_EXP:
		case MATH_ISNAN:
		case MATH_ISINF:
		case MATH_DECIMALS:
		case MATH_SEED:
		case MATH_RANDSEED:
		case MATH_DEG2RAD:
		case MATH_RAD2DEG:
		case MATH_LINEAR2DB:
		case MATH_DB2LINEAR:
		case LOGIC_NEAREST_PO2:
		case OBJ_WEAKREF:
		case TYPE_OF:
		case TEXT_STR:
		case TEXT_PRINT:
		case TEXT_PRINTERR:
		case TEXT_PRINTRAW:
		case VAR_TO_STR:
		case STR_TO_VAR:
		case VAR_TO_BYTES:
		case BYTES_TO_VAR:
		case TYPE_EXISTS:
			return 1;
		case MATH_ATAN2:
		case MATH_FMOD:
		case MATH_FPOSMOD:
		case MATH_POW:
		case MATH_EASE:
		case MATH_STEPIFY:
		case MATH_RANDOM:
		case LOGIC_MAX:
		case LOGIC_MIN:
		case FUNC_FUNCREF:
		case TYPE_CONVERT:
			return 2;
		case MATH_LERP:
		case MATH_DECTIME:
		case LOGIC_CLAMP:
			return 3;
		case FUNC_MAX:{}

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

	switch(func) {
		case MATH_RANDOMIZE:
		case TEXT_PRINT:
		case TEXT_PRINTERR:
		case TEXT_PRINTRAW:
		case MATH_SEED:
			return 0;
		case MATH_RANDSEED:
			return 2;
		default:
			return 1;
	}

	return 1;
}

String VisualScriptBuiltinFunc::get_output_sequence_port_text(int p_port) const {

	return String();
}

PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const{

	switch(func) {

		case MATH_SIN:
		case MATH_COS:
		case MATH_TAN:
		case MATH_SINH:
		case MATH_COSH:
		case MATH_TANH:
		case MATH_ASIN:
		case MATH_ACOS:
		case MATH_ATAN:
		case MATH_ATAN2:
		case MATH_SQRT: {
			return PropertyInfo(Variant::REAL,"num");
		} break;
		case MATH_FMOD:
		case MATH_FPOSMOD: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"x");
			else
				return PropertyInfo(Variant::REAL,"y");
		} break;
		case MATH_FLOOR:
		case MATH_CEIL:
		case MATH_ROUND:
		case MATH_ABS:
		case MATH_SIGN: {
			return PropertyInfo(Variant::REAL,"num");

		} break;

		case MATH_POW: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"x");
			else
				return PropertyInfo(Variant::REAL,"y");
		} break;
		case MATH_LOG:
		case MATH_EXP:
		case MATH_ISNAN:
		case MATH_ISINF: {
			return PropertyInfo(Variant::REAL,"num");
		} break;
		case MATH_EASE: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"s");
			else
				return PropertyInfo(Variant::REAL,"curve");
		} break;
		case MATH_DECIMALS: {
			return PropertyInfo(Variant::REAL,"step");
		} break;
		case MATH_STEPIFY: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"s");
			else
				return PropertyInfo(Variant::REAL,"steps");
		} break;
		case MATH_LERP: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"from");
			else if (p_idx==1)
				return PropertyInfo(Variant::REAL,"to");
			else
				return PropertyInfo(Variant::REAL,"weight");

		} break;
		case MATH_DECTIME: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"value");
			else if (p_idx==1)
				return PropertyInfo(Variant::REAL,"amount");
			else
				return PropertyInfo(Variant::REAL,"step");
		} break;
		case MATH_RANDOMIZE: {

		} break;
		case MATH_RAND: {

		} break;
		case MATH_RANDF: {

		} break;
		case MATH_RANDOM: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"from");
			else
				return PropertyInfo(Variant::REAL,"to");
		} break;
		case MATH_SEED: {
			return PropertyInfo(Variant::INT,"seed");
		} break;
		case MATH_RANDSEED: {
			return PropertyInfo(Variant::INT,"seed");
		} break;
		case MATH_DEG2RAD: {
			return PropertyInfo(Variant::REAL,"deg");
		} break;
		case MATH_RAD2DEG: {
			return PropertyInfo(Variant::REAL,"rad");
		} break;
		case MATH_LINEAR2DB: {
			return PropertyInfo(Variant::REAL,"nrg");
		} break;
		case MATH_DB2LINEAR: {
			return PropertyInfo(Variant::REAL,"db");
		} break;
		case LOGIC_MAX: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"a");
			else
				return PropertyInfo(Variant::REAL,"b");
		} break;
		case LOGIC_MIN: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"a");
			else
				return PropertyInfo(Variant::REAL,"b");
		} break;
		case LOGIC_CLAMP: {
			if (p_idx==0)
				return PropertyInfo(Variant::REAL,"a");
			else if (p_idx==0)
				return PropertyInfo(Variant::REAL,"min");
			else
				return PropertyInfo(Variant::REAL,"max");
		} break;
		case LOGIC_NEAREST_PO2: {
			return PropertyInfo(Variant::INT,"num");
		} break;
		case OBJ_WEAKREF: {

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

		} break;
		case FUNC_FUNCREF: {

			if (p_idx==0)
				return PropertyInfo(Variant::OBJECT,"instance");
			else
				return PropertyInfo(Variant::STRING,"funcname");

		} break;
		case TYPE_CONVERT: {

			if (p_idx==0)
				return PropertyInfo(Variant::NIL,"what");
			else
				return PropertyInfo(Variant::STRING,"type");
		} break;
		case TYPE_OF: {
			return PropertyInfo(Variant::NIL,"what");

		} break;
		case TYPE_EXISTS: {

			return PropertyInfo(Variant::STRING,"type");

		} break;
		case TEXT_STR: {

			return PropertyInfo(Variant::NIL,"value");


		} break;
		case TEXT_PRINT: {

			return PropertyInfo(Variant::NIL,"value");

		} break;
		case TEXT_PRINTERR: {
			return PropertyInfo(Variant::NIL,"value");

		} break;
		case TEXT_PRINTRAW: {

			return PropertyInfo(Variant::NIL,"value");

		} break;
		case VAR_TO_STR: {
			return PropertyInfo(Variant::NIL,"var");

		} break;
		case STR_TO_VAR: {

			return PropertyInfo(Variant::STRING,"string");
		} break;
		case VAR_TO_BYTES: {
			return PropertyInfo(Variant::NIL,"var");

		} break;
		case BYTES_TO_VAR: {

			return PropertyInfo(Variant::RAW_ARRAY,"bytes");
		} break;
		case FUNC_MAX:{}
	}

	return PropertyInfo();
}

PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) const{

	Variant::Type t=Variant::NIL;
	switch(func) {

		case MATH_SIN:
		case MATH_COS:
		case MATH_TAN:
		case MATH_SINH:
		case MATH_COSH:
		case MATH_TANH:
		case MATH_ASIN:
		case MATH_ACOS:
		case MATH_ATAN:
		case MATH_ATAN2:
		case MATH_SQRT:
		case MATH_FMOD:
		case MATH_FPOSMOD:
		case MATH_FLOOR:
		case MATH_CEIL: {
			t=Variant::REAL;
		} break;
		case MATH_ROUND: {
			t=Variant::INT;
		} break;
		case MATH_ABS: {
			t=Variant::NIL;
		} break;
		case MATH_SIGN: {
			t=Variant::NIL;
		} break;
		case MATH_POW:
		case MATH_LOG:
		case MATH_EXP: {
			t=Variant::REAL;
		} break;
		case MATH_ISNAN:
		case MATH_ISINF: {
			t=Variant::BOOL;
		} break;
		case MATH_EASE: {
			t=Variant::REAL;
		} break;
		case MATH_DECIMALS: {
			t=Variant::INT;
		} break;
		case MATH_STEPIFY:
		case MATH_LERP:
		case MATH_DECTIME: {
			t=Variant::REAL;

		} break;
		case MATH_RANDOMIZE: {

		} break;
		case MATH_RAND: {

			t=Variant::INT;
		} break;
		case MATH_RANDF:
		case MATH_RANDOM: {
			t=Variant::REAL;
		} break;
		case MATH_SEED: {

		} break;
		case MATH_RANDSEED: {

			if (p_idx==0)
				return PropertyInfo(Variant::INT,"rnd");
			else
				return PropertyInfo(Variant::INT,"seed");
		} break;
		case MATH_DEG2RAD:
		case MATH_RAD2DEG:
		case MATH_LINEAR2DB:
		case MATH_DB2LINEAR: {
			t=Variant::REAL;
		} break;
		case LOGIC_MAX:
		case LOGIC_MIN:
		case LOGIC_CLAMP: {


		} break;

		case LOGIC_NEAREST_PO2: {
			t=Variant::NIL;
		} break;
		case OBJ_WEAKREF: {

			t=Variant::OBJECT;

		} break;
		case FUNC_FUNCREF: {

			t=Variant::OBJECT;

		} break;
		case TYPE_CONVERT: {



		} break;
		case TYPE_OF: {
			t=Variant::INT;

		} break;
		case TYPE_EXISTS: {

			t=Variant::BOOL;

		} break;
		case TEXT_STR: {

			t=Variant::STRING;

		} break;
		case TEXT_PRINT: {


		} break;
		case TEXT_PRINTERR: {

		} break;
		case TEXT_PRINTRAW: {

		} break;
		case VAR_TO_STR: {
			t=Variant::STRING;
		} break;
		case STR_TO_VAR: {

		} break;
		case VAR_TO_BYTES: {
			t=Variant::RAW_ARRAY;

		} break;
		case BYTES_TO_VAR: {


		} break;
		case FUNC_MAX:{}
	}

	return PropertyInfo(t,"");
}

String VisualScriptBuiltinFunc::get_caption() const {

	return "BuiltinFunc";
}

String VisualScriptBuiltinFunc::get_text() const {

	return func_name[func];
}

void VisualScriptBuiltinFunc::set_func(BuiltinFunc p_which) {

	ERR_FAIL_INDEX(p_which,FUNC_MAX);
	func=p_which;
	_change_notify();
	ports_changed_notify();
}

VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::get_func() {
	return func;
}


#define VALIDATE_ARG_NUM(m_arg) \
	if (!p_inputs[m_arg]->is_num()) {\
		r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;\
		r_error.argument=m_arg;\
		r_error.expected=Variant::REAL;\
		return 0;\
	}

class VisualScriptNodeInstanceBuiltinFunc : public VisualScriptNodeInstance {
public:

	VisualScriptBuiltinFunc *node;
	VisualScriptInstance *instance;

	VisualScriptBuiltinFunc::BuiltinFunc func;


	//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 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) {

		switch(func) {
			case VisualScriptBuiltinFunc::MATH_SIN: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::sin(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_COS: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::cos(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_TAN: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::tan(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_SINH: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::sinh(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_COSH: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::cosh(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_TANH: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::tanh(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_ASIN: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::asin(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_ACOS: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::acos(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_ATAN: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::atan(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_ATAN2: {

				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);
				*p_outputs[0]=Math::atan2(*p_inputs[0],*p_inputs[1]);
			} break;
			case VisualScriptBuiltinFunc::MATH_SQRT: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::sqrt(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_FMOD: {

				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);
				*p_outputs[0]=Math::fmod(*p_inputs[0],*p_inputs[1]);
			} break;
			case VisualScriptBuiltinFunc::MATH_FPOSMOD: {

				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);
				*p_outputs[0]=Math::fposmod(*p_inputs[0],*p_inputs[1]);
			} break;
			case VisualScriptBuiltinFunc::MATH_FLOOR: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::floor(*p_inputs[0]);
			  } break;
			case VisualScriptBuiltinFunc::MATH_CEIL: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::ceil(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_ROUND: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::round(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_ABS: {

				if (p_inputs[0]->get_type()==Variant::INT) {

					int64_t i = *p_inputs[0];
					*p_outputs[0]=ABS(i);
				} else if (p_inputs[0]->get_type()==Variant::REAL) {

					real_t r = *p_inputs[0];
					*p_outputs[0]=Math::abs(r);
				} else {

					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=0;
					r_error.expected=Variant::REAL;

				}
			} break;
			case VisualScriptBuiltinFunc::MATH_SIGN: {

					if (p_inputs[0]->get_type()==Variant::INT) {

						int64_t i = *p_inputs[0];
						*p_outputs[0]= i < 0 ? -1 : ( i > 0 ? +1 : 0);
					} else if (p_inputs[0]->get_type()==Variant::REAL) {

						real_t r = *p_inputs[0];
						*p_outputs[0]= r < 0.0 ? -1.0 : ( r > 0.0 ? +1.0 : 0.0);
					} else {

						r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
						r_error.argument=0;
						r_error.expected=Variant::REAL;

					}
			} break;
			case VisualScriptBuiltinFunc::MATH_POW: {

				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);
				*p_outputs[0]=Math::pow(*p_inputs[0],*p_inputs[1]);
			} break;
			case VisualScriptBuiltinFunc::MATH_LOG: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::log(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_EXP: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::exp(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_ISNAN: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::is_nan(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_ISINF: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::is_inf(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_EASE: {

				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);
				*p_outputs[0]=Math::ease(*p_inputs[0],*p_inputs[1]);
			} break;
			case VisualScriptBuiltinFunc::MATH_DECIMALS: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::step_decimals(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_STEPIFY: {

				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);
				*p_outputs[0]=Math::stepify(*p_inputs[0],*p_inputs[1]);
			} break;
			case VisualScriptBuiltinFunc::MATH_LERP: {

				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);
				VALIDATE_ARG_NUM(2);
				*p_outputs[0]=Math::lerp(*p_inputs[0],*p_inputs[1],*p_inputs[2]);
			} break;
			case VisualScriptBuiltinFunc::MATH_DECTIME: {

				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);
				VALIDATE_ARG_NUM(2);
				*p_outputs[0]=Math::dectime(*p_inputs[0],*p_inputs[1],*p_inputs[2]);
			} break;
			case VisualScriptBuiltinFunc::MATH_RANDOMIZE: {
				Math::randomize();

			} break;
			case VisualScriptBuiltinFunc::MATH_RAND: {
				*p_outputs[0]=Math::rand();
			} break;
			case VisualScriptBuiltinFunc::MATH_RANDF: {
				*p_outputs[0]=Math::randf();
			} break;
			case VisualScriptBuiltinFunc::MATH_RANDOM: {

				VALIDATE_ARG_NUM(0);
				VALIDATE_ARG_NUM(1);
				*p_outputs[0]=Math::random(*p_inputs[0],*p_inputs[1]);
			} break;
			case VisualScriptBuiltinFunc::MATH_SEED: {

				VALIDATE_ARG_NUM(0);
				uint32_t seed=*p_inputs[0];
				Math::seed(seed);

			} break;
			case VisualScriptBuiltinFunc::MATH_RANDSEED: {

				VALIDATE_ARG_NUM(0);
				uint32_t seed=*p_inputs[0];
				int ret = Math::rand_from_seed(&seed);
				Array reta;
				reta.push_back(ret);
				reta.push_back(seed);
				*p_outputs[0]=reta;

			} break;
			case VisualScriptBuiltinFunc::MATH_DEG2RAD: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::deg2rad(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_RAD2DEG: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::rad2deg(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_LINEAR2DB: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::linear2db(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::MATH_DB2LINEAR: {

				VALIDATE_ARG_NUM(0);
				*p_outputs[0]=Math::db2linear(*p_inputs[0]);
			} break;
			case VisualScriptBuiltinFunc::LOGIC_MAX: {

				if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) {

					int64_t a = *p_inputs[0];
					int64_t b = *p_inputs[1];
					*p_outputs[0]=MAX(a,b);
				} else {
					VALIDATE_ARG_NUM(0);
					VALIDATE_ARG_NUM(1);

					real_t a = *p_inputs[0];
					real_t b = *p_inputs[1];

					*p_outputs[0]=MAX(a,b);
				}

			} break;
			case VisualScriptBuiltinFunc::LOGIC_MIN: {

				if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) {

					int64_t a = *p_inputs[0];
					int64_t b = *p_inputs[1];
					*p_outputs[0]=MIN(a,b);
				} else {
					VALIDATE_ARG_NUM(0);
					VALIDATE_ARG_NUM(1);

					real_t a = *p_inputs[0];
					real_t b = *p_inputs[1];

					*p_outputs[0]=MIN(a,b);
				}
			} break;
			case VisualScriptBuiltinFunc::LOGIC_CLAMP: {

				if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT && p_inputs[2]->get_type()==Variant::INT) {

					int64_t a = *p_inputs[0];
					int64_t b = *p_inputs[1];
					int64_t c = *p_inputs[2];
					*p_outputs[0]=CLAMP(a,b,c);
				} else {
					VALIDATE_ARG_NUM(0);
					VALIDATE_ARG_NUM(1);
					VALIDATE_ARG_NUM(2);

					real_t a = *p_inputs[0];
					real_t b = *p_inputs[1];
					real_t c = *p_inputs[2];

					*p_outputs[0]=CLAMP(a,b,c);
				}
			} break;
			case VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2: {

				VALIDATE_ARG_NUM(0);
				int64_t num = *p_inputs[0];
				*p_outputs[0] = nearest_power_of_2(num);
			} break;
			case VisualScriptBuiltinFunc::OBJ_WEAKREF: {

				if (p_inputs[0]->get_type()!=Variant::OBJECT) {

					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=0;
					r_error.expected=Variant::OBJECT;

					return 0;

				}

				if (p_inputs[0]->is_ref()) {

					REF r = *p_inputs[0];
					if (!r.is_valid()) {

						return 0;
					}

					Ref<WeakRef> wref = memnew( WeakRef );
					wref->set_ref(r);
					*p_outputs[0]=wref;
				} else {
					Object *obj = *p_inputs[0];
					if (!obj) {

						return 0;
					}
					Ref<WeakRef> wref = memnew( WeakRef );
					wref->set_obj(obj);
					*p_outputs[0]=wref;
				}




			} break;
			case VisualScriptBuiltinFunc::FUNC_FUNCREF: {

				if (p_inputs[0]->get_type()!=Variant::OBJECT) {

					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=0;
					r_error.expected=Variant::OBJECT;

					return 0;

				}
				if (p_inputs[1]->get_type()!=Variant::STRING && p_inputs[1]->get_type()!=Variant::NODE_PATH) {

					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=1;
					r_error.expected=Variant::STRING;

					return 0;

				}

				Ref<FuncRef> fr = memnew( FuncRef);

				fr->set_instance(*p_inputs[0]);
				fr->set_function(*p_inputs[1]);

				*p_outputs[0]=fr;

			} break;
			case VisualScriptBuiltinFunc::TYPE_CONVERT: {

				VALIDATE_ARG_NUM(1);
				int type=*p_inputs[1];
				if (type<0 || type>=Variant::VARIANT_MAX) {

					*p_outputs[0]=RTR("Invalid type argument to convert(), use TYPE_* constants.");
					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=0;
					r_error.expected=Variant::INT;
					return 0;

				} else {


					*p_outputs[0]=Variant::construct(Variant::Type(type),p_inputs,1,r_error);
				}
			} break;
			case VisualScriptBuiltinFunc::TYPE_OF: {


				*p_outputs[0] = p_inputs[0]->get_type();

			} break;
			case VisualScriptBuiltinFunc::TYPE_EXISTS: {


				*p_outputs[0] = ObjectTypeDB::type_exists(*p_inputs[0]);

			} break;
			case VisualScriptBuiltinFunc::TEXT_STR: {

				String str = *p_inputs[0];

				*p_outputs[0]=str;

			} break;
			case VisualScriptBuiltinFunc::TEXT_PRINT: {

				String str = *p_inputs[0];
				print_line(str);


			} break;

			case VisualScriptBuiltinFunc::TEXT_PRINTERR: {

				String str = *p_inputs[0];

				//str+="\n";
				OS::get_singleton()->printerr("%s\n",str.utf8().get_data());


			} break;
			case VisualScriptBuiltinFunc::TEXT_PRINTRAW: {
				String str = *p_inputs[0];

				//str+="\n";
				OS::get_singleton()->print("%s",str.utf8().get_data());


			} break;
			case VisualScriptBuiltinFunc::VAR_TO_STR: {

				String vars;
				VariantWriter::write_to_string(*p_inputs[0],vars);
				*p_outputs[0]=vars;
			} break;
			case VisualScriptBuiltinFunc::STR_TO_VAR: {

				if (p_inputs[0]->get_type()!=Variant::STRING) {
					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=0;
					r_error.expected=Variant::STRING;

					return 0;
				}

				VariantParser::StreamString ss;
				ss.s=*p_inputs[0];

				String errs;
				int line;
				Error err = VariantParser::parse(&ss,*p_outputs[0],errs,line);

				if (err!=OK) {
					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=0;
					r_error.expected=Variant::STRING;
					*p_outputs[0]="Parse error at line "+itos(line)+": "+errs;
					return 0;
				}

			} break;
			case VisualScriptBuiltinFunc::VAR_TO_BYTES: {


				ByteArray barr;
				int len;
				Error err = encode_variant(*p_inputs[0],NULL,len);
				if (err) {
					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=0;
					r_error.expected=Variant::NIL;
					*p_outputs[0]="Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
					return 0;
				}

				barr.resize(len);
				{
					ByteArray::Write w = barr.write();
					encode_variant(*p_inputs[0],w.ptr(),len);

				}
				*p_outputs[0]=barr;
			} break;
			case VisualScriptBuiltinFunc::BYTES_TO_VAR: {

				if (p_inputs[0]->get_type()!=Variant::RAW_ARRAY) {
					r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
					r_error.argument=0;
					r_error.expected=Variant::RAW_ARRAY;

					return 0;
				}

				ByteArray varr=*p_inputs[0];
				Variant ret;
				{
					ByteArray::Read r=varr.read();
					Error err = decode_variant(ret,r.ptr(),varr.size(),NULL);
					if (err!=OK) {
						*p_outputs[0]=RTR("Not enough bytes for decoding bytes, or invalid format.");
						r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
						r_error.argument=0;
						r_error.expected=Variant::RAW_ARRAY;
						return 0;
					}

				}

				*p_outputs[0]=ret;

			} break;
			default: {}
		}
		return 0;
	}


};

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

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


void VisualScriptBuiltinFunc::_bind_methods() {

	ObjectTypeDB::bind_method(_MD("set_func","which"),&VisualScriptBuiltinFunc::set_func);
	ObjectTypeDB::bind_method(_MD("get_func"),&VisualScriptBuiltinFunc::get_func);

	String cc;

	for(int i=0;i<FUNC_MAX;i++) {

		if (i>0)
			cc+=",";
		cc+=func_name[i];
	}
	ADD_PROPERTY(PropertyInfo(Variant::INT,"function",PROPERTY_HINT_ENUM,cc),_SCS("set_func"),_SCS("get_func"));
}

VisualScriptBuiltinFunc::VisualScriptBuiltinFunc() {

	func=MATH_SIN;
}

template<VisualScriptBuiltinFunc::BuiltinFunc func>
static Ref<VisualScriptNode> create_builtin_func_node(const String& p_name) {

	Ref<VisualScriptBuiltinFunc> node;
	node.instance();
	node->set_func(func);
	return node;
}

void register_visual_script_builtin_func_node() {


	VisualScriptLanguage::singleton->add_register_func("functions/built_in/sin",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SIN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/cos",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_COS>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/tan",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_TAN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/sinh",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SINH>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/cosh",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_COSH>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/tanh",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_TANH>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/asin",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ASIN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/acos",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ACOS>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/atan",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ATAN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/atan2",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ATAN2>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/sqrt",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SQRT>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/fmod",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FMOD>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/fposmod",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FPOSMOD>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/floor",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FLOOR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/ceil",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_CEIL>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/round",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ROUND>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/abs",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ABS>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/sign",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SIGN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/pow",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_POW>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/log",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LOG>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/exp",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_EXP>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/isnan",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ISNAN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/isinf",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ISINF>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/ease",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_EASE>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/decimals",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECIMALS>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/stepify",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_STEPIFY>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/random",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOM>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/seed",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SEED>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/randseed",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDSEED>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/deg2rad",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DEG2RAD>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/rad2deg",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAD2DEG>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/linear2db",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LINEAR2DB>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/max",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MAX>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/min",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MIN>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/clamp",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_CLAMP>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/nearest_po2",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2>);

	VisualScriptLanguage::singleton->add_register_func("functions/built_in/weakref",create_builtin_func_node<VisualScriptBuiltinFunc::OBJ_WEAKREF>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/funcref",create_builtin_func_node<VisualScriptBuiltinFunc::FUNC_FUNCREF>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/convert",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_CONVERT>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/typeof",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_OF>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/type_exists",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_EXISTS>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/str",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_STR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/print",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINT>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/printerr",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTERR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/printraw",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTRAW>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/var2str",create_builtin_func_node<VisualScriptBuiltinFunc::VAR_TO_STR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/str2var",create_builtin_func_node<VisualScriptBuiltinFunc::STR_TO_VAR>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/var2bytes",create_builtin_func_node<VisualScriptBuiltinFunc::VAR_TO_BYTES>);
	VisualScriptLanguage::singleton->add_register_func("functions/built_in/bytes2var",create_builtin_func_node<VisualScriptBuiltinFunc::BYTES_TO_VAR>);

}