#include <pthread.h>

#include <CTPP2.hpp>

using namespace CTPP;

struct thread_context
{
	// Output mutex
	pthread_mutex_t     output_mutex;
	// Template access mutex
	pthread_mutex_t     template_mutex;
	// Max handlers
	INT_32              max_handlers;
	// Set of templates
	std::map<std::string, VMFileLoader *>    templates;
};

// Thread function
static void * thread_function(void * pContext)
{
	thread_context * pThreadContext = (thread_context*)pContext;

	pthread_mutex_lock(&(pThreadContext -> output_mutex));
	fprintf(stderr, "Initilalizing...\n");
	pthread_mutex_unlock(&(pThreadContext -> output_mutex));

	// Create per-thread instances
	// Syscall factory
	SyscallFactory * pSyscallFactory = new SyscallFactory(pThreadContext -> max_handlers);
	// Init standard library
	STDLibInitializer::InitLibrary(*pSyscallFactory);
	// Virtual machine
	VM * pVM = new VM(*pSyscallFactory);
	// Okay, all done with thread-specific

	// Fill data
	CDT oData;
	oData["hello"] = "Hello, World!";

	pthread_mutex_lock(&(pThreadContext -> output_mutex));
	fprintf(stderr, "Okay, ready to work\n");
	pthread_mutex_unlock(&(pThreadContext -> output_mutex));

	// Perform some work
	const VMMemoryCore * pVMMemoryCore = NULL;
	for (UINT_32 iCount = 0; iCount < 10000; ++ iCount)
	{
		std::string sResult;
		StringOutputCollector  oDataCollector(sResult);

		// Get template, thread-safe
		pthread_mutex_lock(&(pThreadContext -> template_mutex));
		std::map<std::string, VMFileLoader *>::iterator itLoader = pThreadContext -> templates.find("hello.ct2");
		if (itLoader == pThreadContext -> templates.end()) { continue; }
		pVMMemoryCore = itLoader -> second -> GetCore();
		pthread_mutex_unlock(&(pThreadContext -> template_mutex));

		// Run VM
		pVM -> Init(oDataCollector, *pVMMemoryCore);
		UINT_32 iIP = 0;
		pVM -> Run(*pVMMemoryCore, iIP, oData);

		// All done, print results
		pthread_mutex_lock(&(pThreadContext -> output_mutex));
		fprintf(stderr, "%s", sResult.c_str());
		pthread_mutex_unlock(&(pThreadContext -> output_mutex));
	}

	delete pVM;
	delete pSyscallFactory;
}


#define MAX_THREADS 100

int main(int argc, char ** argv)
{
	thread_context oContext;

	// Load file
	oContext.templates["hello.ct2"] = new VMFileLoader("hello.ct2");
	oContext.max_handlers = 1024;

	// Init mutexes
	pthread_mutex_init(&oContext.template_mutex, NULL);

	pthread_mutex_init(&oContext.output_mutex, NULL);

	pthread_attr_t        oAttrs;
	pthread_attr_init(&oAttrs);
	pthread_attr_setdetachstate(&oAttrs, PTHREAD_CREATE_JOINABLE);

	pthread_t aThreads[MAX_THREADS];

	printf("Creating %d threads\n", MAX_THREADS);
	INT_32 iPos;
	for (iPos = 0; iPos < MAX_THREADS; ++iPos)
	{
		INT_32 iRC = pthread_create(&aThreads[iPos], &oAttrs, thread_function, &oContext);
	}

	printf("Wait for results\n");
	for (iPos = 0; iPos < MAX_THREADS; ++iPos)
	{
		INT_32 iRC = pthread_join(aThreads[iPos], NULL);
	}

	fprintf(stderr, "Cleanup and exit\n");
	pthread_attr_destroy(&oAttrs);
	pthread_mutex_destroy(&oContext.template_mutex);
	pthread_mutex_destroy(&oContext.output_mutex);
}
