/* Copyright Per Bothner 1987. Read the file Q-INFO */
#include <stdio.h>
#include <fstream.h>
#include <genmap.h>
#include <parsefile.h>
#include <aux_format.h>
#include <Qcompile.h>
#include <debug.h>
#include <exceptions.h>
#include <traverse.h>
#include <typetabs.h>
#include <std.h>
#include "modules.h"
extern "C" {
#include "ext_sym.h"
}

#undef CompileToC
#define CompileToC(cf) (!EvalCompile(cf))
extern void EvalCommon(Expr *exp, struct TraverseData *data,
		       struct Module *module, Root** result);

extern char* PackageName;
HashTable *ModuleTab = NULL;
/* static struct OldModule *(NullModuleList[1]) = {0}; */
extern Object LookupLabelToken(), CompileForEval();
extern struct Declaration *Symbol2Declaration();

extern "C" char * GetSuffix(char *name)
{
    register char *cp = name, *suffix = NULL;
    for (; *cp != '\0'; cp++)
	if (*cp == '.') suffix = cp;
	else if (*cp == '/' || *cp == ':') suffix = NULL;
    return suffix;
}

int GetDirectoryPrefixLength(char *name)
 /* if name is a filename, what is the length of the initial directory part? */
  { register char *cp = name, *cpd = cp;
    for (; *cp != '\0'; cp++)
	if (*cp == '/' || *cp == ']' || *cp == ':' || *cp == '[') cpd = cp + 1;
    return cpd - name;
}

struct bfile_data {
/*NOTE: extended in m68k_prims.c */
    char *sym_name;
    int sym_name_len;
    char *text_start;
    Name initProcName;
    FILE *bfile;
    struct SymbolList *sym_list;
};

struct UndefinedSymbol
  {
    struct UndefinedSymbol *next;
    char rsize;
    long rpos;
    char *rsymbol;
  };

#if 0
struct SymbolEntry
  {
    struct sym sym;
    Object name;
  };
#endif
#define SymChunkLog 5
#define SymChunkSize (1<<SymChunkLog)

#define SymUndef 0
#define SymAddr 1
#define SymCommon 2
struct SymbolList
  {
    struct SymbolList *next;
#ifndef NEW_SYM_LIST
    Name name[SymChunkSize];
#else
    struct
      {
	int kind; /* one of SymUndef, SymAddr, SymCommon */
	union
	  {
	    Name label; /* SymUndef, SymCommon */
	    char *addr; /* SymAddr */
	  } u;
      } sym[SymChunkSize];
#endif
  };

HashTable *CommonTable = NULL;

static char *DefaultBinDir  = "[sys]run/Q/";
#if 0
FILE *
SearchOpenBinary(name)
  { char buf[100]; char *file_name; FILE *bfile;
    strcpy(buf, DefaultBinDir);
    file_name = buf + strlen(DefaultBinDir);
    strcpy(file_name, name);
    strcat(file_name, ".b");
    bfile = fopen(file_name, "r");
    if (bfile == NULL)
	bfile = fopen(buf, "r");
    if (bfile == NULL)
      {
	fprintf(stderr, "Failed to open %s for loading\n", file_name);
	fflush(stderr);
      }
    return bfile;
  }
#endif

#define xread(ptr, n, file) \
    { if (fread(ptr, n, 1, file) != 1) goto err; }

Module::Module(Symbol *nam) : Package(nam->Str(), 32)
{
//    name = nam;
    imported = NULL;
    kind = 0;
}
struct Module *AllocModule(Symbol* module_name)
{
    extern struct Type *AllocType();
/*    struct Type *mtype = 0; *AllocType(0);*/
/*    mtype->class_name = module_name;*/
/*    mod = Alloc(mtype, sizeof(struct Module));*/
    Module *mod = new Module(module_name);
//    bzero(mod, sizeof(struct Module));
    mod->block = new Block(NULL);
/*    mod->block->flags |= BlockReturnSelf;*/
/*    mod->block->type = MakeExType(ExPointerType,mtype);*/
    mod->block->globalName = SymbolString(module_name);
    mod->lastMod = &mod->imported;
//    mod->parseTable = QStdMacros;
    mod->flags = 0;
    mod->lookupDecls = NULL;
    return mod;
  }

struct ModuleEntry { Name arg; struct Module *module;};

void EnterModule(Symbol *module_name, struct Module *module)
{
    struct ModuleEntry *entry;
    if (ModuleTab == NULL)
	ModuleTab = new HashTable(16, NULL);
    entry = (struct ModuleEntry*)SymbolLookup(ModuleTab, module_name);
    entry->arg = module_name;
    entry->module = module;
    ModuleTab->rehash_if_needed();
}

struct Module * LookupModule(Symbol* module_name)
  { struct ModuleEntry *entry; struct Module *module;
    if (ModuleTab == NULL)
	ModuleTab = HashTable::New(16);
    entry = (struct ModuleEntry*)SymbolLookup(ModuleTab, module_name);
    if (entry->arg == module_name) return entry->module;
    entry->arg = module_name;
    entry->module = module = AllocModule(module_name);
    ModuleTab->rehash_if_needed();
    return module;
  }

char *SrcSuffix = ".Q";
char *BinSuffix = ".o";
char *DefaultSrcDir = "[sys]run/Q/";
extern struct Module *DefaultModule;

Root * EvalModule(struct Module *module)
  { int kind;
    if (module == NULL || module == (struct Module*)NoValue)
	return &NullSequence;
    kind = module->kind; module->kind = 5; /* to avoid infinite recursion */
    if (kind >= 4)
	return &NullSequence;
    PreEvalModule(module);
    if (kind == 1)
      {
	Expr* exp = (Expr*)module->temp;
	if (module->flags & ModuleError)
	    return &NullSequence;
	TraverseData *data = new TraverseData(module);
    	module->temp = NULL;
/* Changes here probably also need to be done in ReadEvalPrint */
#if 0
 extern struct Module *StdEnvModule;
	if (need to include StdEnv)
	    ModPutModule(module, StdEnvModule, 0);
#endif
	if (data->errors > WrnMessage)
	  {
	    module->flags |= ModuleError;
	    return &NullSequence;
	  }
	Root* result;
	EvalCommon(exp, data, module, &result);
	
	if (RealHandler(_Handler_) && module != DefaultModule)
	  {
	    fprintf(stderr, "[Multiple results in EvalModule - truncated]\n");
	    _Handler_ = NullHandler;
	  }
	if (result != NULL && result != &NullSequence) // KLUDGE?
	    return result;
//        return result;
      }
//    else if (kind == 2)
//	DoRelocations(module);   

    return module;
  }

EXTERN struct MacroTable * AllocMacroTable(int, struct MacroTable *);
void PreEvalModule(struct Module *module)
 /* fix up dependencies */
{
    struct ModuleList *link;
    for (link = module->imported; link != NULL; link = link->next)
	EvalModule(link->module);
}

struct Module * CheckModule(char *file_name)
{
    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);
	module = LookupModule(module_name);
	file = cin.rdbuf();
	module->kind = 1;
	goto found;	
      }
    else
	file = &myfile;
#if 0
    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;
	  }
#endif
    strcpy(buf, file_name + dir_len);
    buf[module_len] = '\0';
    module_name = EnterSymbol(buf);
    module = LookupModule(module_name);

    if (module->kind != 0)  /* already loaded */
#if 0
	if (suffix != NULL)
	  { /* re-load it anyway */
	    module = AllocModule(module_name);
	    Insert(ModuleTab, module_name, module);
	  }
	else
#endif
	  {
	    if (module->kind == 4)
	      {
		module->kind = 5;
		suffix = GetSuffix(buf);
		if (suffix) strcpy(suffix, ".Qx");
		else strcat(buf, ".Qx");
//		HandleAuxFile(module, buf);
	      }
	    return module;
	  }

    strcpy(buf, file_name);
    myfile.open(buf, ios::in);
    if (myfile.is_open()) {
	if (suffix != NULL && strcmp(suffix, BinSuffix) == 0)
	    module->kind = 2;
	else
	    module->kind = 1;
	goto found;
    }
    if (suffix != SrcSuffix)
      {
	if (suffix == NULL) strcat(buf, BinSuffix);
	myfile.open(buf, ios::in);
	if (myfile.is_open()) { module->kind = 2; goto found; }
      }
    if (suffix != BinSuffix)
      {
	if (suffix == NULL) strcat(buf, SrcSuffix);
	myfile.open(buf, ios::in);
	if (myfile.is_open()) { module->kind = 1; goto found; }
      }
    if (dir_len == 0)
      {
	if (suffix != SrcSuffix)
	  {
	    sprintf(buf, "%s%s", DefaultBinDir, file_name);
	    if (suffix == NULL) strcat(buf, BinSuffix);
	    myfile.open(buf, ios::in);
	    if (myfile.is_open()) { module->kind = 2; goto found; }
	  }
	if (suffix != BinSuffix)
	  {
	    sprintf(buf, "%s%s", DefaultSrcDir, file_name);
	    if (suffix == NULL) strcat(buf, SrcSuffix);
	    myfile.open(buf, ios::in);
	    if (myfile.is_open()) { module->kind = 1; goto found; }
	  }
      }
    fprintf(stderr, "Could not load module \"%s\"\n", file_name);
    fflush(stderr);
    return NULL;
  found:
    fprintf(stderr, "[Loading %s]\n", buf);
    switch (module->kind)
      {
	struct ParseFile *ff;
	extern int PrintExprFlag;
      case 1: /* Src */
	ff = OpenParseFile(new general_parsebuf(file), module);
	UndoTrail = NULL;
	module->lastMod = &module->imported;
	ff->block = module->block;
	Package *save_package = CurrentPackage;
	CurrentPackage = module;
	if (PackageName) {
	    Package* pack = LookupPackage(PackageName, strlen(PackageName));
	    if (pack != NULL)
		CurrentPackage = pack;
	}
	module->temp = ParseLookEOF(ff);
	CurrentPackage = save_package;
/*	module->block->flags |= BlockReturnSelf;*/
	if (file->sbumpc() != EOF)
	  {
	    ParseError(ff, "F Misplaced closing terminator");
	    module->temp = FailedParse;
	  }
	if (PrintExprFlag)
	  {
	    module->temp->printon(cout);
	    fflush(stdout);
	  }
	if (module->temp == FailedParse)
	    module->flags |= ModuleError;
	module->nextToCompile = ModulesToCompile; ModulesToCompile = module;
	break;
#if 0
      case 2:
      { /* Bin */
	suffix = GetSuffix(buf);
	if (suffix) strcpy(suffix, ".Qx");
	else strcat(buf, ".Qx");
	LoadModule(module, file);
        HandleAuxFile(module, buf);
      }
#endif
      }
    return module;
}

struct ModuleList * ReadModule(char *file_name, struct Module *into)
{
    struct ModuleList *link;
#if 0
  retry:
#endif
    struct Module *module = CheckModule(file_name);
    if (module == NULL)
      {
#if 0
	fprintf(stderr,
	    "Alternate module (or file) name (for \"%s\"; ^G to give up)\n: ",
	    file_name);
	fflush(stderr);
	file_name = (Name)ReadLine(stdin);
	if (file_name != NULL) goto retry;
#endif
	return NULL;
      }

    link = (struct ModuleList*)malloc(sizeof(struct ModuleList));
    if (into != NULL)
      { *into->lastMod = link; into->lastMod = &link->next;}
    link->module = module;
    link->isPrivate = 0;
    link->included = 0;
    link->next = NULL;
    link->name = file_name;

#if 0
    struct Declaration *decl = Symbol2Declaration(NULL);
    decl->size = Include_SizeCode;
    decl->defining.P = (Object)module;
    MakeLabelToken(&decl->token, NULL, module, 0);
    decl->flags |= KnownDeclaration|SetDeclaration;
    AddDeclaration(into->block, decl);
#endif
    return link;
  }

#if 0
Expr *
ParseLoad(ff)
    struct ParseFile *ff;
  {
    Name module_name = GetWord(ff, 0);
    struct ModuleList *mod;
    struct Module *module;
    if (!HasHType(module_name, TStringOb))
      {
	ParseErr0(0, "LOAD command not followed by module name");
	return (struct ModuleList*)FailedParse;
      }
   mod = ReadModule(module_name, ff->module);
   if (mod == NULL) return FailedParse;
   module = mod->module;
   if (module->kind != 1 && module->block != NULL)
	module = (struct Module*)module->block;
   return new ExprQuote(module);
  }
#endif

#if 0
AppendIncludedModule(module, included)
/* append included to module->directlyIncluded */
    struct OldModule *module, *included;
  { register struct OldModule **ptr, **new, **ptr2; int n_old = 0;
if (HasHType(module, ModuleList) || HasHType(module, Module))
  { struct ModuleList *link, **ptr;
    if (HasHType(module, ModuleList))
	module = ((struct ModuleList*)module)->module;
    ptr = &((struct Module*)module)->imported;
    while (*ptr != NULL) ptr = &(*ptr)->next;
    *ptr = link = (struct ModuleList*)malloc(sizeof(struct ModuleList));
    link->module = module;
    link->isPrivate = 0;
    link->included = 1;
    link->next = NULL;
    return;
  }
    ptr = ptr2 = module->directlyIncluded;
    for (; *ptr != NULL; n_old++, ptr++) ;
    ptr = new = (struct OldModule **)malloc((n_old + 2) * sizeof(struct OldModule**));
    new[n_old] = included; new[n_old + 1] = NULL;
    while ( --n_old >= 0 ) *ptr++ = *ptr2++;
    ptr = module->directlyIncluded;
    if (ptr != NullModuleList) free(ptr);
    module->directlyIncluded = new;
  }

DumpModule(module, cf, label)
    struct OldModule *module; struct CFile *cf;
  { struct Label L_mods[1]; struct OldModule **ext_mod;
    MakeGenLabel(L_mods, cf->generate_label_number());
    CompLab(cf, L_mods);
    for (ext_mod = module->directlyIncluded; *ext_mod != NULL; ext_mod++)
      { char buf[60];
	sprintf(buf, ModuleLabelFormat, (*ext_mod)->name);
	CompPtr(cf, buf, 0, 0);
      }
    Comp1Long(cf, 0, 0);
    DumpTable(module, cf, label);
    CompileLabelTo(cf, module->name);
    Comp1Long(cf, 0, "text_start");
    CompHandle(cf, L_mods);
  }
#endif

struct ModuleLink { struct ModuleLink *next; struct Module *module;};

#if 0
/* if called at run-time, should return value, and handle imports */
/* Also called from Module'{_pre,_post}fix in procs.Q - not working */
struct Declaration *
FindDeclInModule(module, name, outer)
    struct Module *module; Name name;
    struct ModuleLink *outer; /* to avoid infinite recursion */
  { struct Declaration *decl;
    struct ModuleLink pair;
    if (module == NULL) return NULL;
    pair.next = outer; pair.module = module;
    decl = module->block->decls.first;
    for ( ; decl != NULL; decl = decl->next)
	if (((struct Field*)decl)->name == name) return decl;
	else if (DeclIsInclude(decl))
	  { struct Module *mod;
	    struct ModuleLink *list;
#if 0
	    if (decl->v.token.kind == ConstToken)
	      {
		mod = (struct Module*)LookupLabelToken(&decl->v.token);
		if (mod != NULL && HasHType(mod, Module))
		  {
		    for (list = outer; ; list = list->next)
			if (list == NULL)
			  { struct Declaration *d =
				FindDeclInModule(mod, name, &pair);
			     if (d != NULL) return d;
			     else break;
			  }
			else if (list->module == mod) break;
		  }
	    }
#endif
	  }
    return NULL;
  }

Object
SearchModule(module, name)
    struct Module *module; Name name;
  { struct Declaration *decl = FindDeclInModule(module, name, NULL);
    Object ob;
    if (decl != NULL)
      { Object ob = LookupLabelToken(&decl->v.token);
	if (ob != NULL) return ob;
      }
    RAISE(Undefined, name);
  }
#endif


EXTERN void * LookupGlobalLabel(char *name, int *found);
EXTERN void * lookup_global_label(char *name, int *found);
void * LookupGlobalLabel(char *name, int *found)
{
    return lookup_global_label(name, found);
}

void AddGlobalLabel(char *name, void *value)
{
    fprintf(stderr, "[AddGlobalLabel not implemented]\n");
}

Root* CheckLookupLabel(char *name)
{
    int found;
    Root *object = (Root*)LookupGlobalLabel(name, &found);
    if (!found)
      {
	RunError(0, "Label %s undefined but accessed\n", name);
	return (Root*)NoValue;
      }
    return object;
}

void RegisterModule(Object self, const StringC** name)
{
    struct Module *mod =
	LookupModule(EnterSymbol(name->chars(), name->leng()));
    mod->kind = 4;
    mod->block = (struct Block*)self;
}

void DumpModule()
{
    RunError(0, "DumpModule not implemented!\n");
    abort();
}

#if 0
InsertGlobal(name, value)
    Name name; Object value;
{
    struct Declaration *decl = Symbol2Declaration(name);
    decl->flags |= PrivateDeclaration|KnownDeclaration;
#if 0
    SetDeclToken(decl, DefaultModule->name, 0);
    decl->block = value;
    ModPutDecl(DefaultModule, decl, 1);
#endif
    if (GlobalLabels == NULL) read_symbol)table(NULL);
    AddGlobalLabel(decl->v.token.u.constant.label, value);
}
#endif

#if 0
InsertGlobalName(tab, name, value)
  { register Object *pp = (Object*)SymbolLookup(tab, name);
    if (value == FORGET)
	pp[0] = NULL;
    else if (pp[0] == HashNull || pp[1] == NULL ||
      (!HasHType(pp[1], Statement)
       && !HasHType(pp[1], PostfixProc) && !HasHType(pp[1], PrefixProc)))
      { pp[0] = name; pp[1] = value; }
    else
      { struct Polymorphic *poly = AllocStd(Polymorphic);
	fprintf(stderr, "[Overloadinging %s]\n", name);
	fflush(stderr);
	poly->first = value;
	poly->rest = pp[1];
	pp[1] = (Object)poly;
      }
  }

InsertGlobal(name, value)
    Object name;	/* either a Declaration or a Symbol */
    Object value;
  { register struct PendingValue *new;
    Object label = name; int prvate = 0;
    if (HasHType(name, Declaration))
      { struct Declaration *decl = (struct Declaration*)name;
	if ((decl->flags & PrivateDeclaration) != 0) prvate = 1;
	if (decl->v.token.kind == ConstToken)
	  {
	    label = decl->v.token.u.constant.label;
	    AddGlobalLabel(label, value);
	  }
	decl->block = value; decl->flags |= KnownDeclaration;
	name = decl->name;
      }
    if (name == sAsterisk)
	return;
    InsertGlobalName(Env, name, value);
#if 0
    if (HasHType(value, Variable) && ((struct Variable*)value)->name == NULL)
	((struct Variable*)value)->name = NULL; /* hack */
    if (value == IMPORT || value == FORGET)
	return;
    new = (struct PendingValue*)malloc(sizeof(struct PendingValue));
    new->next = NULL;
    new->lab.flags = GlobalLabels;
    new->lab.kind = StringLabel;
    new->lab.label = label;
    new->lab.offset = 0;
    new->value = value;
    SaveListLast->next = new;
    SaveListLast = new;
    if (!prvate)
	Insert(module, name, value);
#endif
  }
#endif

#if 0
static
EnterModuleElement(dummy, name, value)
  {
    InsertGlobalName(Env, name, value);
  }

EnterModuleNames(module)
    struct Module *module;
  {
    ForEachInTab(module, EnterModuleElement, 0);
  }
#endif
