
/* standard headers */
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <path2fss.h>

/* system headers */
#include <Files.h>
#include <Strings.h>
#include <Errors.h>

/* compiler headers */
#include "DropInCompilerLinker.h"
#include "CompilerMapping.h"
#include "CWPluginErrors.h"


/* local variables */
static long		linecount;

#if CW_USE_PRAGMA_EXPORT
#pragma export on
#endif

CWPLUGIN_ENTRY(CWPlugin_GetDropInFlags)(const DropInFlags** flags, long* flagsSize)
{
	static const DropInFlags sFlags = {
		kCurrentDropInFlagsVersion,
		CWDROPINCOMPILERTYPE,
		DROPINCOMPILERLINKERAPIVERSION_7,
		kCompMultiTargAware | kCompAllowDupFileNames,
		Lang_C_CPP,
		DROPINCOMPILERLINKERAPIVERSION
	};
	
	*flags = &sFlags;
	*flagsSize = sizeof(sFlags);
	
	return cwNoErr;
}

CWPLUGIN_ENTRY(CWPlugin_GetDropInName)(const char** dropinName)
{
	static const char* sDropInName = "plain2c preprocessor";
	
	*dropinName = sDropInName;
	
	return cwNoErr;
}

CWPLUGIN_ENTRY(CWPlugin_GetDisplayName)(const char** displayName)
{
	static const char* sDisplayName = "plain2c preprocessor";
	
	*displayName = sDisplayName;
	
	return cwNoErr;
}

CWPLUGIN_ENTRY(CWPlugin_GetPanelList)(const CWPanelList** panelList)
{
	static CWPanelList sPanelList = {kCurrentCWPanelListVersion, 0, 0};
	
	*panelList = &sPanelList;
	
	return cwNoErr;
}

CWPLUGIN_ENTRY(CWPlugin_GetTargetList)(const CWTargetList** targetList)
{
	static CWDataType sCPU = targetCPUAny;
	static CWDataType sOS = targetOSAny;
	static CWTargetList sTargetList = {kCurrentCWTargetListVersion, 1, &sCPU, 1, &sOS};
	
	*targetList = &sTargetList;
	
	return cwNoErr;
}

CWPLUGIN_ENTRY(CWPlugin_GetDefaultMappingList)(const CWExtMapList** defaultMappingList)
{
	static CWExtensionMapping sExtension = {'TEXT', ".t", 0};
	static CWExtMapList sExtensionMapList = {kCurrentCWExtMapListVersion, 1, &sExtension};
	
	*defaultMappingList = &sExtensionMapList;
	
	return cwNoErr;
}

#if CW_USE_PRAGMA_EXPORT
#pragma export off
#endif

char *
get_path_from_mac_fs(FSSpec inMacFSSpec, Boolean isFolder)
{
char *ret, buf[1024], *ptr;
unsigned char * name;

	ptr = buf+sizeof(buf)-1;
	*ptr = 0;
	if ( isFolder && inMacFSSpec.name[0] != 0 ) {
		ptr--;
		*ptr = ':';
	}
	for ( ; ; ) {
		name = inMacFSSpec.name;
		ptr -= name[0]+1;
		if ( ptr < buf )
			return 0;
		memcpy(ptr+1, name+1, name[0]);
		if ( inMacFSSpec.parID != fsRtParID ) {
			*ptr = ':';
		} else {
			ret = (char*)malloc(buf+sizeof(buf)-ptr-1);
			strcpy(ret,ptr+1);
			return ret;
		}
		if ( noErr != FSMakeFSSpec(inMacFSSpec.vRefNum, 
					inMacFSSpec.parID, "\p", &inMacFSSpec) ) 
			return 0;
	}
}

void
ext_path(char *ret, char *path, FSSpec fs)
{
	int len, go_back;
	char *q = "{Project}:", *projdir, *w;
	strcpy(ret, path);

	// decode "{Project}:"
	for ( ; *path && *q && *path == *q ; path++, q++ ) { }
	if ( *q == 0 ) { // path == "{Project}:...."
		fs.name[0] = 0;
		projdir = get_path_from_mac_fs(fs, true);
//printf("Proj = %s\n", projdir);
		for ( go_back = 0 ; *path == ':' ; go_back++, path++ ) { } // count ':' in "{Project}:::" style path
		w = projdir + strlen(projdir) - 1;
		for ( ; go_back ; go_back-- ) {
			do { // go back to parent dir
				if ( w == projdir )
					goto come_top;
				w--;
			} while ( *w != ':' );
		}
		w++;
come_top:
		*w = 0;
		strcpy(ret, projdir);
		free(projdir);
		strcat(ret,path);
	}
	
	// add ":" to last if not exists
	len = strlen(ret);
	if ( len > 0 && ret[len-1] != ':' ) {
		ret[len++] = ':';
		ret[len] = '0';
	}
}

void
load_path_def(CWPluginContext context, char *srcTopDir, char *destTopDir)
{
	CWFileSpec fs;
	char *path, c, buffer[1024];
	int pos;
	FILE *f;
	enum { modeTop, modeCom, modeArg } mode;
	enum { comSource, comDest } com;
	int skipSpace;

	CWGetProjectFile(context,&fs);
	if ( noErr != FSMakeFSSpec(fs.vRefNum, fs.parID, "\pp2c_path.txt", &fs) ) {
		FSpCreate(&fs, 'CWIE', 'TEXT', smSystemScript);
		*srcTopDir = 0;
		*destTopDir = 0;
		return;
	}
	path = get_path_from_mac_fs(fs, false);
	f = fopen(path, "r");
	free(path);
	if ( f == 0 ) {
		*srcTopDir = 0;
		*destTopDir = 0;
		return;
	}	
	mode = modeTop;
	skipSpace = false;
	do {
		if ( fread(&c, 1, 1, f) < 1 )
			c = EOF;
		if ( skipSpace ) {
			if ( c == ' ' || c == '\t' )
				continue;
			else
				skipSpace = false;
		}
		switch ( mode ) {
		  case modeTop :
			if ( c == '@' ) {
				mode = modeCom;
				pos = 0;
				skipSpace = true;
			}
			break;
		  case modeCom :
			if ( c == ' ' || c == '\t' ) {
				buffer[pos] = 0;
				if ( strcmp("source", buffer) == 0 )
					com = comSource;
				else if ( strcmp("work", buffer) == 0 )
					com = comDest;
				else {
					mode = modeTop;
					continue;
				}
				mode = modeArg;
				pos = 0;
				skipSpace = true;
			} else {
				buffer[pos++] = c;
			}
			break;
		  case modeArg :
			if ( c == '\t' || c == '\n' || c == '\r' || c == EOF ) {
				if ( pos > 0 && buffer[pos-1] != ':' )
					buffer[pos++] = ':';
				buffer[pos] = 0;
				if ( com == comSource )
					ext_path(srcTopDir, buffer, fs);
				if ( com == comDest )
					ext_path(destTopDir, buffer, fs);
				mode = modeTop;
			} else {
				buffer[pos++] = c;
			}
		}
		if ( c == '\n' || c == '\r' )
			mode = modeTop;
	} while ( c != EOF );
	fclose(f);
}

void
get_dest_path(CWPluginContext context, char *dest, char *src)
{
	char		srcTopDir[1024], destTopDir[1024];
	char		* w1, * w2;
	
	load_path_def(context, srcTopDir, destTopDir);

//printf("srcTopDir : %s\ndestTopDir : %s\n",srcTopDir, destTopDir);

	for ( w1 = srcTopDir , w2 = src ; *w1 == *w2 && (*w1 && *w2) ; w1++, w2++ ) { }
	if ( *w1 == 0 ) {	// src == srcTopDir+w2 -> dest = destTopDir + w2
		strcpy(dest, destTopDir);
		strcat(dest, w2);
	} else { 		// src = dest
		strcpy(dest, src);
	}
	
	// replace the extension of dest to ".c"
	w1 = dest;
	while ( *w1++ ) { }
	while ( *(--w1) != '.' ) { }
	*(++w1) = 'c';
	*(++w1) = 0;
	
}

void
mkdir_for_file(char *path)
{
	char t;
	char *w;
	for ( w = path ; *w ; w++ ) {
		if ( *w == ':' ) {
			t = *w;
			*w = 0;
			mkdir(path, 0777);
			*w = t;
		}
	}
}

static CWResult	Compile(CWPluginContext context)
{
	CWFileSpec	sourceSpec, destSpec;
	short			dotpos;
	CWResult		err;
	char *		sourcePath;
	char			destPath[1024];
	char *		argv[3];
	long			whichFile;
	CWNewProjectEntryInfo f_info = 
		{kDefaultLinkPosition, kDefaultLinkPosition, kDefaultLinkPosition, kDefaultLinkPosition,
		"plain2c_out" ,0, 0, 0};
	
	err = CWGetMainFileSpec(context, &sourceSpec);
	if (!CWSUCCESS(err))
		return err;

	sourcePath = get_path_from_mac_fs(sourceSpec,false);
	if ( ! sourcePath )
		return cwErrFileNotFound;
	get_dest_path(context, destPath, sourcePath);
	
	mkdir_for_file(destPath);
	
	linecount = 0;

	argv[0] = "p2c";
	argv[1] = sourcePath;
	argv[2] = destPath;
	
	err = p2c_main(3, argv);

	if (CWSUCCESS(err))
	{
		if ( err = __path2fss(destPath, &destSpec) )
			return err;
		err = CWAddProjectEntry(context, &destSpec, true, &f_info, &whichFile);
//printf("err : %d\n",err);
	}

	return (err);
}

/*
 *	main	-	main entry-point for Drop-In Sample Tool
 *
 */

pascal short main(CWPluginContext context)
{
	short		result;
	long		request;
	
	if (CWGetPluginRequest(context, &request) != cwNoErr)
		return cwErrRequestFailed;
	
	result = cwNoErr;
		
	/* dispatch on compiler request */
	switch (request)
	{
	case reqInitCompiler:
		/* compiler has just been loaded into memory */
		break;
		
	case reqTermCompiler:
		/* compiler is about to be unloaded from memory */
		break;
		
	case reqCompile:
		/* compile a source file */
		result = Compile(context);
		break;
		
	default:
		result = cwErrInvalidParameter;
		break;
	}
	
	/* return result code */
	return (result);
}


