1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use super::*;

impl <'inp, Storage, Pos> GridBuf <Storage, Pos, 2>
	where
		Pos: GridPosParse,
		Storage: Clone + FromIterator <Storage::Item> + GridStorage,
		Storage::Item: Clone + Default + FromParser <'inp> {

	#[ allow (clippy::missing_inline_in_public_items) ]
	pub fn parse_with_delim_and_fn (
		parser: & mut Parser <'inp>,
		delim: impl ParseDelimiter,
		default_fn: impl Fn () -> Storage::Item,
		parse_fn: impl Fn (& mut Parser <'inp>) -> ParseResult <Storage::Item> + Clone,
	) -> ParseResult <Self> {
		let lines: Vec <Vec <Storage::Item>> = parser.delim_fn ("\n", |parser| {
			parser.any ().of (|parser| {
				let items: Vec <Storage::Item> = parser.delim_fn (delim, parse_fn.clone ()).collect ();
				if items.is_empty () { return Err (parser.err ()) }
				Ok (items)
			}).done ()
		}).collect ();
		if lines.is_empty () { return Err (parser.err ()) }
		let num_lines = lines.len ();
		let num_lines_coord = Pos::Coord::from_usize (num_lines).map_err (|_err| parser.err ()) ?;
		let num_cols = lines.iter ().map (Vec::len).max ().unwrap_or (0);
		let num_cols_coord = Pos::Coord::from_usize (num_cols).map_err (|_err| parser.err ()) ?;
		let grid_size = Pos::grid_parse_grid_size (num_lines_coord, num_cols_coord);
		let line_offset = Pos::grid_parse_row_offset (num_lines, num_cols);
		let tile_offset = Pos::grid_parse_col_offset (num_lines, num_cols);
		let first_idx = Pos::grid_parse_first_index (num_lines, num_cols).pan_isize ();
		let mut grid_vec = vec! [ default_fn (); num_lines * num_cols ];
		let mut line_idx = first_idx;
		for line in lines.iter () {
			let mut tile_idx = line_idx;
			for tile in line.iter ().cloned ()
					.chain (std::iter::repeat (default_fn ()))
					.take (num_cols) {
				grid_vec [tile_idx.pan_usize ()] = tile;
				tile_idx += tile_offset;
			}
			line_idx += line_offset;
		}
		Ok (Self::wrap_size (grid_vec.into_iter ().collect (), grid_size))
	}

	#[ allow (clippy::missing_inline_in_public_items) ]
	pub fn parse_with_delim (
		parser: & mut Parser <'inp>,
		default_fn: impl Fn () -> Storage::Item,
		delim: impl ParseDelimiter,
	) -> ParseResult <Self> {
		Self::parse_with_delim_and_fn (parser, delim, default_fn, Parser::item)
	}

	#[ allow (clippy::missing_inline_in_public_items) ]
	pub fn parse_with_fn (
		parser: & mut Parser <'inp>,
		default_fn: impl Fn () -> Storage::Item,
		parse_fn: impl Fn (& mut Parser <'inp>) -> ParseResult <Storage::Item> + Clone,
	) -> ParseResult <Self> {
		let lines: Vec <Vec <Storage::Item>> = parser.delim_fn ("\n", |parser| {
			parser.any ().of (|parser| {
				let items: Vec <Storage::Item> = parser.repeat (parse_fn.clone ()).collect ();
				if items.is_empty () { return Err (parser.err ()) }
				Ok (items)
			}).done ()
		}).collect ();
		if lines.is_empty () { return Err (parser.err ()) }
		let num_lines = lines.len ();
		let num_lines_coord = Pos::Coord::from_usize (num_lines).map_err (|_err| parser.err ()) ?;
		let num_cols = lines.iter ().map (Vec::len).max ().unwrap_or (0);
		let num_cols_coord = Pos::Coord::from_usize (num_cols).map_err (|_err| parser.err ()) ?;
		let grid_size = Pos::grid_parse_grid_size (num_lines_coord, num_cols_coord);
		let line_offset = Pos::grid_parse_row_offset (num_lines, num_cols);
		let tile_offset = Pos::grid_parse_col_offset (num_lines, num_cols);
		let first_idx = Pos::grid_parse_first_index (num_lines, num_cols).pan_isize ();
		let mut grid_vec = vec! [ default_fn (); num_lines * num_cols ];
		let mut line_idx = first_idx;
		for line in lines.iter () {
			let mut tile_idx = line_idx;
			for tile in line.iter ().cloned ()
					.chain (std::iter::repeat (default_fn ()))
					.take (num_cols) {
				grid_vec [tile_idx.pan_usize ()] = tile;
				tile_idx += tile_offset;
			}
			line_idx += line_offset;
		}
		Ok (Self::wrap_size (grid_vec.into_iter ().collect (), grid_size))
	}

}

impl <'inp, Storage, Pos> FromParser <'inp> for GridBuf <Storage, Pos, 2>
	where
		Pos: GridPosParse,
		Storage: Clone + FromIterator <Storage::Item> + GridStorage,
		Storage::Item: Clone + Default + FromParser <'inp> {

	#[ allow (clippy::missing_inline_in_public_items) ]
	fn from_parser (parser: & mut Parser <'inp>) -> ParseResult <Self> {
		Self::parse_with_fn (parser, Storage::Item::default, Parser::item)
	}

}

pub trait GridPosParse: GridPos <2> {
	fn grid_parse_grid_size (rows: Self::Coord, cols: Self::Coord) -> Self;
	fn grid_parse_row_offset (rows: usize, cols: usize) -> isize;
	fn grid_parse_col_offset (rows: usize, cols: usize) -> isize;
	fn grid_parse_first_index (rows: usize, cols: usize) -> usize;
}

impl <Pos: GridPosDisplayAuto> GridPosParse for Pos {

	#[ inline ]
	fn grid_parse_grid_size (rows: Self::Coord, cols: Self::Coord) -> Self {
		use GridPosDisplayType::{ DownRight, RightUp, UpRight, UpRightSlant };
		Self::from_array (match Self::DISPLAY_TYPE {
			DownRight | UpRight | UpRightSlant => [ rows, cols ],
			RightUp => [ cols, rows ],
		})
	}

	#[ inline ]
	fn grid_parse_row_offset (_rows: usize, cols: usize) -> isize {
		use GridPosDisplayType::{ DownRight, RightUp, UpRight, UpRightSlant };
		match Self::DISPLAY_TYPE {
			DownRight | UpRight | UpRightSlant => cols.pan_isize (),
			RightUp => -1_isize,
		}
	}

	#[ inline ]
	fn grid_parse_col_offset (rows: usize, _cols: usize) -> isize {
		use GridPosDisplayType::{ DownRight, RightUp, UpRight, UpRightSlant };
		match Self::DISPLAY_TYPE {
			DownRight => 1_isize,
			RightUp => rows.pan_isize (),
			UpRight | UpRightSlant => -1_isize,
		}
	}

	#[ inline ]
	fn grid_parse_first_index (rows: usize, cols: usize) -> usize {
		use GridPosDisplayType::{ DownRight, RightUp, UpRight, UpRightSlant };
		match Self::DISPLAY_TYPE {
			DownRight => 0_usize,
			RightUp => rows.pan_usize () - 1,
			UpRight | UpRightSlant => (rows.pan_usize () - 1) * cols.pan_usize (),
		}
	}

}