#include <stdio.h>
#include <io.h>
#include <stdlib.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "uplaunch.h"
/*Python vars & functions*/
DECLVAR (int, Py_NoSiteFlag);
DECLVAR (int, Py_VerboseFlag);
DECLPROC (int, Py_Initialize, (void));
DECLPROC (int, Py_Finalize, (void));
DECLPROC (PyObject *, Py_CompileString, (char *, char *, int));
DECLPROC (PyObject *, PyImport_ExecCodeModule, (char *, PyObject *));
DECLPROC (int, PyRun_SimpleString, (char *));
DECLPROC (int, PySys_SetArgv, (int, char **));
DECLPROC (void, Py_SetProgramName, (char *));
DECLPROC (PyObject *, PyImport_ImportModule, (char *));
DECLPROC (int, PyObject_SetAttrString, (PyObject *, char *, PyObject *));
DECLPROC (PyObject *, PyList_New, (int));
DECLPROC (int, PyList_Append, (PyObject *, PyObject *));
DECLPROC (PyObject *, Py_BuildValue, (char *, ...));
DECLPROC (PyObject *, PyFile_FromFile, (FILE *, char *, char *, int));
DECLPROC (PyObject *, PyObject_CallFunction, (PyObject *, char *, ...));
DECLPROC (PyObject *, PyModule_GetDict, (PyObject *));
DECLPROC (PyObject *, PyDict_GetItemString, (PyObject *, char *));
DECLPROC (PyObject *, PyErr_Occurred, () );
DECLPROC (void, PyErr_Print, () );
DECLPROC (void, Py_SetPythonHome, (char *));
DECLPROC (char *, Py_GetPath, (void));

#include "_rl_startup.h"

static int Py_Main(int argc, char *argv[], char *thisfile, char *homepath, char *project, char *script)
{
	int	rc = 0, i, vflag_index = -1;
	char *pypath = NULL;	
	PyObject *py_argv, *val, *sys;

	/*now launch python ...*/
	pypath = (char*)malloc(strlen(homepath)+13);
	strcpy(pypath,"PYTHONHOME=");
	strcpy(pypath+11,homepath);
	i = strlen(pypath)-1;
	if(pypath[i]=='\\' || pypath[i]=='/') pypath[i]=0;
	VS1("Set %s\n",pypath);
	_putenv( pypath );
	free(pypath);
	VS("Setting Python vars\n");
	*Py_NoSiteFlag = 1;
	VS1("Py_NoSiteFlag=%d\n", *Py_NoSiteFlag);
		for (i = 0; i < argc; ++i){
			if(!strcmp (argv[i], "-v")) {
				*Py_VerboseFlag = 1;	
				vflag_index = i;
				}
			}
		
	Py_SetProgramName(thisfile);
	VS("Initializing Python\n");	
	Py_Initialize();
	Py_GetPath();
	_putenv( "PYTHONPATH=" );	
	VS("Importing module sys\n");
	PyRun_SimpleString("import sys;sys.path=filter(lambda x: not x or x.find(sys.exec_prefix)==0, sys.path)");
	/*PyRun_SimpleString("import sys");*/

	py_argv = PyList_New(0);
	val = Py_BuildValue("s", thisfile);
	PyList_Append(py_argv, val);
	Py_DECREF(val);
	for (i = 1; i < argc; ++i){
		if (i != vflag_index){	
			val = Py_BuildValue ("s", argv[i]);
			PyList_Append(py_argv, val);
			Py_DECREF(val);
			}
		}
	sys = PyImport_ImportModule("sys");
	VS("Setting sys.argv\n");
	PyObject_SetAttrString(sys, "argv", py_argv);
	Py_DECREF(sys);
	Py_DECREF(py_argv);
	
	/*3 - install all the zlibs*/
	VS("Installing import hooks\n");
	pypath = malloc(strlen(_rl_startup)+strlen(homepath)+100);
	sprintf(pypath,_rl_startup,homepath);
	if(PyRun_SimpleString(pypath)){
		FATALERROR("Cannot start up importing.\n");
		return -1;
		}
	free(pypath);
	VS("Importing module site\n");
	PyRun_SimpleString("import site;" _RL_RESTORE_PYZ);

	/* pass 4 - run any scripts*/
	VS("Running script\n");
	i = PyRun_SimpleString((char *)script);
	VS(i ? "FAILED\n" : "SUCCEEDED\n" );
	VS("Finalizing Python\n");
	Py_Finalize ();

	VS1("Returning %d to OS\n", rc);
	return rc;
}

static int getRegistryInstallPath(HKEY rootKey, char* id, char* homepath)
{
	HKEY	key=0;
	long	bufSize;
	int		n;
	char	*fmt="Software\\ReportLab\\%s";
	char	*regpath = malloc(strlen(id)+strlen(fmt)+1);

	sprintf(regpath, fmt, id);
	bufSize=_MAX_PATH;
	VS1("REGISTRY PATH: %s\n", regpath);
	if((n=RegOpenKey(rootKey,regpath,&key))==ERROR_SUCCESS &&
		(n=RegQueryValueEx(key, "InstallPath",NULL,NULL, homepath, &bufSize))==ERROR_SUCCESS) n = 1;
	else{
		VS2("Can't Find Registry PYTHONHOME key=%8.8x err=%d\n",key,n);
		n = 0; /*error*/
		}
	if(key) RegCloseKey(key);
	if(regpath) free(regpath);
	return n;
}

int get_homepath(char* thisfile, char* homepath, char *project)
{
	char	*p, *q, *s;
	int		n;
	VS("Finding InstallPath\n");
	if(!getRegistryInstallPath(HKEY_LOCAL_MACHINE,project,homepath)
			&& !getRegistryInstallPath(HKEY_CURRENT_USER,project,homepath)){
		VS("Trying EXE for homepath\n");
		strcpy(homepath, thisfile);
		p = homepath+strlen(homepath);
		while(p>=homepath && *p!='/' && *p != '\\') p--;
		if(p>homepath) *++p = 0;
		else {
			FATALERROR("Can't Find PYTHONHOME\n");
			return -1;
			}
		}
	if(homepath[strlen(homepath)-1]!='\\') strcat(homepath,"\\");
	VS1("Found HOMEPATH=%s\n", homepath);
	/*adjust the path to include homepath/DLLs*/
	p = getenv("PATH");
	n = strlen(thisfile)+strlen(homepath)+11;	/*we add DLLs and PATH=*/
	if(p) n += strlen(p)+1;
	s = (char*)malloc(n);
	strcpy(s,"PATH=");
	strcpy(s+5,thisfile);
	q = s+strlen(s);
	while(q>=s && *q!='/' && *q != '\\') q--;
	*q = 0;
	strcat(s,";");
	strcat(s,homepath);
	strcat(s,"DLLs");
	if(p){
		strcat(s,";");
		strcat(s,p);
		}
	VS1("Adjusted PATH=%s\n",s);
	_putenv(s);
	return 0;
}

int openSelf(char* thisfile, char *homepath, char **project, char **script, int *pyvers)
{
	FILE*	fp;
	char	*p;
#	define RLW_MAGIC "RLW\01\02\03\017\016"
	typedef struct {
		unsigned long scriptlen;
		unsigned short projectlen;
		unsigned short pyvers;
		char magic[8];
		} cookie;
	cookie	c;
	int	cs = sizeof(cookie);
	if (!GetModuleFileNameA(NULL, thisfile, _MAX_PATH)) {
		FATALERROR("System error - unable to load!");
		return -1;
		}
	p = thisfile+strlen(thisfile)-4;
	if (strnicmp(p, ".exe", 4) != 0) strcat(thisfile, ".exe");
	VS1("Opening self=%s\n", thisfile);
	fp = fopen(thisfile, "rb");
	if(fp == NULL){
		FATALERROR("Cannot open self: ");
		FATALERROR(thisfile);
		return -1;
		}
	fseek(fp, 0, SEEK_END);
	if (fseek(fp, -(int)cs, SEEK_END)){
L0:		FATALERROR("executable appears to be invalid");
		return -1;
		}
	fread(&c, sizeof(cookie), 1, fp);
	if (strncmp((char*)&c.magic, RLW_MAGIC, 8)){
#ifdef	_DEBUG
		int i;
		VS("RLW_MAGIC:");
		for(i=0;i<8;i++){
			VS1("%2.2X",RLW_MAGIC[i]);
			}
		VS("\nc.magic:");
		for(i=0;i<8;i++){
			VS1(" %2.2X",c.magic[i],);
			}
		VS("\n");
#endif
		FATALERROR("executable has bad magic - quitting!");
		return -1;
		}
	if (fseek(fp, -cs-(int)c.projectlen, SEEK_END)) goto L0;
	*project = malloc((size_t)c.projectlen + 1);
	fread(*project, (int)c.projectlen, 1, fp);
	(*project)[c.projectlen] = 0;
	VS2("projectlen=%d project='%s'\n",(int)c.projectlen,*project);
	if (fseek(fp, -cs-(int)c.projectlen-(int)c.scriptlen, SEEK_END)) goto L0;
	*script = malloc((size_t)c.scriptlen + 1);
	fread(*script, (int)c.scriptlen, 1, fp);
	(*script)[c.scriptlen] = 0;
	VS1("scriptlen=%d\n",(int)c.scriptlen);
	if (get_homepath(thisfile,homepath,*project)) return -1;
	VS("closing self\n");
	fclose(fp);
	*pyvers = (int)c.pyvers;
	return 0;
}

HINSTANCE loadPython(int pyvers, char* homepath) {
	HINSTANCE dll;
	char *dllpath = (char*)malloc(strlen(homepath)+20);
	VS("Loading python\n");
	sprintf(dllpath, "%sDLLs\\python%02d.dll", homepath, pyvers);
	VS(dllpath);VS("\n");
	dll = LoadLibraryEx(dllpath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
	if(!dll){
		VS("Failed to load python dll\n");
		}
	else{
		/* Thomas Heller's code*/
		VS("Getting entry points\n");
		GETVAR (dll, int, Py_NoSiteFlag);
		GETVAR (dll, int, Py_VerboseFlag);
		GETPROC (dll, int, Py_Initialize, (void));
		GETPROC (dll, int, Py_Finalize, (void));	
		GETPROC (dll, PyObject *, Py_CompileString, (char *, char *, int));
		GETPROC (dll, PyObject *, PyImport_ExecCodeModule, (char *, PyObject *));
		GETPROC (dll, int, PyRun_SimpleString, (char *));
		GETPROC (dll, int, PySys_SetArgv, (int, char **));
		GETPROC (dll, void, Py_SetProgramName, (char *));
		GETPROC (dll, PyObject *, PyImport_ImportModule, (char *));
		GETPROC (dll, int, PyObject_SetAttrString, (PyObject *, char *, PyObject *));
		GETPROC (dll, PyObject *, PyList_New, (int));
		GETPROC (dll, int, PyList_Append, (PyObject *, PyObject *));
		GETPROC (dll, PyObject *, Py_BuildValue, (char *, ...));
		GETPROC (dll, PyObject *, PyFile_FromFile, (FILE *, char *, char *, int));
		GETPROC (dll, PyObject *, PyObject_CallFunction, (PyObject *, char *, ...));
		GETPROC (dll, PyObject *, PyModule_GetDict, (PyObject *));
		GETPROC (dll, PyObject *, PyDict_GetItemString, (PyObject *, char *));
		GETPROC (dll, PyObject *, PyErr_Occurred, () );
		GETPROC (dll, void, PyErr_Print, () );
		GETPROC (dll, void, Py_SetPythonHome, (char *));
		GETPROC (dll, char *, Py_GetPath, (void));
		}
	free(dllpath);

	return dll;
}

#ifdef _CONSOLE
int main(int argc, char* argv[])
#else
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
#endif
{
	int		rc = 0, pyvers;
	char*	homepath = (char*)malloc(_MAX_PATH+1);
	char*	thisfile = (char*)malloc(_MAX_PATH+1);
	char	*project, *script;
	VS("Start " STUBNAME "\n");

	if (openSelf(thisfile, homepath,&project,&script,&pyvers)) return -1;
	if(!loadPython(pyvers,homepath)) return -1;
#ifndef _CONSOLE
	rc = Py_Main(__argc, __argv, thisfile, homepath,project, script);
#else
	rc = Py_Main(argc, argv, thisfile, homepath, project, script);
#endif
	free(homepath);
	free(thisfile);
	return rc;
}
