/* Copyright Per Bothner 1987. Read the file Q-INFO */
#include <genob.h>
//#include <hash.h>
#include <exceptions.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
extern void LogSetFlags(char *);
#include "traverse.h"
extern void ReadEvalPrint();
#include <parsefile.h>
#include <debug.h>
#include <newtype.h>
#include <typetabs.h>
#include "shell.h"
#include "modules.h"
#include "evalprocs.h"
#include "builtin-syms.h"
#include "ivrun.h"

Language DefaultLanguage = QLanguage;

int RootStackSize = 60000; /* was: 20000 - which is \almost/ enough */
//Object id;
char* PackageName = NULL;
extern Symbol * sAsterisk;
short Compiling = 0, c;
int Optimizing = 0;
extern int PrintExprFlag;
#define typePostfix ".Q"
int doInteractive = 1;
extern int forced_interactive;
struct TraverseData DefaultTrData(DefaultModule);
struct Module *ModulesToCompile = NULL;
extern FILE *AvailTempFile;
extern int LoadStdEnv;

struct Module *StdEnvModule = NULL;
int LoadStdEnv = 0; /* 0: not loaded; 1: loaded; -1: don't load */

extern void initialize_shell();
extern "C" char* tilde_expand(char*);

int do_read_initfile()
{
    // Read ./.Qrc or ~/.Qrc
    filebuf initfile;
    initfile.open(".Qrc", ios::in);
    if (!initfile.is_open()) {
	char *home_Qrc = tilde_expand("~/.Qrc");
	initfile.open(home_Qrc, ios::in);
	free(home_Qrc);
	if (!initfile.is_open())
	    return 0;
    }
    ParseFile* ff =
	OpenParseFile(new general_parsebuf(&initfile), DefaultModule);
    ff->block = DefaultModule->block;
    Expression* exp = ParseLookEOF(ff);
    if (exp == FailedParse)
	{ fprintf(stderr, "[Syntax error in init file]\n");
	  return -1; }
    DefaultTrData.clear(DefaultModule);
    EvalInteractive(exp, &DefaultTrData, DefaultModule);
    return 1;
}

void DoLoadStdEnv()
{
    if (LoadStdEnv != 0) return;
    initialize_shell();
#if 0
    struct ModuleList *mlist;
    mlist = ReadModule("std-env.b", DefaultModule);
    StdEnvModule = module;
    decl = Symbol2Declaration(sAsterisk);
    decl->defining.P = (Object)module;
    MakeLabelToken(&decl->token, NULL, module, 0);
    decl->flags |= KnownDeclaration|SetDeclaration;
    AddDeclaration(DefaultModule->block, decl);
#endif
    LoadStdEnv = 1;
    if (Compiling == 0 && DefaultLanguage == QLanguage)
	do_read_initfile();
}

static InitDone = 0;
EXTERN void SymbolTabInit();
EXTERN void InitQStdMacros();
void Init()
{
    if (InitDone) return;
    InitDone = 1;
    SymbolTabInit();
    LogFile = stderr;
}

char* Arg0;

static void RunFile(char* file_name)
{
    doInteractive = 0;
    DoLoadStdEnv();
    if (DefaultLanguage == LispLanguage || DefaultLanguage == SchemeLanguage) {
	extern void LispCompileFile(char*, Language);
	extern void LoadLisp(InStream *, int, Language);
	if (Compiling)
	    LispCompileFile(file_name, DefaultLanguage);
	else {
	    filebuf file;
	    if (!file.open(file_name, ios::in)) {
		fprintf(stderr, "load failed on %s!\n", file_name);
		exit(-1);
	    }
	    InStream instream(&file);
	    LoadLisp(&instream, 0, DefaultLanguage);
	    file.close();
	}
	return;
    }


#if 1
    Module* module = CheckModule(file_name);
    if (module == NULL) {
	fprintf(stderr, "Q: No such input file: %s\n", file_name);
	exit(-1);
    }
    EvalModule(module);
    // FIXME: maybe we want UserPackage to also get all the *internal* symbols
    // as well???
    UserPackage.use(module);
#else

    char *suffix = GetSuffix(file_name); char buf[100];
    int dir_len = GetDirectoryPrefixLength(file_name);
    int file_name_len = strlen(file_name);
    int suffix_len = suffix == NULL ? 0 : strlen(suffix);
    int module_len = file_name_len - dir_len - suffix_len;
    Name module_name;
    streambuf *file;
    filebuf myfile;

    register struct Module *module;

    if (strcmp(file_name, "-") == 0)
      { static unique = 1;
	sprintf(buf, ".stdin.$%d", unique++);
	module_name = EnterSymbol(buf);
	file = cin.rdbuf();
	goto found;	
      }
    else
	file = &myfile;
    if (suffix != NULL)
	if (strcmp(suffix, SrcSuffix) == 0) suffix = SrcSuffix;
	else if (strcmp(suffix, BinSuffix) == 0) suffix = BinSuffix;
	else
	  {
	    RunError(0, "CheckModule: Unknown file suffix: %s", suffix);
	    return NULL;
	  }
    strcpy(buf, file_name + dir_len);
    buf[module_len] = '\0';
    module_name = EnterSymbol(buf);

    module = new AllocModule(module_name);
    module->kind = 1;

    strcpy(buf, file_name);

    if (suffix == NULL) strcat(buf, SrcSuffix);
    myfile.open(buf, ios::in);
    if (myfile.is_open()) { module->kind = 1; goto found; }

    if (dir_len == 0)
      {
	  sprintf(buf, "%s%s", DefaultSrcDir, file_name);
	  if (suffix == NULL) strcat(buf, SrcSuffix);
	  myfile.open(buf, ios::in);
	  if (my.file->is_open()) { module->kind = 1; goto found; }
      }

    fprintf(stderr, "Q: No such input file: %s\n", file_name);
    exit(-1);
  found:
    fprintf(stderr, "[Loading %s]\n", buf);

    struct ParseFile *ff;
    extern int PrintExprFlag;
    ff = OpenParseFile(new general_parsebuf(file), DefaultModule);
    UndoTrail = NULL;
    module->lastMod = &module->imported;
    ff->block = module->block;
    module->temp = ParseLookEOF(ff);
    module->block->flags |= BlockReturnSelf;
    if (file->sbumpc() != EOF)
	{
	    ParseError(ff, "F Misplaced closing terminator");
	    module->temp = FailedParse;
	}
    if (PrintExprFlag)
	{
	    ObPrint(module->temp, stdout, 0);
	    fflush(stdout);
	}
    if (module->temp == FailedParse)
	module->flags |= ModuleError;
    module->nextToCompile = ModulesToCompile; ModulesToCompile = module;
#endif
}

main(int argc, char **argv, char** env)
{
    extern char *rl_readline_name;
    rl_readline_name = argv[0];

#ifdef DO_GC
    GC_quiet = 1;
#endif
    Arg0 = argv[0];
    char** ArgV = argv;

    Init();
    My_IV_init(argc, argv);
    SetArgv(argc, argv);

    for ( ; *++argv; )
      {
	if (argv[0][0] == '-' && argv[0][1] == 'S')
	  {
	    if (argv[0][2] != '\0') Compiling = argv[0][2];
	    else Compiling |= 1;
	  }
	else if (strcmp(*argv, "-c") == 0)
	    Compiling |= 2;
	else if ((strcmp(*argv,"--lisp") == 0
		     || strcmp(*argv,"--Lisp") == 0))
	    DefaultLanguage = LispLanguage;
	else if ((strcmp(*argv,"--scheme") == 0
		  || strcmp(*argv,"--Scheme") == 0))
	    DefaultLanguage = SchemeLanguage;
	else if (strcmp(*argv, "-O") == 0)
	    Optimizing = 1;
	else if (strcmp(*argv, "-i") == 0)
	    forced_interactive = 1;
	else if (strncmp(*argv, "-L", 2) == 0)
	  { 
	    if ((*argv)[2] == '\0') LogSetFlags("es");
	    else LogSetFlags((*argv)+2);
	  }
	else if (strcmp(*argv, "-") == 0 /* read stdin */
	|| (strcmp(*argv, "-f") == 0 && *++argv))
	  {
	    int postfixLen = strlen(typePostfix);
	    if (strcmp(*argv, ".") != 0)
		RunFile(*argv);
	    else if (LoadStdEnv == 1)
	      fprintf(stderr,"-f . flags too late - stdenv already loaded\n");
	    else LoadStdEnv = -1;
	  }
	else if ((strcmp(*argv, "-e") == 0)
		 && *++argv) {
	    doInteractive = 0;
	    DoLoadStdEnv();
	    EvalString(*argv, -1);
	}
	else if (strcmp(*argv, "-P") == 0 && *++argv)
	    PackageName = *argv;
	else if (strcmp(*argv, "--") == 0)
	  {	  
	    DoLoadStdEnv();
/*	    EvalExpr(ff->block); ff->block = NULL; */
	    ReadEvalPrint();
	    doInteractive = 0;
	  }
	else if (strcmp(*argv, "-E") == 0)
	    PrintExprFlag++;
#if 0
	else if (strcmp(*argv, "-d0") == 0)
	    EnableDebug(0);
	else if (strcmp(*argv, "-d1") == 0)
	    EnableDebug(1);
#endif
	else if (argv[0][0] == '-')
	  {
	    fprintf(stderr, "[Bad command line switch: %s]\n", *argv);
	  }
	else break;
      }

    argc -= argv - ArgV;
    if (argc) {
	SetArgv(argc, argv);
	RunFile(*argv);
    }

#if 1
    DoLoadStdEnv();
 /* force EvalModule on modules listed in header */
    PreEvalModule(DefaultModule);
/*   EvalExpr(ff->block); ff->block = NULL; */
#endif

    if (doInteractive)
	ReadEvalPrint();
    if (Compiling)
      { struct Module *mod;
	for (mod = ModulesToCompile; mod != NULL; mod = mod->nextToCompile)
	    SaveModule(mod, Compiling);
      }
    if (LogFile != NULL) {
	fflush(LogFile);
	if (LogFile != stderr) fclose(LogFile);
    }
    if (AvailTempFile)
	fclose(AvailTempFile);
    return 0;
}

#ifdef DLD
extern "C" int dld_init(char*);
static int dld_init_done = 0;
extern "C" char * dld_find_executable (char*);
void my_dld_init()
{
    if (dld_init_done) return;
    dld_init_done = 1;
    char *name = dld_find_executable(Arg0);
    fprintf(stderr, "[Loading symbols from %s ...", name);
    fflush(stderr);
    if (dld_init(name)) {
	fprintf(stderr, " failed!]\n");
    }
    else
	fprintf(stderr, " done]\n");
    fflush(stderr);
}
#endif
