#include <stdio.h>
#include "common.h"
#include "module.h"
#include "allocate.h"
#include "hash.h"
#include "error.h"
#include "segment.h"
#include "public.h"
#include "extern.h"
#include "list.h"

extern HASHREC ** PublicHash;
extern char *modname;
HASHREC ** ExternHash;				// Pass 1 hash table for externs
int NumExternItems = 0;				// Hash table is used as an indexed array
															// during pass 2, this is the number of items
															// (current module)

/*
 * Read in the extern record
 */
static void ReadExterns(BOOL is32, BYTE *buffer, 
			int size, char *name, uint pass, BOOL toMangle)
{
  EXTERN *Extern;

	// Size of record is 0: then just increment the index used for the next
  // extern record
  if (size == 0) {
    if (pass == 2) {
			ExternHash[NumExternItems++] = 0;
		}
		return;
	}
  // Read in all strings this record
  while(size > 0) {
		char *string;

		// Read the name
    string = ReadName(&buffer, &size, toMangle);

		// Ignore the type index
    ReadIndex(&buffer, &size);
		
		Extern = (EXTERN *) AllocateMemory(sizeof(EXTERN));
		Extern->name = string;
    Extern->module = name;
		Extern->nonlibs = 0;
    if (pass == 1) {
      if (LookupHash(PublicHash, string) == 0) {
        if (AddHash(ExternHash, Extern, string))
#ifdef DEBUG
          printf("Extern %s already in extern table\n",Extern->name);
				else
          printf("Adding %s to extern table\n",Extern->name);
#else
				;
#endif
				
      }
		  else {
#ifdef DEBUG
        printf("Extern %s already in public table\n",Extern->name);
#endif
			  DeallocateMemory(Extern);
      }
    }
    else {
			PUBLIC **t = LookupHash(PublicHash, string);
			if (t)
				t = *t;
			ExternHash[NumExternItems++] = (HASHREC *) t;
		}
  }
    
  CheckSize(size);
}
/*
 * Initialize the table at the start of a pass
 */
void ExternInit(uint pass)
{
	NumExternItems = 0;
  if (pass == 1) {
    ExternHash = CreateHashTable();
  }
}
/*
 * Do any cleanup required at the end of each module- i.e. erase the
 * table on pass 2 and release VIRDEF records
 */
void ExternModuleRundown(uint pass)
{
  if (pass == 2) {
		int i;
		for (i=0; i < NumExternItems; i++) {
			PUBLIC *p = ExternHash[i];
			if (p) {
				if (p->VIRDEF) {
					DeallocateMemory(p->name);
					DeallocateMemory(p);
				}
			}
		}
		NumExternItems = 0;
	}
}
/*
 * Resolve an external given a public
 */
void ResolveExternal(char *name)
{
  HASHREC **p;
	p=LookupHash(ExternHash, name);
	if (p && *p) {
    EXTERN *q = *p;
		*p=q->link;
#ifdef DEBUG
		printf("Deleting extern %s module %s from hash table\n",q->name, q->module);
#endif
					
	  DeallocateMemory(q->name);
	  DeallocateMemory(q);
  }
}
/*
 * List all the unresolved externs
 */
void EnumerateExterns(void)
{
  int i;
  for (i = 0 ; i < HASH_TABLE_SIZE; i++) {
    HASHREC **p;
    if (*(p = &ExternHash[i]) != 0) {
      while (*p) {
				EXTERN *q = (EXTERN *) *p;
				Error("Unresolved extern %s in module %s",q->name, q->module);
				p = *p;
			}
		}
  }
}
/*
 * Get a public record according to the xternal
 */
PUBLIC *GetExtern(uint index)
{
  PUBLIC *p;
  if ((index > NumExternItems) || (index == 0))
    fatal("Undefine external being indexed in module %s", modname);

  p = ExternHash[index-1];
  if (p)
	  (p->referenced)++;

  return(p);
}
/*
 * End of pass cleanup
 */
void ExternRundown(uint pass)
{
  if (pass == 1){
#ifdef DEBUG
		DumpHashTable(ExternHash,"Extern Dump");
#endif DEBUG
  }
  else {
		RemoveHashTable(ExternHash);
  }
}
// Public extern definitions record handled here
void ExternDefinitions(BOOL is32,BYTE *buffer, 
			int size, char *name, uint pass)
{
    ReadExterns(is32,buffer, size,name, pass, FALSE);
}
// Local extern definitions record handled here
void LocalExternDefinitions(BOOL is32,BYTE *buffer, 
			int size, char *name, uint pass)
{
    ReadExterns(is32,buffer, size,name, pass, TRUE);
}