//
// Die Implementation der drei Klassen:
//
// -- ResFile:	enthält die Daten eines ResourceFiles
//
// -- ResType:	verwaltet eine Typeliste
//
// -- ResID:	verwaltet eine Resource.
//
//
// 22.03.1992 Andre    geschrieben
//

extern "C" {
#include <unistd.h>
#include <stdlib.h>
};
#include "resources.h"
#include "utils.h"
#include "export.h"

#define ONE ((size_t)1)

// ----------------------------------------------------------------------
//
// Die Methoden für ResID
//

ResID::ResID(MacResID *mri, unsigned long theResType,
	     void *theMacResNameList, unsigned long FileDataOffset)
      : node(NULL, nocopy, mri->ID, theResType)
{
   if( mri->NameOffset!=-1 ) // Nur wenn es einen Namen gibt diesen eintragen
      node::setname( pstr2cstr( theMacResNameList+mri->NameOffset ) );
   Attrs = mri->Attrs;
   DataOffset = mri->DataOffset + FileDataOffset;
}

ResID::~ResID(void)
{
}

int ResID::export(void)
{
   extern FILE *globalFile;

   return exportres(getpack(),globalFile,DataOffset);
}

#if 0
int ResID::export(void)
{
   unsigned long   len;
   FILE *out;
   extern FILE *globalFile;
   char  *buffer;

   if( (out=fopen("T:ResGrepOut","w"))==NULL )
   {
      printf("Kann OutFile nicht öffnen.\n");
      return 1;
   }
   printf("Lese Daten ab: %5d\n",DataOffset);

   if( fseek(globalFile,(long)DataOffset,SEEK_SET)!=0 )
   {
      printf("Fehler bei fseek.\n");
      return 2;
   }

   // Länge bestimmen.
   if( fread( (void *)&len, sizeof(unsigned long), ONE, globalFile)!=ONE )
   {
      printf("Fehler bei fread.\n");
      return 3;
   }

   printf("Länge der Daten: %d\n",len);

   if( (buffer=(char *)malloc(len))==NULL )
   {
      printf("Fehler bei malloc.\n");
      return 4;
   }

   if( fread((void *)buffer,sizeof(char),len,globalFile)!=len )
   {
      printf("Fehler bei fread (2).\n");
      return 5;
   }
   if( fwrite( (void *)buffer,sizeof(char),len,out)!=len )
   {
      printf("Fehler bei fwrite.\n");
      return 6;
   }
   fclose(out);
   free(buffer);
   return 0;
}
#endif


//---------------------------------------------------------------------------
//
// Die Methoden für ResType
//

ResType::ResType(MacResType *mrt, MacResID *mri, void *theMacResNameList,
		 unsigned long FileDataOffset, char *name, long pri)
	: node(name, nocopy, pri, mrt->theResType)
	, theResID()
{
   NumberOfResIDs = mrt->NumberOfResRef + 1;

   for(int index=0; index<NumberOfResIDs; index++)
   {
      ResID *ri;

      // Auch hier ein neues Listenelement belegen und initialisieren.
      ri= new ResID(&mri[index], mrt->theResType, theMacResNameList,
		    FileDataOffset);
      theResID.enqueuepri(ri);
   }
}

ResType::~ResType(void)
{
   node *n;

   while( (n=theResID.remhead())!=NULL )
      delete ((ResID *)n);
}

void ResType::hidewin(struct IntuiMessage *msg)
{
   node *n;

   // Fehler abfangen.
   if( msg->IDCMPWindow==NULL )
      return;

   // Bei NULL auf jeden Fall verstecken.
   if( msg==NULL )
      theResID.hide();

   n=theResID.checkdis(msg);
   if( msg->IDCMPWindow==NULL )
      theResID.hide();
}

void ResType::revealwin(struct IntuiMessage *msg)
{
   node *n;

   // Fehler abfangen.
   if( msg->IDCMPWindow==NULL )
      return;

   // Bei NULL auf jeden Fall hervorkommen lassen
   if( msg==NULL )
      theResID.reveal();

   n=theResID.checkdis(msg);
   if( msg->IDCMPWindow==NULL )
      theResID.reveal();
}

void ResType::closewin(struct IntuiMessage *msg)
{
   node *n;

   // Fehler abfangen.
   if( msg->IDCMPWindow==NULL )
      return;

   // Bei NULL auf jeden Fall schliessen
   if( msg==NULL )
      theResID.closedis();

   n=theResID.checkdis(msg);
   if( msg->IDCMPWindow==NULL )
      theResID.closedis();
}

void ResType::displaywin(struct MsgPort *mp, struct Menu *menu)
{
   static char buffer[256];

   sprintf(buffer,"Resgrep - IDs of Type '%4s' from [%ld] %s",
	   pack2cstr(node::getpack()),getpri(),getname());
   if( theResID.display(mp,menu,readwrite,"pn",35,6,NewXPos(),NewYPos(),
			buffer+22, buffer)==0)
      CoordsUsed();
}

node *ResType::check(struct IntuiMessage *msg)
{
   node *n;
   int er;

   // Fehler abfangen.
   if( msg->IDCMPWindow==NULL )
      return NULL;
   // Ist das aktuelle Fenster (also das Type-Fenster (Inhalt: verschiedene
   // IDs)) gemeint?
   n=theResID.checkdis(msg);
   if( msg->IDCMPWindow==NULL )  // Das Fenster war gemeint!
   {
      if( n!=NULL )
      {
	 er=((ResID *)n)->export();
	 if( er!=ERR_NO )
	    ResError("Error during data export.");
	 return NULL;
      }
      else
	 return NULL;
   }
   return NULL;
}


//---------------------------------------------------------------------------
//
// Die Methoden zu ResFile
//

ResFile::ResFile(void) : node(NULL), theResType()
{
   // Initialisierung und der Versuch eine Datei zu öffnen.
   fullname=NULL;
   fp=NULL;
   NumberOfResTypes=0;
   DataOffset=0;
   Attrs=0;
}

ResFile::~ResFile(void)
{
   node *n;

   close();
   while( (n=theResType.remhead())!=NULL )
   {
      ((ResType *)n)->closewin(NULL);
      delete ((ResType *)n);
   }
}

// Öffnet ein Resourcefile. 0=Alles OK. Sonst: Fehler.
// Die Fehler im einzelnen:
// 0:  Kein Fehler
// 1:  Kann File nicht öffnen
// 2:  File nicht in Ordnung (festgestellt beim Einlesen vom ResHeader)
// 3:  Kein Speicher für ResMap
// 4:  File nicht in Ordnung (festgestellt beim Einlesen vom ResMapHeader)


int ResFile::open(char *name)
{
   MacResHeader theMacResHeader; // Da ist der ResFile Header drin.
   void        *theMacMap;	 // Eine Kopie der ganzen ResMap im Speicher.
   MacResMapHeader *theMacResMapHeader;  // Den Anfang dieser MacMap bildet
				 // der MacResMapHeader.
   MacResFile  *theMacResFile;	 // Die Resource Type Liste,
				 // enthält die Anzahl der verschiedenen
				 // Recourcetypen und die Resourcetypen
				 // selbst.
   void        *theMacResNameList;	 // Der Zeiger auf den Anfang der
				 // Namen der Resourcen.

   if( name==NULL )
      name=ASLGetName();

   node::setname(name);
   if( (fp=fopen(name,"r"))==NULL )      // Der Versuch, das File zum lesen
      return 1; 		 // zu öffnen.
   rewind(fp);                   // Das File zurücksetzen.

				 // Dann den Resource File Header einlesen.
   if( fread( (void *)&theMacResHeader, ONE, sizeof(MacResHeader),fp )
	      != sizeof(MacResHeader) )
      return 2;

   DataOffset = theMacResHeader.DataOffset;

				 // Die ResMap einlesen:
   if( (theMacMap=malloc(theMacResHeader.MapLength))==NULL )
      return 3;
   if( fseek(fp,theMacResHeader.MapOffset,SEEK_SET) )
   {
      free(theMacMap);
      return 4;
   }
   if( fread(theMacMap, ONE, (size_t)theMacResHeader.MapLength,fp)
      != (size_t)theMacResHeader.MapLength )
   {
      free(theMacMap);
      return 4;
   }

   // Dann die neue Filenummer festlegen.


   setpri(GetNewFileNum());

				 // Dann erstmal nachsehen, ob das
				 // Eingelesene zu gebrauchen ist.
   theMacResMapHeader  = (MacResMapHeader *)theMacMap;
   Attrs	       = theMacResMapHeader->Attrs;
   theMacResFile       = (MacResFile *)( ((char *)theMacMap)
			     + theMacResMapHeader->TypeListOffset);
   NumberOfResTypes    = theMacResFile->Number;
   theMacResNameList   = (MacResFile *)( ((char *)theMacMap)
			     + theMacResMapHeader->NameListOffset);

   for(int index=0; index<=theMacResFile->Number; index++)
   {
      // Für jeden Resource-Typen ein ResType-Objekt anlegen
      ResType *rt;

      rt=new ResType(&(theMacResFile->Type[index]),
		      (MacResID *)(((char *)theMacResFile) +
		       theMacResFile->Type[index].RefListOffset),
		     theMacResNameList,
		     DataOffset,
		     name, node::getpri());

      // Und dann noch tierisch wichtige Sachen initialisieren:
      // rt->setname( node::getname() );
      // rt->setpri( node::getpri() );

      theResType.enqueuepack(rt);
   }
   return 0;
}

void ResFile::close(void)
{
   fclose(fp);
}

FILE *ResFile::getfp(void)
{
   return fp;
}


// Die Fensterfunktionen für das ResFile

void ResFile::hidewin(struct IntuiMessage *msg)
{
   node *n;

   // Fehler abfangen.
   if( msg->IDCMPWindow==NULL )
      return;

   // Bei NULL auf jeden Fall verstecken.
   if( msg==NULL )
      theResType.hide();

   n=theResType.checkdis(msg);
   if( msg->IDCMPWindow==NULL )
      theResType.hide();
   else
      for(node *no=theResType.getfirst(); no->getsucc(); no=no->getsucc())
	 ((ResType *)no)->hidewin(msg);
}

void ResFile::hidechild(struct IntuiMessage *msg)
{
   node *n;

   // Alle verstecken.
   if( msg==NULL )
   {
      theResType.hide();
      for(node *no=theResType.getfirst(); no->getsucc(); no=no->getsucc())
	 ((ResType *)no)->hidewin(NULL);
      return;
   }

   if(msg!=NULL && msg->IDCMPWindow!=NULL )
   {
      n=theResType.checkdis(msg);
      if( msg->IDCMPWindow==NULL )
      {
	 theResType.hide();
	 msg=NULL;
      }
   }

   for(node *no=theResType.getfirst(); no->getsucc(); no=no->getsucc())
      ((ResType *)no)->hidewin(msg);
}

void ResFile::revealwin(struct IntuiMessage *msg)
{
   node *n;

   // Fehler abfangen.
   if( msg==NULL )
      return;

   // Bei NULL auf jeden Fall hervorkommen lassen
   if( msg==NULL )
      theResType.reveal();

   n=theResType.checkdis(msg);
   if( msg->IDCMPWindow==NULL )
      theResType.reveal();
   else
      for(node *no=theResType.getfirst(); no->getsucc(); no=no->getsucc())
	 ((ResType *)no)->revealwin(msg);
}

void ResFile::revealchild(struct IntuiMessage *msg)
{
   node *n;

   // Alle hervorholen.
   if( msg==NULL )
   {
      theResType.reveal();
      for(node *no=theResType.getfirst(); no->getsucc(); no=no->getsucc())
	 ((ResType *)no)->revealwin(NULL);
      return;
   }

   if(msg!=NULL && msg->IDCMPWindow!=NULL )
   {
      n=theResType.checkdis(msg);
      if( msg->IDCMPWindow==NULL )
      {
	 theResType.reveal();
	 msg=NULL;
      }
   }

   for(node *no=theResType.getfirst(); no->getsucc(); no=no->getsucc())
      ((ResType *)no)->revealwin(msg);
}

void ResFile::closewin(struct IntuiMessage *msg)
{
   node *n;

   // Fehler abfangen.
   if( msg==NULL || msg->IDCMPWindow==NULL )
      return;

   // Bei NULL auf jeden Fall schliessen
   if( msg==NULL )
      theResType.closedis();

   n=theResType.checkdis(msg);
   if( msg->IDCMPWindow==NULL )
      theResType.closedis();
   else
      for(node *no=theResType.getfirst(); no->getsucc(); no=no->getsucc())
	 ((ResType *)no)->closewin(msg);
}

void ResFile::closechild(struct IntuiMessage *msg)
{
   node *n;

   // Alle verstecken.
   if( msg==NULL )
   {
      theResType.closedis();
      for(node *no=theResType.getfirst(); no->getsucc(); no=no->getsucc())
	 ((ResType *)no)->closewin(NULL);
      return;
   }

   if(msg!=NULL && msg->IDCMPWindow!=NULL )
   {
      n=theResType.checkdis(msg);
      if( msg->IDCMPWindow==NULL )
      {
	 theResType.closedis();
	 msg=NULL;
      }
   }

   for(node *no=theResType.getfirst(); no->getsucc(); no=no->getsucc())
      ((ResType *)no)->closewin(msg);
}

void ResFile::displaywin(struct MsgPort *mp, struct Menu *menu)
{
   static char buffer[256];

   sprintf(buffer,"Resgrep - Types from [%ld] %s",getpri(),getname());
   if( theResType.display(mp,menu,readwrite,"i",6,12,NewXPos(),NewYPos(),
			  buffer+21,buffer)==0 )
      // Koordinaten sind wirklich gebraucht worden!
      CoordsUsed();

}

node *ResFile::check(struct IntuiMessage *msg)
{
   node *n;

   // Fehler abfangen.
   if( msg->IDCMPWindow==NULL )
      return NULL;
   // Ist das aktuelle Fenster (also das File-Fenster (Inhalt: verschiedene
   // Typen)) gemeint?
   n=theResType.checkdis(msg);
   if( msg->IDCMPWindow==NULL )  // Das Fenster war gemeint!
   {
      if( n!=NULL )
      {
	 ((ResType *)n)->
	    displaywin(theResType.getmp(),theResType.getmenu());
	 return NULL;
      }
      else
	 return NULL;
   }
   for(node *no=theResType.getfirst(); no->getsucc(); no=no->getsucc())
   {
      n=((ResType *)no)->check(msg);
      if( msg->IDCMPWindow==NULL )
	 return n;
   }
   return NULL;
}


