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

extern EXTERN ** ExternHash;
HASHREC ** PublicHash;		/* Public hash table */

static LIST *PublicLinkList; /* Link list of publics 
															* (gathered on a per-module basis 
															* for segment processing */

/* Error: duplicate public */
void Duplicate(char *sym, char *source, char *new)
{
  Error("%s defined in module %s duplicated in %s", sym, source, new);
}
/* Read a publics record */

static void ReadPublics(BOOL is32, BYTE *buffer, 
			int size, char *name, uint pass, BOOL toMangle)
{
  PUBLIC *public;
  /* We allow for the OMF possibility of multiple symbols per record
	 * But I've never seen a borland object file that didn't do a record
	 * for every symbol */
  while(size > 0) {
	  char *string;
		uint BaseSegment, PublicOffset;
    PUBLIC *t;
		// Read group and segment indexes
    ReadIndex(&buffer, &size);
    BaseSegment = ReadIndex(&buffer, &size);

		// Ignore the Base Frame
    if (BaseSegment == 0) {
		  buffer += 2;	
      size -= 2;
    }

		// Read the name and offset
    string = ReadName(&buffer, &size, toMangle);
    PublicOffset = ReadSizedValue(&buffer, &size, is32);

    // Error if unreferenced
		if (BaseSegment == 0)
			Error("Unbased public %s in module %s",string, name);

		// Ignore the type index
    ReadIndex(&buffer, &size);
		
		public = (PUBLIC *) AllocateMemory(sizeof(PUBLIC));
		public->name = string;
    public->module = name;
		public->segment = (SEGMENT *)BaseSegment;
    public->offset = PublicOffset;
		public->absoffset = 0;
    public->referenced = 0;
    public->VIRDEF = FALSE;
    if ((t = (PUBLIC *)AddHash(PublicHash, public, string)) != 0)
      Duplicate(t->name, t->module, name);
		
    if (pass == 1) {
#ifdef DEBUG
			printf("Adding %s to public table\n",string);
#endif
			ResolveExternal(string);
			AddHash(PublicHash, public, string);
		  AppendToList(&PublicLinkList, public);
		}
  }
    
  CheckSize(size);
}
/*
 * Pre-pass initialization
 */
void PublicInit(uint pass)
{
  PublicLinkList = 0;
  if (pass == 1) {
    PublicHash = CreateHashTable();
  }
}
/*
 * Per module rundown.  Used to point the segment field of the public
 * record at the segment the public is defined in
 */
void PublicModuleRundown(uint pass)
{
  if (pass == 1)
    while (PublicLinkList) {
      PUBLIC *data = (PUBLIC *) UnlinkFromList(&PublicLinkList,PublicLinkList);
			// Segment fixup code goes here
		  if (pass == 1) {
				data->segment = GetSegment((uint)data->segment);
			}
		}
}
/*
 * Give each public an absolute address based on where the segment is located
 */
void ResolvePublics(uint pass)
{
  int i;
#ifdef DEBUG
  printf("Public fixups:\n");
#endif PUBLIC
  for (i=0; i < HASH_TABLE_SIZE; i++) {
    PUBLIC *p = PublicHash[i];
    while (p) {
			SEGMENT *q = p->segment;
			p->absoffset = p->offset + q->absoffset;
#ifdef DEBUG
			printf("  %s module %s location %x\n", p->name, p->module, p->absoffset);
#endif DEBUG
			p = p->link;

		}
	}
}
/*
 * Post pass rundown
 */
void PublicRundown(uint pass)
{
  if (pass == 1){

  }
  else {
#ifdef DEBUG
		DumpHashTable(PublicHash,"Public Dump");
#endif DEBUG
		RemoveHashTable(PublicHash);
  }
}
/* 
 * Public public definitions
 */
void PublicDefinitions(BOOL is32,BYTE *buffer, 
			int size, char *name, uint pass)
{
  if (pass == 1)
    ReadPublics(is32,buffer, size,name, pass, FALSE);
}
/*
 * Private public definitions
 */
void LocalPublicDefinitions(BOOL is32,BYTE *buffer, 
			int size, char *name, uint pass)
{
  if (pass == 1)
    ReadPublics(is32,buffer, size,name, pass, TRUE);
}