#include <VlppWorkflowCompiler.h>
#include "../W05_Lib/W05_Lib.h"

using namespace vl::console;
using namespace vl::collections;
using namespace vl::stream;
using namespace vl::filesystem;
using namespace vl::reflection::description;
using namespace vl::workflow;
using namespace vl::workflow::analyzer;
using namespace vl::workflow::emitter;
using namespace vl::workflow::cppcodegen;

const wchar_t ScriptCode[] = LR"Workflow(

module sampleModule;

using myapi::*;

namespace myscript
{
	class MyApp
	{
		new(){}

		func CreateScripting(): IScripting^
		{
			return new IScripting^
			{
				override func Execute(name: string): void
				{
					App::Print($"Hello, $(name)!");
				}
			};
		}
	}
}

)Workflow";

int wmain(int argc, const wchar_t* argv[])
{
	// start reflection
	LoadPredefinedTypes();
	WfLoadLibraryTypes();
	LoadMyApiTypes();
	GetGlobalTypeManager()->Load();

	{
		// prepare Workflow script code
		List<WString> codes;
		codes.Add(WString::Unmanaged(ScriptCode));

		// compile code
		// WfLexicalScopeManager is required because we produce assembly and C++ code at the same time
		List<glr::ParsingError> errors;
		workflow::Parser parser;
		WfLexicalScopeManager manager(parser, workflow::analyzer::WfCpuArchitecture::AsExecutable);
		auto assembly = Compile(&manager, codes, errors);
		CHECK_ERROR(assembly && errors.Count() == 0, L"Please check the 'errors' variable.");

		// save the assembly to file
		{
			FilePath assemblyPath = FilePath(argv[0]).GetFolder() / L"WorkflowAssembly.bin";
			Console::WriteLine(L"Writing the assembly to: " + assemblyPath.GetFullPath());
			FileStream fileStream(assemblyPath.GetFullPath(), FileStream::WriteOnly);
			assembly->Serialize(fileStream);
		}

		// generate C++ code
		{
			FilePath cppFolder = FilePath(argv[0]).GetFolder();
			while (cppFolder.GetName() != L"Console_Workflow")
			{
				// the loop exist because x64 binaries are stored in ../x64/Debug instead of ../Debug
				cppFolder = cppFolder.GetFolder();
			}
			cppFolder = cppFolder / L"W05_StaticRun" / L"Generated";
			Console::WriteLine(L"Writing C++ files to: " + cppFolder.GetFullPath());

			auto cppInput = Ptr(new WfCppInput(L"W05Script"));
			cppInput->comment = L"THIS FILE IS GENERATED BY W05_COMPILE";
			cppInput->headerGuardPrefix = L"W05_SCRIPT_";
			cppInput->normalIncludes.Add(L"../../W05_Lib/W05_Lib.h");
			cppInput->normalIncludes.Add(L"VlppWorkflowLibrary.h");

			auto cppOutput = GenerateCppFiles(cppInput, &manager);
			for (vint i = 0; i < cppOutput->cppFiles.Count(); i++)
			{
				auto key = cppOutput->cppFiles.Keys()[i];
				auto value = cppOutput->cppFiles.Values()[i];
				Console::WriteLine(L"    " + key);
				File(cppFolder / key).WriteAllText(value, false, BomEncoder::Utf8);
			}
		}
	}
	Console::WriteLine(L"Finished!");

	// stop reflection
	DestroyGlobalTypeManager();
}