// Reki (May 28 2024):
// Extended map scripting capabilities
// essentially a mini asm language meant for semi-visual scripting
// by mappers inside of the .map file, using placed entities to create
// the logic chains and call entrypoints inside their MapScript files

#ifdef SSQC

#define LOGIC_IF_SPAWNFIELDS(v) \
	SPAWNFIELD_PARSESTRING(targetname, v) \
	SPAWNFIELD_PARSESTRING(value1, v) \
	SPAWNFIELD_PARSESTRING(value2, v) \
	SPAWNFIELD_PARSESTRING(targetThen, v) \
	SPAWNFIELD_PARSESTRING(targetElse, v)

#define LOGIC_STORE_SPAWNFIELDS(v) \
	SPAWNFIELD_PARSEINT(datatype, v) \
	SPAWNFIELD_PARSESTRING(value, v)

#define LOGIC_SET_SPAWNFIELDS(v) \
	SPAWNFIELD_PARSESTRING(register, v) \
	SPAWNFIELD_PARSESTRING(value, v)

class logic_store; // prototype, icky
class script_base_c : map_entity_c {
	//string targetname; <- inherited

	//string target; <- inherited
	string target2;
	string target3;
	string target4;


	nonvirtual void(string msg) ErrorOut = {
		error(sprintf("^xF00ERROR IN SCRIPT ENTITY %s\n^xF00     %s\n", this.targetname, msg));
	};
	nonvirtual logic_store(string str) isRegister;
	nonvirtual logic_store(string str) resolveRegister;
};

// data registers
class logic_store : script_base_c
{
	int datatype;
	__variant value;

	logic_store();
	virtual void ParseSpawnField(string key, string value);

	// fetcher for 
	virtual __variant(int type=0) get = {
		__variant val = this.value;
		int val_type = this.datatype;

		if (val_type == EV_STRING) // this could potentially be a ""pointer""" to another register
		{
			logic_store aliasTo = resolveRegister((string)val);
			if (aliasTo)
			{
				val = aliasTo.value;
				val_type = aliasTo.datatype;
			}
		}

		__variant retval = val;

		if (type > 0)
		{
			if (val_type != type)
			{
				switch(type) // switch for what type we were requested to return
				{
					case EV_INTEGER: // we need to convert to a integer
						switch (val_type)
						{
							case EV_FLOAT: retval = (int)(val); break;
							case EV_VECTOR: retval = (int)vlen((vector)val); break;
							case EV_STRING: retval = (int)stoi((string)val); break;
							default: ErrorOut(sprintf("impossible conversion from %i to %i", val_type, type));
						} break;
					case EV_FLOAT: // we need to convert to a float
						switch (val_type)
						{
							case EV_INTEGER: retval = (float)((int)val); break;
							case EV_VECTOR: retval = (float)vlen((vector)val); break;
							case EV_STRING: retval = (float)stof((string)val); break;
							default: ErrorOut(sprintf("impossible conversion from %i to %i", val_type, type));
						} break;
					case EV_VECTOR: // we need to convert to a vector
						switch (val_type)
						{
							case EV_INTEGER: retval = (vector)([(int)val, (int)val, (int)val]); break;
							case EV_FLOAT: retval = (vector)([(float)val, (float)val, (float)val]); break;
							case EV_STRING: retval = (vector)stov((string)val); break;
							default: ErrorOut(sprintf("impossible conversion from %i to %i", val_type, type));
						} break;
					case EV_STRING: // we need to convert to a string
						switch (val_type)
						{
							case EV_INTEGER: retval = (string)itos((float)val); break;
							case EV_FLOAT: retval = (string)ftos((float)val); break;
							case EV_VECTOR: retval = (string)vtos((vector)val); break;
							default: ErrorOut(sprintf("impossible conversion from %i to %i", val_type, type));
						} break;
				}
			}
		}

		return retval;
	};
	
	// setter
	virtual void(string val) set = {
		switch(this.datatype)
		{
			case EV_INTEGER:
				this.value = (int)stoi(val);
				break;
			case EV_FLOAT:
				this.value = (float)stof(val);
				break;
			case EV_VECTOR:
				this.value = (vector)stov(val);
				break;
			case EV_STRING:
				this.value = val;
				break;
		}
	};
};

class logic_set : script_base_c
{
	string register;
	string value;

	logic_set();
	virtual void ParseSpawnField(string key, string value);

	nonvirtual void() set_value = {
		if (!isRegister(this.register))
		{
			ErrorOut(sprintf("invalid register \"%s\" in logic_set entity", this.register));
			return;
		}
		
		logic_store regRef = resolveRegister(this.register);
		string old_val = regRef.get(EV_STRING);
		regRef.set(this.value);

		// trigger targets if the value changed
		if (old_val != this.value)
		{
			use_targets_ex(this, this.target);
			use_targets_ex(this, this.target2);
			use_targets_ex(this, this.target3);
			use_targets_ex(this, this.target4);
		}
	};
};

class logic_ifeq : script_base_c
{
	string value1;
	string value2;
	string targetThen;
	string targetElse;

	logic_ifeq();
	virtual void ParseSpawnField(string key, string value);
	
	nonvirtual void() evaluateTrue = {
		if (this.targetThen == __NULL__)
			return;
		use_targets_ex(this, this.targetThen);
	};

	nonvirtual void() evaluateFalse = {
		if (this.targetElse == __NULL__)
			return;
		use_targets_ex(this, this.targetElse);
	};

	virtual void(__variant val1, __variant val2) evaluate = {
		if ((float)val1 == (float)val2)
		{
			evaluateTrue();
			print("evaluated true\n");
		}
		else
		{
			evaluateFalse();
			print("evaluated false\n");
		}
	};

	virtual void() resolve = {
		float val1, val2;
		void(__out float val, __in string str) getVal = {
			if (isRegister(str))
			{
				logic_store aliasTo = resolveRegister(str);
				val = aliasTo.get(EV_FLOAT);
			}
			else
			{
				val = stof(str);
			}
		}
		getVal(val1, value1);
		getVal(val2, value2);

		print(sprintf("%g(%s) == %g(%s)\n", val1, value1, val2, value2));

		evaluate(val1, val2);
	};
};

class logic_ifgt : logic_ifeq
{
	virtual void(__variant val1, __variant val2) evaluate = {
		if (val1 > val2)
			evaluateTrue();
		else
			evaluateFalse();
	};
};

class logic_ifge : logic_ifeq
{
	virtual void(__variant val1, __variant val2) evaluate = {
		if (val1 >= val2)
			evaluateTrue();
		else
			evaluateFalse();
	};
};

class logic_ifstrcmp : logic_ifeq
{
	virtual void(__variant val1, __variant val2) evaluate = {
		if (strcmp(val1, val2))
			evaluateTrue();
		else
			evaluateFalse();
	};

	virtual void() resolve = {
		string val1, val2;
		void(__out string val, __in string str) getVal = {
			if (isRegister(str))
			{
				logic_store aliasTo = resolveRegister(str);
				val = aliasTo.get(EV_STRING);
			}
			else
			{
				val = str;
			}
		}
		getVal(val1, value1);
		getVal(val2, value2);
		
		evaluate(val1, val2);
	};
};

class logic_ifstricmp : logic_ifstrcmp
{
	virtual void(__variant val1, __variant val2) evaluate = {
		if (strcasecmp(val1, val2))
			evaluateTrue();
		else
			evaluateFalse();
	};
};
#endif

