//
//  XTAppResourceLoader.m
//  XTads
//
//  Created by Rune Berg on 11/04/15.
//  Copyright (c) 2015 Rune Berg. All rights reserved.
//
//  This is XTads' replacement for the resnoexe.cpp implementation
//  of a couple of CResLoader methods, so that we can load resources
//  from the XTads app archive.
//

#import <Foundation/Foundation.h>
#import "XTLogger.h"
#import "XTFileUtils.h"
#import "XTResourceFinder.h"
#import "XTResourceFindResult.h"
#include "resload.h"
#include "vmimage.h"


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

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


/* ------------------------------------------------------------------------
 *   Resource loader interface implementation
 *
 *   (Implementation copied from from T3's resdlexe.cpp by mjr.)
 */
class CVmImageLoaderMres_resload: public CVmImageLoaderMres
{
public:
	CVmImageLoaderMres_resload(const char *respath)
	{
		/* remember the path of the resource we're trying to find */
		respath_ = respath;
		respath_len_ = strlen(respath);
		
		/* we haven't found it yet */
		found_ = FALSE;
		link_ = 0;
	}
	
	~CVmImageLoaderMres_resload()
	{
		lib_free_str(link_);
	}
	
	/* add a resource */
	void add_resource(uint32_t seek_ofs, uint32_t siz,
					  const char *res_name, size_t res_name_len)
	{
		/*
		 *   if we've already found a match, there's no need to consider
		 *   anything else
		 */
		if (found_)
			return;
		
		/* check to see if this is the one we're looking for */
		if (res_name_len == respath_len_
			&& memicmp(respath_, res_name, res_name_len) == 0)
		{
			/* we found it */
			found_ = TRUE;
			
			/* remember the seek location */
			res_seek_ = seek_ofs;
			res_size_ = siz;
		}
	}
	
	/* add a resource link */
	void add_resource(const char *fname, size_t fnamelen,
					  const char *res_name, size_t res_name_len)
	{
		/*
		 *   if we've already found a match, there's no need to consider
		 *   anything else
		 */
		if (found_)
			return;
		
		/* check to see if this is the one we're looking for */
		if (res_name_len == respath_len_
			&& memicmp(respath_, res_name, res_name_len) == 0)
		{
			/* we found it */
			found_ = TRUE;
			
			/* remember the link */
			link_ = lib_copy_str(fname, fnamelen);
		}
	}
	
	/* did we find the resource? */
	int found_resource() const { return found_; }
	
	/* get the seek location and size of the resource we found */
	uint32_t get_resource_seek() const { return res_seek_; }
	uint32_t get_resource_size() const { return res_size_; }
	
	/* get the local filename link, if it's given as a link */
	const char *get_link_fname() const { return link_; }
	
private:
	/* name of the resource we're looking for */
	const char *respath_;
	size_t respath_len_;
	
	/* flag: we found the resource we're looking for */
	int found_;
	
	/* seek location and size of the resource we found */
	uint32_t res_seek_;
	uint32_t res_size_;
	
	/* local filename link, if the resource is given as a link */
	char *link_;
};


/*
 *   Try loading a resource from the "executable file",
 *   i.e. load an individual resource file in the app bundle (directory).
 */
osfildef *CResLoader::open_exe_res(const char *respath, const char *restype) {
	
	NSString *selName = @"CResLoader::open_exe_res";
	XT_TRACE_2(@"respath=\"%s\" restype=\"%s\"", respath, restype);
	
	NSString *resPathString = XTADS_FILESYSTEM_C_STRING_TO_NSSTRING(respath);
	NSString *resTypeString = XTADS_FILESYSTEM_C_STRING_TO_NSSTRING(restype);

	XTResourceFinder *resFinder = [XTResourceFinder new];
	
	XTResourceFindResult *findResult = [resFinder findResourceInTerpExecutable:resPathString ofType:resTypeString];

	osfildef *res = nil;
	
	if (findResult != nil) {
		res = findResult.fileHandle;
	} else {
		XT_TRACE_1(@"failed to find path to terp-bundled resource for resPathString=\"%@\"", resPathString);
	}
	
	return res;
}

/*
 *  Map the very few known terp-level resource library file names to
 *  actual file name in OS X app bundle.
 */
NSString *terpLevelLibFilenameToAppBundledFilename(const char *libFilename)
{
	NSString *tbFilename = nil;
	
	if (libFilename != NULL) {
		if (strcmp(libFilename, "charmap/cmaplib.t3r") == 0) {
			tbFilename = @"cmaplib.t3r";
		}
	}
	
	return tbFilename;
}

/*
 *   Try loading a resource from a resource library,
 *   i.e. from a T3 library (.t3r) file in the app bundle (directory).
 *
 *   (Implementation heavily drawn from T3's resdlexe.cpp by mjr)
 */
osfildef *CResLoader::open_lib_res(const char *libfile,
								   const char *respath)
{
	NSString *selName = @"CResLoader::open_lib_res";
	XT_TRACE_2(@"libfile=\"%s\" respath=\"%s\"", libfile, respath);
	
	NSBundle *mainBundle = [NSBundle mainBundle];
	
	NSString *appBundledFilename = terpLevelLibFilenameToAppBundledFilename(libfile);
	if (appBundledFilename == NULL) {
		XT_WARN_1(@"failed to map terp-bundled resource library file \"%s\" to an actual bundled file", libfile);
		return NULL;
	}
	
	NSString *appBundleLibraryFilePath = [mainBundle pathForResource:appBundledFilename ofType:nil];
	if (appBundleLibraryFilePath == nil) {
		XT_WARN_1(@"failed to locate terp-bundled resource library file \"%@\"", appBundledFilename);
		return NULL;
	}

	const char *appBundleLibraryFilePathCString = XTADS_NSSTRING_TO_FILESYSTEM_C_STRING(appBundleLibraryFilePath);
	osfildef *file = osfoprb(appBundleLibraryFilePathCString, OSFTT3IMG);
	if (file == NULL) {
		XT_WARN_1(@"failed to open terp-bundled resource library file \"%s\"", appBundleLibraryFilePathCString);
		return NULL;
	}
	
 	/* set up a resource finder for our resource */
	CVmImageLoaderMres_resload res_ifc(respath);
	 
	/* load the file, so that we can try finding the resource */
	CVmImageLoader::load_resources_from_fp(file, appBundleLibraryFilePathCString, &res_ifc);
	 
	/* check to see if we found it */
	if (res_ifc.found_resource())
	{
		/* we got it - check the type */
		if (res_ifc.get_link_fname() != 0)
		{
			/*
			 *   linked local file - NOT SUPPORTED
			 */

			XT_ERROR_3(@"resource \"%s\" in terp-bundled resource library \"%s\" is a link (to \"%s\") -- not supported",
					  respath, libfile, res_ifc.get_link_fname());
			
			osfcls(file);
			return NULL;
		}
		else
		{
			/* embedded resource - seek to the first byte */
			XT_TRACE_2(@"found embedded resource \"%s\" in terp-bundled resource library \"%s\"", respath, libfile);

			uint32_t seekPos = res_ifc.get_resource_seek();
			osfseek(file, seekPos, OSFSK_SET);
			return file;
		}
	}
	else
	{
		XT_TRACE_2(@"failed to find resource \"%s\" in terp-bundled resource library \"%s\"", respath, libfile);

		osfcls(file);
		return NULL;
	 }
}


