//
//  XTVmHostIfc.cpp
//  TadsTerp
//
//  Created by Rune Berg on 24/05/14.
//  Copyright (c) 2014 Rune Berg. All rights reserved.
//


#include "XTVmHostIfc.h"
#import "XTLogger.h"


static XTLogger *logger = [XTLogger loggerNamed:@"XTVmHostIfc"];

#define XTOSIFC_DEF_SELNAME(sn) NSString *selName = sn;
#define XTOSIFC_TRACE_ENTRY(sn) XTOSIFC_DEF_SELNAME(sn); [logger trace:@"%@", XT_SELNAME];


XTVmHostIfc::XTVmHostIfc( struct appctxdef* appctx )
{
	XTOSIFC_TRACE_ENTRY(@"ctor");

	this->fAppctx = appctx;
	
	this->fCmapResLoader = new CResLoader("./");
		//TODO "./" is what qtads does. but is it correct? even a TODO i qtads src...
}

XTVmHostIfc::~XTVmHostIfc() //override
{
	delete this->fCmapResLoader;
}

//
// CVmHostIfc interface implementation.
//

//TODO needs a good looking over!! esp wrt T2 app ctx

int
XTVmHostIfc::get_io_safety_read() //override
{
	XTOSIFC_DEF_SELNAME(@"get_io_safety_read");
	
	int readLvl = 999;
	(*this->fAppctx->get_io_safety_level)(nullptr, &readLvl, nullptr);
	
	XT_TRACE_1(@"-> %d", readLvl);
	
	return readLvl;
}

int
XTVmHostIfc::get_io_safety_write() //override
{
	XTOSIFC_DEF_SELNAME(@"get_io_safety_write");

	int writeLvl = 999;
	(*this->fAppctx->get_io_safety_level)(nullptr, nullptr, &writeLvl);

	XT_TRACE_1(@"-> %d", writeLvl);
	
	return writeLvl;
}

void
XTVmHostIfc::set_io_safety( int readLvl, int writeLvl ) //override
{
	// Not relevant for XTads
}

// FIXME: Implement
void
XTVmHostIfc::get_net_safety( int* client_level, int* server_level ) //override
{
	XTOSIFC_DEF_SELNAME(@"get_net_safety");

	if (client_level != 0)
		*client_level = 0;
	if (server_level != 0)
		*server_level = 0;
}

// FIXME: Implement
void
XTVmHostIfc::set_net_safety( int /*client_level*/, int /*server_level*/ ) //override
{
	XTOSIFC_DEF_SELNAME(@"set_net_safety");
}

class CResLoader*
XTVmHostIfc::get_sys_res_loader() //override
{
	XTOSIFC_TRACE_ENTRY(@"get_sys_res_loader");
	
	return this->fCmapResLoader;
}

void
XTVmHostIfc::set_image_name( const char* fname ) //override
{
	XTOSIFC_DEF_SELNAME(@"set_image_name");
	XT_TRACE_1(@"fname=\"%s\"", fname);

	if (this->fAppctx != 0 and this->fAppctx->set_game_name != 0) {
		(*this->fAppctx->set_game_name)(this->fAppctx->set_game_name_ctx, fname);
	} else {
		XT_WARN_0(@"no app ctx, or app ctx doesn't support this");
	}
}

void
XTVmHostIfc::set_res_dir( const char* fname ) //override
{
	XTOSIFC_DEF_SELNAME(@"set_res_dir");
	XT_TRACE_1(@"fname=\"%s\"", fname);
	
	// Pass it through the app context if possible.
	if (this->fAppctx != 0 and this->fAppctx->set_res_dir != 0) {
		(*this->fAppctx->set_res_dir)(this->fAppctx->set_res_dir_ctx, fname);
	} else {
		XT_WARN_0(@"no app ctx, or app ctx doesn't support this");
	}
}

/*
 *   Add a resource collection file.  The return value is a non-zero file
 *   number assigned by the host system; the VM uses this number in
 *   subsequent calls to add_resource() to add resources from this file.
 *   The VM cannot add any resources for a file until it first adds the
 *   file with this routine.
 */
int
XTVmHostIfc::add_resfile( const char* fname ) //override
{
	XTOSIFC_DEF_SELNAME(@"add_resfile");
	XT_TRACE_1(@"fname=\"%s\"", fname);
	
	// Pass it through the app context if possible.
	if (this->fAppctx != 0 and this->fAppctx->add_resfile != 0) {
		return (*this->fAppctx->add_resfile)(this->fAppctx->add_resfile_ctx, fname);
	} else {
		XT_WARN_0(@"no app ctx, or app ctx doesn't support this");
		return 0;
	}
}

int
XTVmHostIfc::can_add_resfiles() //override
{
	XTOSIFC_DEF_SELNAME(@"can_add_resfiles");

	// If the add_resfile function is defined in the application context,
	// we support adding resource files.
	int res = (this->fAppctx != 0 and this->fAppctx->add_resfile != 0);
	
	XT_TRACE_1(@"-> %d", res);

	return res;
}

void
XTVmHostIfc::add_resource( unsigned long ofs, unsigned long siz, const char* res_name, size_t res_name_len,
			 int fileno ) //override
{
	XTOSIFC_DEF_SELNAME(@"add_resource");
	XT_TRACE_1(@"res_name=\"%s\"", res_name);

	// Pass it through the app context if possible.
	if (this->fAppctx != 0 and this->fAppctx->add_resource != 0) {
		(*this->fAppctx->add_resource)(this->fAppctx->add_resource_ctx, ofs, siz, res_name,
									   res_name_len, fileno);
	} else {
		XT_WARN_0(@"no app ctx, or app ctx doesn't support this");
	}
}

void
XTVmHostIfc::add_resource( const char* fname, size_t fname_len, const char* res_name, size_t res_name_len) //override
{
	XTOSIFC_DEF_SELNAME(@"add_resource");
	XT_TRACE_2(@"fname=\"%s\" res_name=\"%s\"", res_name, res_name);

	// Pass it through the app context if possible.
	if (this->fAppctx != 0 and this->fAppctx->add_resource_link != 0) {
		(*this->fAppctx->add_resource_link)(this->fAppctx->add_resource_link_ctx, fname, fname_len, res_name, res_name_len);
	} else {
		XT_WARN_0(@"no app ctx, or app ctx doesn't support this");
	}
}

const char*
XTVmHostIfc::get_res_path() //override
{
	XTOSIFC_DEF_SELNAME(@"get_res_path");

	// Get the path from the app context if possible.
	const char* res = (this->fAppctx != 0 ? this->fAppctx->ext_res_path : 0);
	
	if (res != NULL) {
		XT_WARN_0(@"-> NULL")
	} else {
		XT_TRACE_1(@"-> \"%@\"", res);
	}
	
	return res;
}

// Determine if a resource exists.
int
XTVmHostIfc::resfile_exists( const char* res_name, size_t res_name_len ) //override
{
	XTOSIFC_DEF_SELNAME(@"resfile_exists");
	XT_TRACE_1(@"res_name=\"%@\"", res_name);
	
	// Let the application context handle it if possible; if not, just
	// return false, since we can't otherwise provide resource operations.
	int res;
	if (this->fAppctx != 0 and this->fAppctx->resfile_exists != 0) {
		res = (*this->fAppctx->resfile_exists)(this->fAppctx->resfile_exists_ctx, res_name, res_name_len);
	} else {
		res = false;
	}
	
	XT_TRACE_1(@"-> \"%s\"", res);
	
	return res;
}

osfildef*
XTVmHostIfc::find_resource( const char* res_name, size_t res_name_len, unsigned long* res_size ) //override
{
	XTOSIFC_DEF_SELNAME(@"find_resource");
	XT_TRACE_1(@"res_name=\"%@\"", res_name);
	
	// Let the application context handle it; if we don't have an
	// application context, we don't provide resource operation, so simply
	// return failure.
	if (this->fAppctx != 0 and this->fAppctx->find_resource != 0) {
		return (*this->fAppctx->find_resource)(this->fAppctx->find_resource_ctx, res_name, res_name_len, res_size);
	} else {
		return 0;
	}
}

vmhost_gin_t
XTVmHostIfc::get_image_name( char* buf, size_t buflen ) //override
{
	XTOSIFC_TRACE_ENTRY(@"get_image_name");

	// Let the application context handle it if possible; otherwise, return
	// false, since we can't otherwise ask for an image name.
	if (this->fAppctx != 0 and this->fAppctx->get_game_name != 0) {
		// Ask the host system to get a name.
		int ret = (*this->fAppctx->get_game_name)(this->fAppctx->get_game_name_ctx, buf, buflen);
		
		// If that failed, the user must have chosen to cancel; otherwise,
		// we were successful.
		return (ret ? VMHOST_GIN_SUCCESS : VMHOST_GIN_CANCEL);
	} else {
		// We can't ask for a name.
		return VMHOST_GIN_IGNORED;
	}
}

// Get a special file system path.
void
XTVmHostIfc::get_special_file_path( char* buf, size_t buflen, int id ) //override
{
	XTOSIFC_TRACE_ENTRY(@"get_special_file_path");
	
	return os_get_special_path(buf, buflen, 0, id);
}
