#pragma once
#include "c-buildcfg.hh"
#include "c-filewatcher.hh"
#include "c-opast.hh"
#include "c-project.hh"
#include <ebcl/SRDData.hh>


/*= PARSER ===================================================================*/

// Parser output. Consists in a root node as well as other details (table of
// constants, data types, etc...)
struct T_OpsParserOutput
{
	opast::T_RootNode root;
	T_KeyValueTable< T_String , opast::E_DataType > types;
	ebcl::T_Set< T_FSPath > files{ ebcl::UseTag< ebcl::ArrayBacked< 16 > >( ) };
};

// The actual parser
class T_OpsParser : public ebcl::A_PrivateImplementation
{
    private:
	F_OPLogger logger_{ []( auto , auto ) { } };
	T_Array< ebcl::T_SRDError > errors_;
	T_OwnPtr< T_OpsParserOutput > output_;

    public:
	T_OpsParser( ) noexcept;

	void setLogger( F_OPLogger logger )
	{
		if ( logger ) {
			logger_ = std::move( logger );
		} else {
			logger_ = []( auto , auto ) { };
		}
	}

	bool parse( T_FSPath const& filePath ,
			ebcl::T_SRDList const& input ) noexcept;

	T_Array< ebcl::T_SRDError > const& errors( ) const noexcept
		{ return errors_; }

	T_OwnPtr< T_OpsParserOutput > result( ) noexcept
		{ return std::move( output_ ); }
};


/*= COMPILER ===================================================================*/

namespace ops { struct T_OpProgram; using P_OpProgram = T_OwnPtr< T_OpProgram >; }

// Generates bytecode based on the type-checked tree produced by the parser
class T_OpsCompiler : public ebcl::A_PrivateImplementation
{
    private:
	F_OPLogger logger_{ []( auto , auto ) { } };

    public:
	T_OpsCompiler( bool noUIInstructions = false ) noexcept;

	void setLogger( F_OPLogger logger )
	{
		if ( logger ) {
			logger_ = std::move( logger );
		} else {
			logger_ = []( auto , auto ) { };
		}
	}

	ops::P_OpProgram compile( T_OpsParserOutput const& input ) noexcept;
};


/*= SCRIPT MANAGER =============================================================*/

// The script manager watches over the demo's script, attempting to reload it if
// it is changed.
class T_ScriptManager : public A_ProjectPathListener
{
    public:
	T_ScriptManager( ) noexcept;
	~T_ScriptManager( );

	void enable( ) noexcept;
	void disable( ) noexcept
		{ enabled_ = false; }
	bool enabled( ) const noexcept
		{ return enabled_; }

	bool hasNewProgram( ) const noexcept
		{ return bool( output_ ); }

	ops::P_OpProgram program( ) noexcept
		{ return std::move( output_ ); }

	T_Array< ebcl::T_SRDError > const& errors( ) const noexcept
		{ return errors_; }

    private:
	bool enabled_{ false };

	T_FSPath path_;
	T_WatchedFiles watcher_;

	T_OpsParser parser_;
	T_OpsCompiler compiler_;

	ops::P_OpProgram output_;
	T_Array< ebcl::T_SRDError > errors_;

	void loadScript( ) noexcept;

    protected:
	void projectPathChanged( ) noexcept override;
};