/* 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/>.
 */

#ifndef id3F4AB2FAA29D4A0FA87760E61F0762C0
#define id3F4AB2FAA29D4A0FA87760E61F0762C0

#include "doorkeeper/primitivetypes.hpp"
#include "doorkeeper/implem/vector.hpp"
#include "doorkeeper/components/basemapsource.hpp"
#include "doorkeeper/implem/maptypes.hpp"
#include "doorkeeper/components/exception.hpp"
#include <fstream>
#include <cstdint>
#include <memory>
#include <map>
#include <string>
#include <ciso646>

#if !defined(NDEBUG) && !defined(WITH_TYLERMAP_WRITER)
#	define WITH_TYLERMAP_WRITER
#endif

namespace dkh {
	namespace implem {
		class TylerMapSourceBase {
		public:
			typedef std::istream::pos_type pos_type;

			explicit TylerMapSourceBase ( std::istream* parStream );
			~TylerMapSourceBase ( void );

			dk::HashType layerTypeHash ( int parIndex ) const;
			void chainedMaps ( std::vector<std::string>& parOut ) const;
			dk::MapTypes mapType ( void ) const;
			void parseMapHeaders ( uint32_t parDim, std::vector<uint32_t>& parMapDims, std::vector<uint16_t>& parTileDims );

		private:
			struct LayerInfo;

			std::vector<LayerInfo> m_layers_info;
			dk::MapTypes m_map_type;
			std::map<std::string, std::string> m_comments;
			std::string m_file_vendor;
			std::unique_ptr<std::istream> m_stream;
			pos_type m_stream_start;
		};
	} //namespace implem

	template <uint32_t D>
	class TylerMapSource : private implem::TylerMapSourceBase, public dk::BaseMapSource<D> {
		typedef implem::TylerMapSourceBase base_class;
		typedef base_class::pos_type pos_type;
	public:
		typedef dk::Vector<D> coords;
		enum {
			MapDimensions = D
		};

		TylerMapSource ( void ) = delete;
		TylerMapSource ( const TylerMapSource& ) = delete;
		TylerMapSource ( TylerMapSource&& ) = default;
		explicit TylerMapSource ( const char* parFilename );
		explicit TylerMapSource ( const std::string& parFilename );
		explicit TylerMapSource ( std::istream* parData );
		virtual ~TylerMapSource ( void ) noexcept = default;

		virtual const coords& mapSize ( void ) const;
		virtual const coords& tileSize ( void ) const;
		virtual dk::MapTypes mapType ( void ) const;
		virtual int layersCount ( void ) const;
		virtual void chainedMaps ( std::vector<std::string>& parOut ) const;
		virtual dk::HashType layerTypeHash ( int parIndex ) const;

	private:
		void parse_map_data ( void );
		virtual void fetch_raw ( char* parOut, const coords& parFrom, const coords& parTo, std::size_t parSize );

		coords m_map_size;
		coords m_tile_size;
		uint16_t m_map_version_major;
		uint16_t m_map_version_minor;
		uint8_t m_layer_count;
	};

#if defined(WITH_TYLERMAP_WRITER)
	class TylerMapWriter {
	public:
		TylerMapWriter ( void ) = delete;
		TylerMapWriter ( const TylerMapWriter& ) = delete;
		TylerMapWriter ( TylerMapWriter&& ) = default;

	private:
		std::unique_ptr<std::ostream> m_stream;
	};
#endif

	class InvalidMapFileException : public dk::DoorKeeperException {
	};
	class InvalidMapDimensionsException : public dk::DoorKeeperException {
	};

	namespace implem {
		struct TylerMapHeader {
			uint32_t signature;
			uint32_t file_size;
			uint16_t version_major;
			uint16_t version_minor;
			uint8_t layer_count;
			uint8_t map_type;
			uint8_t hashing_algorithm;
			uint8_t dimensions;
			uint32_t dimensions_start;
			uint32_t comment_start;
			uint32_t layer_start;
		} __attribute__((packed, aligned(__alignof__(uint32_t))));

		struct TylerMapLayerHeader {
			uint32_t layer_length;
			uint32_t layer_format;
		} __attribute__((packed, aligned(__alignof__(uint32_t))));

		void read_header ( std::istream& parStream, TylerMapHeader& parHeader );
		void read_layer_header ( std::istream& parStream, TylerMapLayerHeader& parHeader );
		void read_dimensions ( std::istream& parStream, uint32_t parDimensions, std::vector<uint32_t>& parMap, std::vector<uint16_t>& parTile );
	} //namespace implem
} //namespace dkh

#include "doorkeeper/implem/tylermapsource.inl"

#endif
