/*
Copyright (C) 2007, 2010 - Bit-Blot

This file is part of Aquaria.

Aquaria is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "DSQ.h"

#if MAPVIS_SUBDIV % 8 != 0
	#error MAPVIS_SUBDIV must be a multiple of 8
#endif
const unsigned int rowSize = MAPVIS_SUBDIV / 8;
const unsigned int dataSize = MAPVIS_SUBDIV * rowSize;

// Used by dataToString(), stringToData()
static const char base64Chars[] =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

WorldMapTile::WorldMapTile()
{
	revealed = false;
	prerevealed = false;
	scale = scale2 = 1;
	layer = 0;
	index = -1;
	data = 0;
	q = 0;
	stringIndex = 0;
}

WorldMapTile::~WorldMapTile()
{
	delete[] data;
}

void WorldMapTile::markVisited(int left, int top, int right, int bottom)
{
	if (data == 0)
	{
		data = new unsigned char[dataSize];
		memset(data, 0, dataSize);
	}

	if (left < 0)
		left = 0;
	if (top < 0)
		top = 0;
	if (right > MAPVIS_SUBDIV - 1)
		right = MAPVIS_SUBDIV - 1;
	if (bottom > MAPVIS_SUBDIV - 1)
		bottom = MAPVIS_SUBDIV - 1;

	for (int y = top; y <= bottom; y++)
	{
		for (int x = left; x <= right; x++)
		{
			data[y*rowSize + x/8] |= 1 << (x%8);
		}
	}
}

// Convert the data array to string format for saving.
void WorldMapTile::dataToString(std::ostringstream &os)
{
	if (!data)
	{
		os << "0 0";
		return;
	}

#ifdef AQUARIA_SAVE_MAPVIS_RAW

	char *outbuf = new char[((dataSize+2)/3)*4 + 1];
	char *ptr = outbuf;

	unsigned int i;
	for (i = 0; i+3 <= dataSize; i += 3, ptr += 4)
	{
		ptr[0] = base64Chars[(               data[i+0]>>2) & 0x3F];
		ptr[1] = base64Chars[(data[i+0]<<4 | data[i+1]>>4) & 0x3F];
		ptr[2] = base64Chars[(data[i+1]<<2 | data[i+2]>>6) & 0x3F];
		ptr[3] = base64Chars[(data[i+2]<<0               ) & 0x3F];
	}
	if (i < dataSize)
	{
		ptr[0] = base64Chars[(data[i+0]>>2) & 0x3F];
		if (i+1 < dataSize)
		{
			ptr[1] = base64Chars[(data[i+0]<<4 | data[i+1]>>4) & 0x3F];
			ptr[2] = base64Chars[(data[i+1]<<2) & 0x3F];
		} else {
			ptr[1] = base64Chars[(data[i+0]<<4) & 0x3F];
			ptr[2] = '=';
		}
		ptr[3] = '=';
		ptr += 4;
	}
	*ptr = 0;

	os << MAPVIS_SUBDIV << " b " // "b" for bitmap
	   << (dataSize==0 ? "====" : outbuf);  // Always write a non-empty string
	delete[] outbuf;

#else  // !AQUARIA_SAVE_MAPVIS_RAW

	unsigned int count = 0;
	std::ostringstream tempStream;
	unsigned char *ptr = data;
	for (unsigned int y = 0; y < MAPVIS_SUBDIV; y++, ptr += rowSize)
	{
		for (unsigned int x = 0; x < MAPVIS_SUBDIV; x += 8)
		{
			unsigned char dataByte = ptr[x/8];
			for (unsigned int x2 = 0; x2 < 8; x2++)
			{
				if (dataByte & (1 << x2))
				{
					tempStream << " " << (x+x2) << " " << y;
					count++;
				}
			}
		}
	}

	os << MAPVIS_SUBDIV << " " << count << tempStream.str();

#endif
}

// Parse a string from a save file and store in the data array.
void WorldMapTile::stringToData(std::istringstream &is)
{
	delete[] data;
	data = 0;

	int subdiv;
	std::string countOrType;
	is >> subdiv >> countOrType;
	if (subdiv != MAPVIS_SUBDIV)
	{
		if (subdiv != 0)
		{
			std::ostringstream os;
			os << "subdiv " << subdiv << " != MAPVIS_SUBDIV "
			   << MAPVIS_SUBDIV << ", can't load data!";
			debugLog(os.str());
		}

		// Skip over the data so we can still load the next tile's data
		if (countOrType == "b")
		{
			std::string encodedData;
			is >> encodedData;
		}
		else
		{
			int count = 0;
			std::istringstream is2(countOrType);
			is2 >> count;
			for (int i = 0; i < count; i++)
			{
				int x, y;
				is >> x >> y;
			}
		}
		return;
	}

	data = new unsigned char[dataSize];
	memset(data, 0, dataSize);
	if (countOrType == "b")  // Raw bitmap (base64-encoded)
	{
		std::string encodedData = "";
		is >> encodedData;
		const char *in = encodedData.c_str();
		unsigned char *out = data;
		unsigned char * const top = data + dataSize;
		while (in[0] != 0 && in[1] != 0 && out < top)
		{
			unsigned char ch0, ch1, ch2, ch3;
			const char *temp;
			temp = strchr(base64Chars, in[0]);
				ch0 = temp ? temp - base64Chars : 0;
			temp = strchr(base64Chars, in[1]);
				ch1 = temp ? temp - base64Chars : 0;
			if (in[2] != 0)
			{
				temp = strchr(base64Chars, in[2]);
					ch2 = temp ? temp - base64Chars : 0;
				temp = strchr(base64Chars, in[3]);
					ch3 = temp ? temp - base64Chars : 0;
			}
			else
			{
				ch2 = ch3 = 0;
			}
			*out++ = ch0<<2 | ch1>>4;
			if (out >= top || in[2] == 0 || in[2] == '=')
				break;
			*out++ = ch1<<4 | ch2>>2;
			if (out >= top || in[3] == 0 || in[3] == '=')
				break;
			*out++ = ch2<<6 | ch3>>0;
			in += 4;
		}
	}
	else  // List of coordinate pairs
	{
		int count = 0;
		std::istringstream is2(countOrType);
		is2 >> count;

		for (int i = 0; i < count; i++)
		{
			int x, y;
			is >> x >> y;
			if (x >= 0 && x < MAPVIS_SUBDIV && y >= 0 && y < MAPVIS_SUBDIV)
				data[y*rowSize + x/8] |= 1 << (x%8);
		}
	}
}


WorldMap::WorldMap()
{
	gw=gh=0;
}

void WorldMap::load()
{
	if (!dsq->mod.isActive())
		_load("data/worldmap.txt");
	else
		_load(dsq->mod.getPath() + "worldmap.txt");
}

void WorldMap::_load(const std::string &file)
{
	worldMapTiles.clear();

	std::string line;

	InStream in(file.c_str());
	
	while (std::getline(in, line))
	{
		WorldMapTile t;
		std::istringstream is(line);
		is >> t.index >> t.stringIndex >> t.name >> t.layer >> t.scale >> t.gridPos.x >> t.gridPos.y >> t.prerevealed >> t.scale2;
		t.revealed = t.prerevealed;
		stringToUpper(t.name);
		worldMapTiles.push_back(t);
	}
}

void WorldMap::save()
{
	std::string fn;

	if (dsq->mod.isActive())
		fn = dsq->mod.getPath() + "worldmap.txt";
	else
		fn = "data/worldmap.txt";

	std::ofstream out(fn.c_str());

	if (out)
	{
		for (int i = 0; i < worldMapTiles.size(); i++)
		{
			WorldMapTile *t = &worldMapTiles[i];
			out << t->index << " " << t->stringIndex << " " << t->name << " " << t->layer << " " << t->scale << " " << t->gridPos.x << " " << t->gridPos.y << " " << t->prerevealed << " " << t->scale2 << std::endl;
		}
		dsq->screenMessage(dsq->continuity.stringBank.get(2019) + " " + fn);
	}
	else
	{
		dsq->screenMessage(dsq->continuity.stringBank.get(2020) + " " + fn);
	}
}

void WorldMap::revealMap(const std::string &name)
{
	WorldMapTile *t = getWorldMapTile(name);
	if (t)
	{
		t->revealed = true;
	}
}

void WorldMap::revealMapIndex(int index)
{
	WorldMapTile *t = getWorldMapTileByIndex(index);
	if (t)
	{
		t->revealed = true;
	}
}

WorldMapTile *WorldMap::getWorldMapTile(const std::string &name)
{
	std::string n = name;
	stringToUpper(n);
	for (int i = 0; i < worldMapTiles.size(); i++)
	{
		if (worldMapTiles[i].name == n)
		{
			return &worldMapTiles[i];
		}
	}
	return 0;
}

WorldMapTile *WorldMap::getWorldMapTileByIndex(int index)
{
	for (int i = 0; i < worldMapTiles.size(); i++)
	{
		if (worldMapTiles[i].index == index)
		{
			return &worldMapTiles[i];
		}
	}
	return 0;
}

/*



void WorldMap::revealMapIndex(int index)
{
	if (index < 0 || index >= worldMapTiles.size()) return;

	worldMapTiles[index].revealed = true;
}
*/

void WorldMap::hideMap()
{
	for (int i = 0; i < worldMapTiles.size(); i++)
	{
		worldMapTiles[i].revealed = false;
	}
}

int WorldMap::getNumWorldMapTiles()
{
	return worldMapTiles.size();
}

WorldMapTile *WorldMap::getWorldMapTile(int index)
{
	if (index < 0 || index >= worldMapTiles.size()) return 0;

	return &worldMapTiles[index];
}

