/* Copyright 2015, Michele Santullo
 * This file is part of DoorKeeper.
 *
 * DoorKeeper 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 3 of the License, or
 * (at your option) any later version.
 *
 * DoorKeeper 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 DoorKeeper.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "doorkeeper/helpers/asciimapsource.hpp"
#include "doorkeeper/implem/compatibility.h"
#include "doorkeeper/helpers/typename.hpp"
#include "doorkeeper/helpers/typename_native.hpp"
#include "asciimap_parser.hpp"
#include <fstream>
#include <iomanip>
#include <stdexcept>
#include <algorithm>
#include <sstream>

namespace dkh {
	///-------------------------------------------------------------------------
	///-------------------------------------------------------------------------
	AsciiMapSource::AsciiMapSource (const char* parFilename, const coords& parSize, dk::MapTypes parMapType, const coords& parTileSize) :
		m_mapSize(parSize),
		m_pixel_conv(parTileSize),
		m_mapType(parMapType)
	{
		std::ifstream src(parFilename);
		parse_map_data(src);
	}

	///-------------------------------------------------------------------------
	///-------------------------------------------------------------------------
	AsciiMapSource::AsciiMapSource (const std::string& parFilename, const coords& parSize, dk::MapTypes parMapType, const coords& parTileSize) :
		m_mapSize(parSize),
		m_pixel_conv(parTileSize),
		m_mapType(parMapType)
	{
		std::ifstream src(parFilename);
		parse_map_data(src);
	}

	///-------------------------------------------------------------------------
	///-------------------------------------------------------------------------
	AsciiMapSource::AsciiMapSource (std::istream& parData, const coords& parSize, dk::MapTypes parMapType, const coords& parTileSize) :
		m_mapSize(parSize),
		m_pixel_conv(parTileSize),
		m_mapType(parMapType)
	{
		parse_map_data(parData);
	}

	///-------------------------------------------------------------------------
	///-------------------------------------------------------------------------
	void AsciiMapSource::parse_map_data (std::istream& parSrc) {
		typedef std::istream_iterator<char> DataIterator;
		typedef boost::spirit::multi_pass<DataIterator> WrappedIterator;

		parSrc >> std::noskipws;

		auto first = WrappedIterator(DataIterator(parSrc));
		auto last = WrappedIterator(DataIterator());
		implem::AsciiMapGrammar<WrappedIterator, MapTileType> grammar(first);
		const bool p = boost::spirit::qi::parse(
			first,
			last,
			grammar,
			m_wholedata
		);

		if (not p or last != first) {
			throw std::runtime_error("Invalid data: can't parse file");
		}

		coords map_size;
		if (grammar.lengths.empty()) {
			map_size.x() = map_size.y() = 0;
			throw std::runtime_error("Invalid data: can't guess map size");
		}

		//Only look at the front. Besides a bug in spirit makes the other counts
		//invalid, see
		//http://boost.2283326.n4.nabble.com/Possible-bug-in-line-pos-iterator-td4636592.html
		map_size.x() = grammar.lengths.front();
		map_size.y() = m_wholedata.size() / map_size.x();
		if (map_size != m_mapSize) {
			std::ostringstream oss;
			oss << "Calculated map size is " << map_size <<
				" which is different than the expected size " << m_mapSize;
			throw std::runtime_error(oss.str());
		}
	}

	const AsciiMapSource::coords& AsciiMapSource::mapSize() const {
		return m_mapSize;
	}

	const AsciiMapSource::coords& AsciiMapSource::tileSize() const {
		return m_pixel_conv.tile_size();
	}

	void AsciiMapSource::fetch_raw (char* parOut, const coords& parFrom, const coords& parTo, std::size_t parSize) {
		const std::size_t from = parFrom.x() + parFrom.y() * m_mapSize.x();
		const std::size_t to = parTo.x() - 1 + (parTo.y() - 1) * m_mapSize.x() + 1;
		DK_ASSERT(to >= from);
		DK_ASSERT(parSize / sizeof(MapTileType) == to - from);
		std::copy(m_wholedata.begin() + from, m_wholedata.begin() + to, reinterpret_cast<MapTileType*>(parOut));
	}

	dk::MapTypes AsciiMapSource::mapType() const {
		return m_mapType;
	}

	int AsciiMapSource::layersCount() const {
		return 1;
	}

	void AsciiMapSource::chainedMaps (std::vector<std::string>&) const {
	}

	dk::HashType AsciiMapSource::layerTypeHash (int parIndex) const {
#if defined(DK_ASSERTIONS_ENABLED)
		DK_ASSERT(0 == parIndex);
#else
		(void)parIndex;
#endif
		return dk::type_name_hash<int>();
	}

	const dk::PixelConv<2>& AsciiMapSource::pixel_conv() const {
		return m_pixel_conv;
	}
} //namespace dkh
