#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "BasicNewton.h"

/* While not usually a fan of globals, the input file pointer was basically
   just being passed to everything anyway.  Making it a global is just simpler
   and more efficient. */
extern FILE *InFile;

void processContentsFrame(ULong frameStart,int needsFixing)
{
  ObjectHeaderPlus frame,symbol;
  ULong curPos;
  ULong numOfSlots,slotNum;
  ULong *mapRefs,*slots;
  char **map;
  ULong symbolLen;
  char *symbolData;
  if(fseek(InFile,frameStart,SEEK_SET)!=0)reportError();
  frame.flags=getULong();
  frame.marker=getULong();
  frame.classOrMap=getULong();
  if((!isFrame(frame.flags))||
     (frame.marker!=0)||
     (!isPointer(frame.classOrMap)))
  {
    puts("Bad part frame.");
    exit(-1);
  }
  numOfSlots=getNumOfSlots(frame.flags);
  mapRefs=malloc(numOfSlots*sizeof(ULong));
  slots=malloc(numOfSlots*sizeof(ULong));
  map=malloc(numOfSlots*sizeof(wchar_t*));
  curPos=ftell(InFile);
  linearizeMap(getPointer(frame.classOrMap),mapRefs,numOfSlots-1,numOfSlots);
  if(fseek(InFile,curPos,SEEK_SET)!=0)reportError();
  for(slotNum=0;slotNum<numOfSlots;slotNum++)
  {
    slots[slotNum]=getULong();
  }
  for(slotNum=0;slotNum<numOfSlots;slotNum++)
  {
    if(isPointer(mapRefs[slotNum]))
	{
	  if(fseek(InFile,getPointer(mapRefs[slotNum]),SEEK_SET)!=0)reportError();
	  symbol.flags=getULong();
	  symbol.marker=getULong();
	  symbol.classOrMap=getULong();
	  if(symbol.marker!=0||!isBinary(symbol.flags)||!isSymbol(symbol.classOrMap))
	  {
	    puts("Bad label.");
		exit(-1);
	  }
	  symbolLen=extractSize(symbol.flags)-4*sizeof(ULong);
	  symbolData=malloc(symbolLen+1);
	  loadSymbol(getPointer(mapRefs[slotNum]),symbolData);
	  map[slotNum]=symbolData;
	}
  }
  printField("title",map,slots,numOfSlots);
  puts("\nby");
  printField("author",map,slots,numOfSlots);
  putchar('\n');
  printField("copyright",map,slots,numOfSlots);
  puts("\n");
  printStringsFromField("browsers",map,slots,numOfSlots,"\n");
  printf("\n\n");
  printStringsFromField("rendering",map,slots,numOfSlots,"\n");
  for(slotNum=0;slotNum<numOfSlots;slotNum++)free(map[slotNum]);
  free(map);
  free(mapRefs);
  free(slots);
}

int main(int argc,char *argv[])
{
  FixedHeader fh;
  PartEntry *pe;
  RelocationHeader rh;
  char versionChar;
  unsigned int partNum;
  int relocationUsed=0;
  ObjectHeaderPlus first;
  ULong partFrameRef;
  InfoRef finalVariableLengthEntity;
  ULong softwareCreditsStart,softwareCreditsMaxLen;
  char *softwareCredits;
  int needsFixing=0;

  if(argc!=2)
  {
    printf("Usage: %s book\n",argv[0]);
    exit(1);
  }
  InFile=fopen(argv[1],"rb");
  if(InFile==NULL)
  {
    puts("Unable to open input file.");
    exit(-1);
  }

  /* parse the important stuff in the fixed header */

  if(fread(fh.signature,1,SIGNATURE_LEN,InFile)!=SIGNATURE_LEN)
  {
    puts("Can't read input file.");
    exit(-1);
  };
  versionChar=fh.signature[SIGNATURE_LEN-1];
  fh.signature[SIGNATURE_LEN-1]='\0';
  if((strcmp(fh.signature,"package")!=0)||(versionChar!='0'&&versionChar!='1'))
  {
    puts("Bad magic.");
    exit(-1);
  }
  fh.reserved1=getULong();
  fh.flags=getULong();
  if((fh.flags&kRelocationFlag)&&(versionChar=='1'))
  {
    // there is a relocation area...
    relocationUsed=1;
  }
  else if((fh.flags&kAutoRemoveFlag)||(fh.flags&kRelocationFlag))
  {
    puts("Bad flag settings.");
    exit(-1);
  }
  fh.version=getULong();
  finalVariableLengthEntity.offset=fh.copyright.offset=getUShort();
  finalVariableLengthEntity.length=fh.copyright.length=getUShort();
  fh.name.offset=getUShort();
  fh.name.length=getUShort();
  if(fh.name.offset>finalVariableLengthEntity.offset)
  {
    finalVariableLengthEntity.offset=fh.name.offset;
	finalVariableLengthEntity.length=fh.name.length;
  }
  fh.size=getULong();
  fh.creationDate=getULong();
  fh.modificationDate=getULong();
  if(fh.modificationDate!=0&&fh.modificationDate<fh.creationDate)
  {
    puts("Date mismatch.");
	exit(-1);
  }
  fh.reserved3=getULong();
  fh.directorySize=getULong();
  if(fh.directorySize>fh.size)
  {
    puts("Directory size > total size error.");
    exit(-1);
  }
  fh.numParts=getULong();

  /* parse the important stuff in the part entry (or entries) */

  pe=malloc(fh.numParts*sizeof(PartEntry));
  for(partNum=0;partNum<fh.numParts;partNum++)
  {
    pe[partNum].offset=getULong();
    pe[partNum].size=getULong();
    pe[partNum].size2=getULong();
    if(pe[partNum].size!=pe[partNum].size2)
    {
      puts("Part entry size mismatch.");
      exit(-1);
    }
    pe[partNum].type=getULong();
	pe[partNum].reserved1=getULong();
	pe[partNum].flags=getULong();
	pe[partNum].info.offset=getUShort();
	pe[partNum].info.length=getUShort();
	if(pe[partNum].info.offset>finalVariableLengthEntity.offset)
    {
      finalVariableLengthEntity.offset=pe[partNum].info.offset;
	  finalVariableLengthEntity.length=pe[partNum].info.length;
    }
  }

  /* parse the important stuff in the relocation section (if needed) */

  rh.relocationSize=0;
  if(relocationUsed)
  {
    rh.reserved=getULong();
    rh.relocationSize=getULong();
	rh.pageSize=getULong();
	rh.numEntries=getULong();
	rh.baseAddress=getULong();
  }

  /* check for optional software credits */

  softwareCreditsStart=finalVariableLengthEntity.offset+finalVariableLengthEntity.length+
                       fixedHeaderSize+fh.numParts*partEntrySize;
  softwareCreditsMaxLen=fh.directorySize-fixedHeaderSize-fh.numParts*partEntrySize;
  softwareCredits=malloc(softwareCreditsMaxLen);
  if(fseek(InFile,softwareCreditsStart,SEEK_SET)!=0)reportError();
  if(fread(softwareCredits,1,softwareCreditsMaxLen,InFile)!=softwareCreditsMaxLen)
  {
    puts("File read error.");
	exit(-1);
  }
  softwareCredits[softwareCreditsMaxLen-1]='\0';	/* just in case */
  if(strcmp(softwareCredits,"Built with Newton Press")==0)
    needsFixing=1;									/* fix the dreaded Newton Press date bug */

  /* (finally) Fetch real information from the part(s) */

  for(partNum=0;partNum<fh.numParts;partNum++)
  {
    /* only bother processing book parts */
    if(pe[partNum].type==bookMarker)
    {
      if(fseek(InFile,
               fh.directorySize+rh.relocationSize+pe[partNum].offset,
               SEEK_SET)!=0)reportError();
      first.flags=getULong();
      first.marker=getULong();
      first.classOrMap=getULong();
      if((!isArray(first.flags))||
         (first.marker>1)||
         (!isNil(first.classOrMap))||
         (getNumOfSlots(first.flags)!=1))
      {
        puts("Bad first object.");
        exit(-1);
      }
      partFrameRef=getULong();
      if(isPointer(partFrameRef))
        processPartFrame(getPointer(partFrameRef),needsFixing);
      else
      {
        puts("Bad part frame pointer.");
        exit(-1);
      }
    }
  }

  /* Clean up */

  free(softwareCredits);
  free(pe);
  if(fclose(InFile)!=0)reportError();
  return 0;
}

