#pragma once

#include "fwd.hpp"

#include <string>

#include "Lexeme.hpp"
#include "Op.hpp"
#include "UniquePtr.hpp"

NAMESPACE_SOUP
{
	struct astNode
	{
		enum Type : uint8_t
		{
			BLOCK = 0,
			LEXEME,
			OP,
		};

		const Type type;

		astNode(Type type) noexcept
			: type(type)
		{
		}

		virtual ~astNode() = default;

		[[nodiscard]] bool isValue() const noexcept;

		[[nodiscard]] std::string toString(const std::string& prefix = {}) const;

		void compile(Writer& w) const;
	};

	struct astBlock : public astNode
	{
		std::vector<UniquePtr<astNode>> children{};
		std::vector<UniquePtr<astNode>> param_literals;

		astBlock(std::vector<UniquePtr<astNode>>&& children = {})
			: astNode(BLOCK), children(std::move(children))
		{
		}

		void checkUnexpected() const;

		[[nodiscard]] std::string toString(std::string prefix = {}) const;

		void compile(Writer& w) const;
	};

	struct LexemeNode : public astNode
	{
		Lexeme lexeme;

		LexemeNode(Lexeme lexeme)
			: astNode(LEXEME), lexeme(std::move(lexeme))
		{
		}

		[[nodiscard]] std::string toString(const std::string& prefix = {}) const;

		void compile(Writer& w) const;
	};

	struct OpNode : public astNode
	{
		Op op;

		OpNode(Op&& op)
			: astNode(OP), op(std::move(op))
		{
		}

		[[nodiscard]] std::string toString(std::string prefix = {}) const;

		void compile(Writer& w) const;
	};
}
