#include "td/protocol/packets/WorldBeginDataPacket.h"

namespace td {
namespace protocol {

static DataBuffer& operator<<(DataBuffer& buffer, game::TilePtr tile) {
	buffer << tile->GetType();

	switch (tile->GetType()) {

	case game::TileType::Tower: {
		const game::TowerTile* towerTile = dynamic_cast<const game::TowerTile*>(tile.get());
		buffer << towerTile->color_palette_ref << towerTile->team_owner;
		break;
	}

	case game::TileType::Walk: {
		const game::WalkableTile* walkTile = dynamic_cast<const game::WalkableTile*>(tile.get());
		buffer << walkTile->direction;
		break;
	}

	case game::TileType::Decoration: {
		const game::DecorationTile* decoTile = dynamic_cast<const game::DecorationTile*>(tile.get());
		buffer << decoTile->color_palette_ref;
		break;
	}

	default:
		break;
	}

	return buffer;
}

static DataBuffer& operator>>(DataBuffer& buffer, game::TilePtr& tile) {
	game::TileType tileType;
	buffer >> tileType;
	switch (tileType) {
	case game::TileType::Tower: {
		auto tilePtr = std::make_shared<game::TowerTile>();
		buffer >> tilePtr->color_palette_ref >> tilePtr->team_owner;
		tile = tilePtr;
		break;
	}
	case game::TileType::Walk: {
		auto tilePtr = std::make_shared<game::WalkableTile>();
		buffer >> tilePtr->direction;
		tile = tilePtr;
		break;
	}
	case game::TileType::Decoration: {
		auto tilePtr = std::make_shared<game::DecorationTile>();
		buffer >> tilePtr->color_palette_ref;
		tile = tilePtr;
		break;
	}
	default:
		break;
	}
	return buffer;
}

DataBuffer WorldBeginDataPacket::Serialize(bool packetID) const {
	DataBuffer data;
	const game::TowerTileColorPalette& towerTilePalette = m_Header.m_World->GetTowerTileColorPalette();
	const std::vector<Color>& decoTilePalette = m_Header.m_World->GetDecorationPalette();

	WritePacketID(data, packetID);

	data << towerTilePalette << m_Header.m_World->GetWalkableTileColor()
		<< static_cast<std::uint16_t>(decoTilePalette.size());

	// deco color palette
	std::size_t bufferSize = data.GetSize();
	data.Resize(bufferSize + decoTilePalette.size() * sizeof(Color));

	memcpy(reinterpret_cast<std::uint8_t*>(data.data()) + bufferSize, decoTilePalette.data(), decoTilePalette.size() * sizeof(Color));

	data << m_Header.m_World->GetBackgroundColor();

	const game::Spawn& redSpawn = m_Header.m_World->GetRedTeam().GetSpawn(), blueSpawn = m_Header.m_World->GetBlueTeam().GetSpawn();
	const game::TeamCastle& redCastle = m_Header.m_World->GetRedTeam().GetCastle(), blueCastle = m_Header.m_World->GetBlueTeam().GetCastle();

	data << redSpawn << static_cast<utils::shape::Rectangle>(redCastle);
	data << blueSpawn << static_cast<utils::shape::Rectangle>(blueCastle);

	// tile palette
	data << static_cast<std::uint64_t>(m_Header.m_World->GetTilePalette().size());

	for (game::TilePtr tile : m_Header.m_World->GetTilePalette()) {
		data << tile;
	}

	data << m_Header.m_World->GetSpawnColors();

	return data;
}

void WorldBeginDataPacket::Deserialize(DataBuffer& data) {
	data >> m_Header.m_TowerPlacePalette >> m_Header.m_WalkablePalette;

	std::uint16_t decoPaletteSize;
	data >> decoPaletteSize;

	std::size_t decoPalletteSizeByte = decoPaletteSize * sizeof(Color);

	m_Header.m_DecorationPalette.resize(decoPaletteSize);

	memcpy(reinterpret_cast<std::uint8_t*>(m_Header.m_DecorationPalette.data()), data.data() + data.GetReadOffset(), decoPalletteSizeByte);

	data.SetReadOffset(data.GetReadOffset() + decoPalletteSizeByte);

	data >> m_Header.m_Background;

	utils::shape::Rectangle redCastle, blueCastle;

	data >> m_Header.m_RedSpawn >> redCastle;
	data >> m_Header.m_BlueSpawn >> blueCastle;

	m_Header.m_RedCastle.SetShape(redCastle);
	m_Header.m_BlueCastle.SetShape(blueCastle);

	std::uint64_t tilePaletteSize;
	data >> tilePaletteSize;

	m_Header.m_TilePalette.reserve(tilePaletteSize);

	for (std::uint64_t tileNumber = 0; tileNumber < tilePaletteSize; tileNumber++) {
		game::TilePtr tile;
		data >> tile;
		m_Header.m_TilePalette.push_back(tile);
	}

	data >> m_Header.m_SpawnColorPalette;
}

} // namespace protocol
} // namespace td
