use pom::parser::*;

#[derive(Clone, Debug, PartialEq)]
struct Container {
	containers: Vec<Container>,
	contents: Vec<String>,
}

enum TmpContainerOrContent {
	Container(Container),
	Content(String),
}

fn whitespace<'a>() -> Parser<'a, u8, ()> {
	one_of(b" \t\r\n").repeat(0..).discard()
}

fn linebreak<'a>() -> Parser<'a, u8, ()> {
	sym(b'\r').opt() * sym(b'\n').discard()
}

fn indented<'a>() -> Parser<'a, u8, Vec<u8>> {
	sym(b'\t') * none_of(b"\n\r").repeat(1..) - linebreak()
}

fn empty<'a>() -> Parser<'a, u8, ()> {
	one_of(b" \t").repeat(0..).discard() - linebreak()
}

fn content<'a>() -> Parser<'a, u8, String> {
	none_of(b" \t\r\n").repeat(1..).convert(String::from_utf8) - linebreak()
}

fn subcontainer<'a>() -> Parser<'a, u8, (Vec<Container>, Vec<String>)> {
	(call(container).map(|ctr| TmpContainerOrContent::Container(ctr))
		| content().map(|ctn| TmpContainerOrContent::Content(ctn)))
	.repeat(1..)
	.map(|tmp| {
		tmp.into_iter().fold((vec![], vec![]), |acc, x| match x {
			TmpContainerOrContent::Container(ct) => (
				acc.0.into_iter().chain(vec![ct].into_iter()).collect(),
				acc.1,
			),
			TmpContainerOrContent::Content(cn) => (
				acc.0,
				acc.1.into_iter().chain(vec![cn].into_iter()).collect(),
			),
		})
	})
}

fn container<'a>() -> Parser<'a, u8, Container> {
	seq(b"Container\n")
		* (indented() | empty().map(|()| vec![]))
			.repeat(1..)
			.map(|lines| {
				lines
					.into_iter()
					.filter(|line| line.len() > 0)
					.fold(vec![], |accum, line| {
						accum
							.into_iter()
							.chain(line.into_iter().chain(vec![b'\n'].into_iter()))
							.collect()
					})
			})
			.map(|deden| subcontainer().parse(&deden).expect("subcont"))
			.map(|(containers, contents)| Container {
				containers,
				contents,
			})
}

fn mylang<'a>() -> Parser<'a, u8, Vec<Container>> {
	whitespace() * list(call(container), whitespace())
}

fn main() -> Result<(), ()> {
	let input = br#"
Container
	Container
		a
		b
		c

	1
	2
	3

	Container
		q

Container
	foo
	bar

	Container
		baz
		quux
		"#;

	assert_eq!(
		mylang().parse(input),
		Ok(vec![
			Container {
				containers: vec![
					Container {
						containers: vec![],
						contents: vec!["a".into(), "b".into(), "c".into(),]
					},
					Container {
						containers: vec![],
						contents: vec!["q".into(),]
					}
				],
				contents: vec!["1".into(), "2".into(), "3".into(),]
			},
			Container {
				containers: vec![Container {
					contents: vec!["baz".into(), "quux".into(),],
					containers: vec![],
				},],
				contents: vec!["foo".into(), "bar".into(),]
			},
		])
	);

	Ok(())
}
