const { inspect } = require("util");
function* prepare(str) {
	for (let i = 0; i < str.length; i++) {
		yield {
			current: str[i],
			next: (n = 1) => str.slice(i + 1, i + 1 + n),
			prev: (n = 1) => str.slice(i - n, i),
			skip: (n = 1) => (i += n),
		};
	}
}

function Parse(str, inner = false) {
	let context = null;
	let buffer = "";
	let AST = [];
	let link = "";

	const flush = () => {
		if (buffer) {
			if (context === null && inner) {
				AST.push(buffer);
			} else {
				if (context === "url") {
					AST.push({
						type: context,
						children: Parse(buffer, true),
						url: link,
					});
				} else {
					AST.push({
						type: context || "text",
						children: context === null ? [buffer] : Parse(buffer, true),
					});
				}
			}
			buffer = "";
		}
		context = null;
		link = "";
	};

	for (const char of prepare(str)) {
		const currentContext = context;
		if (
			(!context || context === "bold") &&
			char.current === "*" &&
			char.next() === "*"
		) {
			flush();
			if (!currentContext) {
				context = "bold";
			}
			char.skip();
		} else if ((!context || context === "italics") && char.current === "_") {
			flush();
			if (!currentContext) {
				context = "italics";
			}
		} else if (
			(!context || context === "strikethrough") &&
			char.current === "~" &&
			char.next() === "~"
		) {
			flush();
			if (!currentContext) {
				context = "strikethrough";
			}
			char.skip();
		} else if (
			(!context || context === "underline") &&
			char.current === "=" &&
			char.next() === "="
		) {
			flush();
			if (!currentContext) {
				context = "underline";
			}
			char.skip();
		} else if (
			(char.current === "[" || char.current === "]") &&
			(!context || context === "link")
		) {
			if (char.current === "[") {
				flush();
				context = "link";
			}
			if (inner) {
				buffer += char.current;
			}
		} else if (
			(context === "link" || !context || context === "url") &&
			(char.current === "(" || char.current === ")") &&
			!inner
		) {
			if (char.prev() === "]") {
				if (!currentContext || currentContext === "link") {
					context = "url";
				}
			} else {
				flush();
			}
		} else {
			if (context === "url") {
				link += char.current;
			} else {
				buffer += char.current;
			}
		}
	}

	const updater = char => {
		context = null;
		let result = Parse(buffer);
		for (const check of result) {
			if (check.type !== "text") {
				if (!AST.length) {
					buffer = char;
					flush();
				}
				AST.push(check);
				buffer = "";
			} else {
				buffer = check.children[0];
				if (!AST.length) {
					buffer = char + buffer;
				}
				flush();
			}
		}
	};

	if (context === "bold") {
		updater("**");
	} else if (context === "italics") {
		updater("_");
	} else if (context === "strikethrough") {
		updater("~~");
	} else if (context == "underline") {
		updater("==");
	}

	if (buffer) {
		context = null;
		flush();
	}

	return AST;
}

module.exports = Parse;