/*
 *    Copyright 2013, 2015, 2016 Kai Pastor
 *
 *    This file is part of OpenOrienteering.
 *
 *    OpenOrienteering 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.
 *
 *    OpenOrienteering 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 OpenOrienteering.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef OPENORIENTEERING_OCD_TYPES_V8_H
#define OPENORIENTEERING_OCD_TYPES_V8_H

#include <array>

#include "ocd_types.h"

namespace Ocd
{
	
#pragma pack(push, 1)
	
	// Not found in OCD >= V9.
	// Common default values, not preserved on import.
	struct CmykScreenV8
	{
		quint16 cyan_freq     = 1500;
		quint16 cyan_angle    =  150;
		quint16 magenta_freq  = 1500;
		quint16 magenta_angle =  750;
		quint16 yellow_freq   = 1500;
		quint16 yellow_angle  =    0;
		quint16 black_freq    = 1500;
		quint16 black_angle   =  450;
	};
	
	inline bool operator==(const CmykScreenV8& lhs, const CmykScreenV8& rhs)
	{
		return 0 == memcmp(&lhs, &rhs, sizeof(CmykScreenV8));
	}
	
	inline bool operator!=(const CmykScreenV8& lhs, const CmykScreenV8& rhs)
	{
		return !(lhs == rhs);
	}
	
	struct CmykV8
	{
		quint8 cyan;
		quint8 magenta;
		quint8 yellow;
		quint8 black;
	};
	
	struct ColorInfoV8
	{
		quint16 number;
		quint16 RESERVED_MEMBER;
		CmykV8  cmyk;
		PascalString<31> name;
		quint8  separations[32];
	};
	
	struct SeparationInfoV8
	{
		PascalString<15> name;
		CmykV8  cmyk;
		quint16 raster_freq;
		quint16 raster_angle;
	};
	
	struct SymbolHeaderV8
	{
		quint16 num_colors;
		quint16 num_separations;
		CmykScreenV8 cmyk_screen;
		quint16 RESERVED_MEMBER[2];
		ColorInfoV8 color_info[256];
		SeparationInfoV8 separation_info[32];
	};
	
	struct FileHeaderV8 : public FileHeaderGeneric
	{
		quint32 first_symbol_block;
		quint32 first_object_block;
		quint32 setup_pos;
		quint32 setup_size;
		quint32 info_pos;
		quint32 info_size;
		quint32 first_string_block;
		quint32 RESERVED_MEMBER[3];
		SymbolHeaderV8 symbol_header;
	};
	
	
	struct IconV9;
	
	struct IconV8
	{
		quint8  bits[264];
		static constexpr unsigned length() { return 264; }
		
		static constexpr int height() { return 22; }
		static constexpr int width() { return 22; }
		
		template<typename RGB>
		static std::array<RGB, 16> palette() {
			return {
				RGB{   0,   0,   0 },
				RGB{ 128,   0,   0 },
				RGB{ 0,   128,   0 },
				RGB{ 128, 128,   0 },
				RGB{   0,   0, 128 },
				RGB{ 128,   0, 128 },
				RGB{   0, 128, 128 },
				RGB{ 128, 128, 128 },
				RGB{ 192, 192, 192 },
				RGB{ 255,   0,   0 },
				RGB{   0, 255,   0 },
				RGB{ 255, 255,   0 },
				RGB{   0,   0, 255 },
				RGB{ 255,   0, 255 },
				RGB{   0, 255, 255 },
				RGB{ 255, 255, 255 }
			};
		}
		
		IconV9 uncompress() const;  // may throw
	};
	
	bool operator==(const IconV8& lhs, const IconV8& rhs);
	
	inline bool operator!=(const IconV8& lhs, const IconV8& rhs) { return !(lhs == rhs); }
	
	struct BaseSymbolV8
	{
		using IndexEntryType = SymbolIndexEntry;
		static const int symbol_number_factor = 10;
		
		quint16 size;
		quint16 number;
		quint16 type;
		quint8  type2;
		quint8  flags;
		quint16 extent;
		quint8  selected;
		quint8  status;
		quint16 RESERVED_MEMBER[2];
		qint32  file_pos;
		quint8  colors[32];
		PascalString<31> description;
		IconV8  icon;
	};
	
	struct PointSymbolElementV8
	{
		quint16 type;
		quint16 flags;
		quint16 color;
		qint16  line_width;
		qint16  diameter;
		quint16 num_coords;
		quint32 RESERVED_MEMBER;
		
		enum PointSymbolElementTypes
		{
			TypeLine   = 1,
			TypeArea   = 2,
			TypeCircle = 3,
			TypeDot    = 4
		};
		
		enum Flags
		{
			NoFlags        = 0,
			RoundStyle     = 1,
			FlatMiterStyle = 4,
		};
	};
	
	struct PointSymbolV8
	{
		using BaseSymbol = BaseSymbolV8;
		using Element    = PointSymbolElementV8;
		
		BaseSymbol base;
		
		quint16 data_size;
		quint16 RESERVED_MEMBER;
		Element begin_of_elements[1];
	};
	
	struct LineSymbolCommonV8
	{
		quint16 line_color;
		quint16 line_width;
		quint16 line_style;
		qint16  dist_from_start;
		qint16  dist_from_end;
		qint16  main_length;
		qint16  end_length;
		qint16  main_gap;
		qint16  sec_gap;
		qint16  end_gap;
		qint16  min_sym;
		qint16  num_prim_sym;
		qint16  prim_sym_dist;
		quint16 double_mode;
		quint16 double_flags;
		quint16 double_color;
		quint16 double_left_color;
		quint16 double_right_color;
		qint16  double_width;
		qint16  double_left_width;
		qint16  double_right_width;
		qint16  double_length;
		qint16  double_gap;
		quint16 double_background_color_V11; /// \since V11
		quint16 RESERVED_MEMBER[2];
		quint16 dec_mode;
		quint16 dec_last;
		quint16 RESERVED_MEMBER;
		quint16 framing_color;
		qint16  framing_width;
		quint16 framing_style;
		quint16 primary_data_size;
		quint16 secondary_data_size;
		quint16 corner_data_size;
		quint16 start_data_size;
		quint16 end_data_size;
		quint8  active_symbols_V11;          /// \since V11
		quint8  RESERVED_MEMBER;
		
		enum LineStyleFlag
		{
			BevelJoin_FlatCap    = 0,
			RoundJoin_RoundCap   = 1,
			BevelJoin_PointedCap = 2,
			RoundJoin_PointedCap = 3,
			MiterJoin_FlatCap    = 4,
			MiterJoin_PointedCap = 6
		};
		
		enum DoubleLineFlags
		{
			DoubleFlagFillColorOn       = 1,
			DoubleFlagBackgroundColorOn = 2,
		};
		
		enum DoubleLineMode
		{
			DoubleLineOff              = 0,
			DoubleLineContinuous       = 1,
			DoubleLineLeftBorderDashed = 2,
			DoubleLineBordersDashed    = 3,
			DoubleLineAllDashed        = 4,
		};
	};

	struct LineSymbolV8
	{
		using BaseSymbol = BaseSymbolV8;
		using Element    = PointSymbolElementV8;
		
		BaseSymbol base;
		
		LineSymbolCommonV8 common;
		
		Element begin_of_elements[1];
	};
	
	struct AreaSymbolCommonV8
	{
		quint16 fill_color;
		quint16 hatch_mode;
		quint16 hatch_color;
		quint16 hatch_line_width;
		quint16 hatch_dist;
		qint16  hatch_angle_1;
		qint16  hatch_angle_2;
		quint8  fill_on_V9;         /// \since V9
		quint8  border_on_V9;       /// \since V9
		quint8  structure_mode;
		quint8  structure_draw_V12; /// \since V12
		quint16 structure_width;
		quint16 structure_height;
		qint16  structure_angle;
	};
	
	struct AreaSymbolV8
	{
		using BaseSymbol = BaseSymbolV8;
		using Element    = PointSymbolElementV8;
		
		BaseSymbol base;
		
		quint16 RESERVED_MEMBER;    // formerly known as area_flags
		quint16 fill_on;
		AreaSymbolCommonV8 common;
		quint16 RESERVED_MEMBER;
		quint16 data_size;
		
		Element begin_of_elements[1];
	};
	
	struct BasicTextAttributesV8
	{
		quint16 color;
		quint16 font_size;
		quint16 font_weight;
		quint8  font_italic;
		quint8  charset_V8_ONLY;         /// V8 text symbols only
		quint16 char_spacing;
		quint16 word_spacing;
		quint16 alignment;
	};
	
	struct SpecialTextAttributesV8
	{
		quint16 line_spacing;
		qint16  para_spacing;
		quint16 indent_first_line;
		quint16 indent_other_lines;
		quint16 num_tabs;
		quint32 tab_pos[32];
		quint16 line_below_on;
		quint16 line_below_color;
		quint16 line_below_width;
		quint16 line_below_offset;
	};
	
	struct FramingAttributesV8
	{
		quint8  mode;                    /// 16 bit in V8
		quint8  line_style_V9;           /// \since V9
		quint8  point_symbol_on_V10;     /// \since V10
		quint32 point_symbol_number_V10; /// \since V10
		char    RESERVED_MEMBER[19];
		quint16 border_left_V9;          /// \since V9; TextSymbol only
		quint16 border_bottom_V9;        /// \since V9; TextSymbol only
		quint16 border_right_V9;         /// \since V9; TextSymbol only
		quint16 border_top_V9;           /// \since V9; TextSymbol only
		quint16 color;
		quint16 line_width;
		quint16 font_weight;             /// TextSymbol only
		quint16 italic;                  /// TextSymbol only
		quint16 offset_x;
		quint16 offset_y;
	};

	struct TextSymbolV8
	{
		using BaseSymbol = BaseSymbolV8;
		
		BaseSymbol base;
		
		PascalString<31>        font_name;
		BasicTextAttributesV8   basic;
		SpecialTextAttributesV8 special;
		quint16                 RESERVED_MEMBER;
		FramingAttributesV8     framing;
	};
	
	struct LineTextSymbolV8 /// \todo use and test...
	{
		using BaseSymbol = BaseSymbolV8;
		
		BaseSymbol base;
		
		PascalString<31>      font_name;
		BasicTextAttributesV8 basic;
		FramingAttributesV8   framing;
	};
	
	struct RectangleSymbolV8
	{
		using BaseSymbol = BaseSymbolV8;
		
		BaseSymbol base;
		
		quint16 line_color;
		quint16 line_width;
		quint16 corner_radius;
		quint16 grid_flags;
		quint16 cell_width;
		quint16 cell_height;
		quint16 RESERVED_MEMBER[2];
		quint16 unnumbered_cells;
		PascalString<3> unnumbered_text;
		quint16 RESERVED_MEMBER;
		PascalString<31> RESERVED_MEMBER;
		quint16 RESERVED_MEMBER[6];
	};
	
	struct ObjectIndexEntryV8
	{
		OcdPoint32 bottom_left_bound;
		OcdPoint32 top_right_bound;
		quint32 pos;
		quint16 size;      /// Different interpretation for version < 8
		qint16  symbol;
	};
	
	struct ObjectV8
	{
		using IndexEntryType = ObjectIndexEntryV8;
		
		qint16  symbol;
		quint8  type;
		quint8  unicode;
		quint16 num_items;
		quint16 num_text;
		qint16  angle;
		quint16 RESERVED_MEMBER;
		quint32 RESERVED_MEMBER;
		PascalString<15> reserved;
		
		OcdPoint32 coords[1];
	};
	
	typedef double PascalDouble;
	
	struct GpsPointV8
	{
		OcdPoint32 map_point;
		PascalDouble lat;
		PascalDouble lon;
		PascalString<15> name;
	};
	
	struct PrintSetupV8
	{
		OcdPoint32 bottom_left;
		OcdPoint32 top_right;
		quint16 grid_enabled;
		qint16  grid_color;
		qint16  overlap_x;
		qint16  overlap_y;
		PascalDouble scale;
		quint16 intensity;
		quint16 line_width;
		quint16 RESERVED_MEMBER[4];
	};
	
	struct ExportSetupV8
	{
		OcdPoint32 bottom_left;
		OcdPoint32 top_right;
	};
	
	struct ZoomRectV8
	{
		PascalDouble zoom;
		OcdPoint32 center;
	};
	
	struct SetupV8
	{
		OcdPoint32 center;
		PascalDouble grid_dist;
		quint16 work_mode;
		quint16 line_mode;
		quint16 edit_mode;
		qint16  selected_symbol;
		PascalDouble map_scale;
		PascalDouble real_offset_x;
		PascalDouble real_offset_y;
		PascalDouble real_angle;
		PascalDouble real_grid;
		PascalDouble gps_angle;
		GpsPointV8 gps_adjustment[12];
		quint32 num_gps_adjustments;
		PascalDouble RESERVED_MEMBER[2];
		OcdPoint32 RESERVED_MEMBER;
		char    RESERVED_MEMBER[256];
		quint16 RESERVED_MEMBER[2];
		PascalDouble RESERVED_MEMBER;
		OcdPoint32 RESERVED_MEMBER;
		PascalDouble RESERVED_MEMBER;
		PrintSetupV8 print_setup;
		ExportSetupV8 export_setup;
		PascalDouble zoom;
		ZoomRectV8 zoom_history[8];
		quint32 zoom_history_size;
		// V6 header ends here, but Mapper doesn't use the following fields.
		quint16 real_coords_IGNORED;
		char    filename_IGNORED[256];
		quint16 hatch_areas_IGNORED;
		quint16 dim_templates_IGNORED;
		quint16 hide_templates_IGNORED;
		quint16 template_mode_IGNORED;
		qint16  template_color_IGNORED;
	};
	
#pragma pack(pop)
	
	/** OCD file format version 8 trait. */
	struct FormatV8
	{
		constexpr static quint16 version = 8;
		using FileHeader      = FileHeaderV8;
		using BaseSymbol      = BaseSymbolV8;
		using PointSymbol     = PointSymbolV8;
		using LineSymbol      = LineSymbolV8;
		using AreaSymbol      = AreaSymbolV8;
		using TextSymbol      = TextSymbolV8;
		using LineTextSymbol  = LineTextSymbolV8;
		using RectangleSymbol = RectangleSymbolV8;
		using Object          = ObjectV8;
		using Encoding        = Custom8BitEncoding;
	};
}


OCD_EXPLICIT_INSTANTIATION(extern template, Ocd::FormatV8)


#endif // OPENORIENTEERING_OCD_TYPES_V8_H
