// Miscellaneous conversion of text syntax to data structures used by the game engine.

#include "block.hpp"
#include "bytebuf.hpp"

#include <cassert>
#include <iostream>
#include <rovdefs.h>
#include <RPK.h>

static int FlagForName(const std::string& name)
{
	if (name == "blockmovement") {
		return OF_BLOCKMOVEMENT;
	} else if (name == "canbeblocked") {
		return OF_CANBEBLOCKED;
	} else if (name == "ticker") {
		return OF_TICKER;
	} else if (name == "canhurt") {
		return OF_CANHURT;
	} else if (name == "canuse") {
		return OF_CANUSE;
	} else if (name == "glow") {
		return OF_GLOW;
	} else if (name == "flying") {
		return OF_FLYING;
	} else if (name == "invert") {
		return OF_INVERT;
	} else if (name == "canknockback") {
		return OF_CANKNOCKBACK;
	} else if (name == "shine") {
		return OF_SHINE;
	} else if (name == "blur") {
		return OF_BLUR;
	} else if (name == "notarget") {
		return OF_NOTARGET;
	} else if (name == "lavaimmune") {
		return OF_LAVAIMMUNE;
	} else if (name == "normal") {
		return OF_NORMAL;
	} else if (name == "dead") {
		return OF_DEAD;
	} else {
		// TODO: Implement a way to create aliases for sets of flags
		std::cerr << "Unknown flag name " << name << std::endl;
		return 0;
	}
}

int IntifyFlagString(const std::string &str)
{
	int result, flag;
	char operation;
	size_t opidx, offs = 0;
	std::string flagName;

	opidx = str.find_first_of("+-");
	flagName = str.substr(0, opidx);
	result = FlagForName(flagName);

	while (opidx != std::string::npos)
	{
		operation = str[opidx];

		offs = opidx + 1;
		opidx = str.find_first_of("+-", offs);

		flagName = str.substr(offs, opidx - offs);
		flag = FlagForName(flagName);

		switch (operation)
		{
		case '+':
			result |= flag;
			break;
		case '-':
			result &= ~flag;
			break;
		default:
			break;
		}
	}

	return result;
}

void SerializeEntity(ByteBuffer &bytebuf, const Block &entity)
{
	bytebuf.WriteUInt16(entity.GetInteger("Health"));
	bytebuf.WriteUInt16(entity.GetOptionalInteger("XPReward", 0));
	bytebuf.WriteUInt16(entity.GetReference("StateInit", REF_STATE));
	bytebuf.WriteUInt16(entity.GetOptionalReference("StateHitWall", REF_STATE));
	bytebuf.WriteUInt16(entity.GetOptionalReference("StateHurt", REF_STATE));
	bytebuf.WriteUInt16(entity.GetOptionalReference("StateDead", REF_STATE));
	bytebuf.WriteUInt16(entity.GetOptionalReference("StateUse", REF_STATE));
	bytebuf.WriteUInt16(entity.GetOptionalReference("OnTouch", REF_SCRIPT));
	bytebuf.WriteUInt16(entity.GetOptionalReference("OnHit", REF_SCRIPT));
	bytebuf.WriteUInt16(0);
	bytebuf.WriteFloat(entity.GetFloat("HitboxSize"));
	bytebuf.WriteInt8(entity.GetOptionalChar("SpawnTile", '.'));
	bytebuf.WriteUInt8(entity.GetInteger("Team"));
	bytebuf.WriteInt16(entity.GetOptionalInteger("InitialData", 0));
	bytebuf.WriteUInt32(entity.GetOptionalInteger("ParticleType", 0));
}

void SerializeItem(ByteBuffer &bytebuf, const Block &item)
{
	bytebuf.WriteLengthString(item.GetString("Name"));
	bytebuf.WriteLengthString(item.GetString("Desc"));
	bytebuf.WriteUInt8(item.GetInteger("Width"));
	bytebuf.WriteUInt8(item.GetInteger("Height"));
	bytebuf.WriteInt16(item.GetOptionalInteger("InitialData", 0));
	bytebuf.WriteUInt8(item.GetOptionalInteger("AutoFire", 0));
	bytebuf.WriteUInt8(item.GetOptionalInteger("MinFloor", 99));
	bytebuf.WriteUInt8(item.GetOptionalInteger("MaxFloor", 99));
	bytebuf.WriteUInt8(item.GetOptionalInteger("Special", 0));
	bytebuf.WriteUInt16(item.GetInteger("MaxUses"));
	bytebuf.WriteUInt16(item.GetReference("OnUse", REF_SCRIPT));
	bytebuf.WriteFloat((float)item.GetOptionalInteger("BaseDamage", 0));
	bytebuf.WriteFloat((float)item.GetOptionalInteger("BaseSpeed", 0));
	bytebuf.WriteFloat(item.GetOptionalFloat("BaseRange", 0.0f));
}

void SerializeState(ByteBuffer &bytebuf, const List &state)
{
	assert(state.items.size() == 6);
	bytebuf.WriteFixedString(state.GetName(0), RPK_NAME_LENGTH);
	bytebuf.WriteFixedString(state.items[2].contents, RPK_NAME_LENGTH);
	bytebuf.WriteUInt16(state.GetOptionalReference(1, REF_SCRIPT));
	bytebuf.WriteUInt16(IntifyFlagString(state.GetName(3)));
	bytebuf.WriteUInt16(state.GetOptionalReference(4, REF_STATE));
	bytebuf.WriteUInt16(state.GetInteger(5));
}
