/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1993 by Amit Fridman
 *
 *	Name .....: IO.c
 *	Created ..: Tuesday 14-Dec-93 11:42:13
 *	Revision .: 5
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	13-May-94   Amit Fridman           v1.05 Preliminary release
 *	15-Apr-94   Amit Fridman           v1.04 Beta version complete
 *	24-Mar-94   Amit Fridman           v1.03 Added Keys
 *	24-Feb-94   Amit Fridman           v1.02
 *	07-Jan-94   Amit Fridman           v1.01
 *	14-Dec-93   Amit Fridman           Created this file!
 *
 * $Revision Header ********************************************************/
#define REVISION	5

/* IO operations */

#include "FBase.h"
#include "Protos.h"

#define ID_FBASE1  MAKE_ID('F','B','S','1')
#define ID_KEYS    MAKE_ID('K','E','Y','S')
#define ID_FLOPPY  MAKE_ID('F','L','P','Y')
#define ID_RECORD  MAKE_ID('R','C','R','D')

static BOOL    GetSaveName(void);
static BOOL    GetLoadName(void);
static RECORD *LinkRecord(FLOPPY *, ULONG);
static BOOL    ChunkIs(ULONG, ULONG);
static void    PrintRecord(FILE *fd,RECORD *Record);
static void    PrintRecordFull(FILE *fd,RECORD *Record);

extern char        *CY_Labels[];
extern Object      *App;
extern Object      *WI_Main;
extern Object      *LV_Floppies,*LV_Files;
extern Object      *ST_Match;
extern Object      *CM_Keys,*CM_KeyAnd;
extern struct List  FloppyList;
extern FLOPPY      *MainFloppy;
extern RECORD      *MainRecord;
extern BOOL         DataChanged;
extern LONGBITS     CurKeyMask;

static struct IFFHandle *Iff;
static UBYTE             SaveName[108];
static UBYTE             LoadName[108];


void InitDiskNames()
{
   struct DosList *dl;
   char           *ptr,name[32][32];
   int             i,devs,j,sys;

   dl=LockDosList(LDF_DEVICES|LDF_READ);
   for (i=0,devs=0; i<32; i++) {
      if (dl=NextDosEntry(dl,LDF_DEVICES)) {
         ptr=BADDR(dl->dol_Name);
         strncpy(name[devs],&ptr[1],ptr[0]);
         name[devs][ptr[0]]=0;
         strcat(name[devs],":");
         devs++;
      } else
         break;
   }
   UnLockDosList(LDF_DEVICES|LDF_READ);

   for (j=0,sys=0; j<devs; j++) {
      if (IsFileSystem(name[j])) {
         CY_Labels[sys]=AllocMem(strlen(name[j])+1,MEMF_CLEAR);
         if (!CY_Labels[sys]) break;
         strcpy(CY_Labels[sys],name[j]);
         sys++;
      }
   }
   tqsort(CY_Labels,sys);
   while (sys<32)
      CY_Labels[sys++]=NULL;
}

void SetSaveName()
{
   SaveName[0]='\0';
   LoadName[0]='\0';
}

void SaveKeys()
{
   STRPTR *Key;
   WORD    KeyNum;
   UBYTE   LocalKey[MAXKEYLEN];

   if (!PushChunk(Iff,NULL,ID_KEYS,IFFSIZE_UNKNOWN)) {
      InitKeyScan();
      while (Key=GetNextKey(&KeyNum)) {
         strcpy(LocalKey,*Key);
         WriteChunkBytes(Iff,&KeyNum,sizeof(KeyNum));
         WriteChunkBytes(Iff,LocalKey,MAXKEYLEN);
      }
      PopChunk(Iff);
   }
}

void SaveAllFloppies(struct List *FList)
{
   struct Node *Floppy;

   set(WI_Main,MUIA_Window_Sleep,TRUE);
   Floppy=FList->lh_Head;
   while (Floppy->ln_Succ) {
      SaveFloppy((FLOPPY *)Floppy);
      Floppy=Floppy->ln_Succ;
   }
   set(WI_Main,MUIA_Window_Sleep,FALSE);
}

void SaveFloppy(FLOPPY *Floppy)
{
   struct Node *RList;

   if (!PushChunk(Iff,ID_FLOPPY,ID_FORM,IFFSIZE_UNKNOWN)) {
      RList=Floppy->Records.lh_Head;
      while (RList->ln_Succ) {
         SaveRecord((RECORD *)RList);
         RList=RList->ln_Succ;
      }
      PopChunk(Iff);
   }
}

void SaveRecord(RECORD *Record)
{
   char  Empty[] = { 0,0,0,0 };
   short len;

   if (!PushChunk(Iff,NULL,ID_RECORD,IFFSIZE_UNKNOWN)) {
      WriteChunkBytes(Iff,&Record->Flags,sizeof(Record->Flags));
      WriteChunkBytes(Iff,&Record->Size,sizeof(Record->Size));
      WriteChunkBytes(Iff,&Record->Depth,sizeof(Record->Depth));
      WriteChunkBytes(Iff,&Record->Unique,sizeof(Record->Unique));
      WriteChunkBytes(Iff,&Record->Keys,sizeof(Record->Keys));
      if (Record->Next)
         WriteChunkBytes(Iff,&Record->Next->Unique,sizeof(Record->Unique));
      else
         WriteChunkBytes(Iff,Empty,sizeof(Record->Unique));
      if (Record->Parent)
         WriteChunkBytes(Iff,&Record->Parent->Unique,sizeof(Record->Unique));
      else
         WriteChunkBytes(Iff,Empty,sizeof(Record->Unique));
      if (Record->Sub)
         WriteChunkBytes(Iff,&Record->Sub->Unique,sizeof(Record->Unique));
      else
         WriteChunkBytes(Iff,Empty,sizeof(Record->Unique));
      if (Record->FName) {
         len=strlen(Record->FName)+1;
         WriteChunkBytes(Iff,&len,sizeof(len));
         WriteChunkBytes(Iff,Record->FName,len);
      } else {
         len=0;
         WriteChunkBytes(Iff,&len,sizeof(len));
      }
      if (Record->FRem) {
         len=strlen(Record->FRem)+1;
         WriteChunkBytes(Iff,&len,sizeof(len));
         WriteChunkBytes(Iff,Record->FRem,len);
      } else {
         len=0;
         WriteChunkBytes(Iff,&len,sizeof(len));
      }
      PopChunk(Iff);
   }
}

void DoSave()
{
   if (SaveName[0]==0)
      if (!GetSaveName())
         return;

   if (Iff=AllocIFF()) {
      if (Iff->iff_Stream=(ULONG)Open(SaveName,MODE_NEWFILE)) {
         InitIFFasDOS(Iff);
         if (!OpenIFF(Iff,IFFF_WRITE)) {
            if (!PushChunk(Iff,ID_FBASE1,ID_FORM,IFFSIZE_UNKNOWN)) {
               SaveKeys();
               SaveAllFloppies(&FloppyList);
               DataChanged=FALSE;
            }
            CloseIFF(Iff);
         }
         Close((BPTR)Iff->iff_Stream);
      }
      FreeIFF(Iff);
   }
}

void DoSaveAs()
{
   if (!GetSaveName()) return;
   DoSave();
}

static BOOL GetSaveName()
{
   APTR fr;
   BPTR File;
   LONG Response;

   fr=MUI_AllocAslRequestTags(ASL_FileRequest,
                              ASL_Hail,(ULONG)"Select Save File",
                              ASL_File,(ULONG)SaveName,
                              ASL_FuncFlags,FILF_SAVE,
                              TAG_END);
   if (MUI_AslRequest(fr,NULL)) {
      strcpy(SaveName,((struct FileRequester *)fr)->rf_Dir);
      AddPart(SaveName,((struct FileRequester *)fr)->rf_File,sizeof(SaveName));
      MUI_FreeAslRequest(fr);
      File=Open(SaveName,MODE_OLDFILE);
      if (File) {
         Close(File);
         Response=MUI_Request(App,NULL,0,NULL,"Yes|*No","Overwrite File?");
         if (Response==0)
            return(FALSE);
      }
      return(TRUE);
   }
   MUI_FreeAslRequest(fr);
   return(FALSE);
}


void LoadKeys()
{
   LONG  rlen;
   WORD  KeyNum;
   UBYTE LocalKey[MAXKEYLEN];

   FOREVER {
      rlen=ReadChunkBytes(Iff,&KeyNum,sizeof(KeyNum));
      if (rlen<=0) break;
      ReadChunkBytes(Iff,LocalKey,MAXKEYLEN);
      AddKeyInPos(KeyNum,LocalKey);
   }
}

BOOL LoadAllFloppies(struct List *FList)
{
   FLOPPY *Floppy;
   RECORD *Record;
   STRPTR  String;
   LONG    error;
   short   len;
   BOOL    Parse = FALSE;

   FOREVER {
      if (Parse) {
         error=ParseIFF(Iff,IFFPARSE_SCAN);
         if (error==IFFERR_EOC) continue;
         if (error) break;
      } else
         Parse=TRUE;
      if (ChunkIs(ID_FLOPPY,ID_FORM)) {
         Floppy=AllocMem(sizeof(FLOPPY),MEMF_CLEAR);
         if (!Floppy) return(FALSE);
         NewList(&Floppy->Records);
         AddTail(FList,(struct Node *)Floppy);
         FOREVER {
            error=ParseIFF(Iff,IFFPARSE_RAWSTEP);
            if (error==IFFERR_EOC) continue;
            if (error) break;
            if (ChunkIs(NULL,ID_RECORD)) {
               Record=AllocMem(sizeof(RECORD),MEMF_CLEAR);
               if (!Record) return(FALSE);
               ReadChunkBytes(Iff,&Record->Flags,sizeof(Record->Flags));
               ReadChunkBytes(Iff,&Record->Size,sizeof(Record->Size));
               ReadChunkBytes(Iff,&Record->Depth,sizeof(Record->Depth));
               ReadChunkBytes(Iff,&Record->Unique,sizeof(Record->Unique));
               ReadChunkBytes(Iff,&Record->Keys,sizeof(Record->Keys));
               ReadChunkBytes(Iff,&Record->Next,sizeof(Record->Next));
               ReadChunkBytes(Iff,&Record->Parent,sizeof(Record->Parent));
               ReadChunkBytes(Iff,&Record->Sub,sizeof(Record->Sub));
               ReadChunkBytes(Iff,&len,sizeof(len));
               if (len) {
                  String=AllocMem(len,MEMF_CLEAR);
                  if (!String) return(FALSE);
                  ReadChunkBytes(Iff,String,len);
                  Record->FName=String;
               }
               ReadChunkBytes(Iff,&len,sizeof(len));
               if (len) {
                  String=AllocMem(len,MEMF_CLEAR);
                  if (!String) return(FALSE);
                  ReadChunkBytes(Iff,String,len);
                  Record->FRem=String;
               }
               if (!Floppy->HeadRecord)
                  Floppy->HeadRecord=Record;
               AddTail((struct List *)&Floppy->Records,(struct Node *)Record);
            } else if (ChunkIs(ID_FLOPPY,ID_FORM)) {
               Record=(RECORD *)Floppy->Records.lh_Head;
               while (((struct Node *)Record)->ln_Succ) {
                  Record->Next=LinkRecord(Floppy,(ULONG)Record->Next);
                  Record->Parent=LinkRecord(Floppy,(ULONG)Record->Parent);
                  Record->Sub=LinkRecord(Floppy,(ULONG)Record->Sub);
                  Record=(RECORD *)(((struct Node *)Record)->ln_Succ);
               }
               Floppy=AllocMem(sizeof(FLOPPY),MEMF_CLEAR);
               if (!Floppy) return(FALSE);
               NewList(&Floppy->Records);
               AddTail(FList,(struct Node *)Floppy);
            }
         }
         Record=(RECORD *)Floppy->Records.lh_Head;
         while (((struct Node *)Record)->ln_Succ) {
            Record->Next=LinkRecord(Floppy,(ULONG)Record->Next);
            Record->Parent=LinkRecord(Floppy,(ULONG)Record->Parent);
            Record->Sub=LinkRecord(Floppy,(ULONG)Record->Sub);
            Record=(RECORD *)(((struct Node *)Record)->ln_Succ);
         }
      }
   }
   return(TRUE);
}

static BOOL GetLoadName()
{
   APTR fr;

   fr=MUI_AllocAslRequestTags(ASL_FileRequest,
                              ASL_Hail,(ULONG)"Select Load File",
                              ASL_File,(ULONG)LoadName,
                              TAG_END);
   if (MUI_AslRequest(fr,NULL)) {
      strcpy(LoadName,((struct FileRequester *)fr)->rf_Dir);
      AddPart(LoadName,((struct FileRequester *)fr)->rf_File,sizeof(LoadName));
      MUI_FreeAslRequest(fr);
      return(TRUE);
   }
   MUI_FreeAslRequest(fr);
   return(FALSE);
}

void DoLoad()
{
   LONG Response;
   LONG error;

   if ((DataChanged) && (FloppyList.lh_Head->ln_Succ)) {
      Response=MUI_Request(App,NULL,0,NULL,"Yes|*No","Lose Changes?");
      if (!Response)
         return;
   }

   if (!GetLoadName())
      return;

   set(WI_Main,MUIA_Window_Sleep,TRUE);
   if (Iff=AllocIFF()) {
      if (Iff->iff_Stream=(ULONG)Open(LoadName,MODE_OLDFILE)) {
         InitIFFasDOS(Iff);
         if (!OpenIFF(Iff,IFFF_READ)) {
            StopChunk(Iff,ID_FBASE1,ID_FORM);
            StopChunk(Iff,ID_FBASE1,ID_KEYS);
            StopChunk(Iff,ID_FLOPPY,ID_FORM);
            StopChunk(Iff,ID_FLOPPY,ID_RECORD);
            if (ParseIFF(Iff,IFFPARSE_SCAN)==0) {
               FOREVER {
                  error=ParseIFF(Iff,IFFPARSE_SCAN);
                  if (error==IFFERR_EOC) continue;
                  if (error) break;
                  if (ChunkIs(NULL,ID_KEYS)) {
                     FreeKeys();
                     LoadKeys();
                  } else if (ChunkIs(ID_FLOPPY,ID_FORM)) {
                     FreeAllFloppies(&FloppyList);
                     if (!LoadAllFloppies(&FloppyList))
                        FreeAllFloppies(&FloppyList);
                     CreateList(LV_Floppies,&FloppyList);
                  }
               }
               strcpy(SaveName,LoadName);
               DataChanged=FALSE;
            } else
               MUI_Request(App,NULL,0,NULL,"OK","Wrong File Format!");
            CloseIFF(Iff);
         }
         Close((BPTR)Iff->iff_Stream);
      }
      FreeIFF(Iff);
   }
   set(WI_Main,MUIA_Window_Sleep,FALSE);
}

static RECORD *LinkRecord(FLOPPY *Floppy,ULONG Unique)
{
   RECORD *Record;

   if (Unique==0) return(NULL);
   Record=(RECORD *)Floppy->Records.lh_Head;
   while (((struct Node *)Record)->ln_Succ) {
      if (Record->Unique==Unique) return(Record);
      Record=(RECORD *)(((struct Node *)Record)->ln_Succ);
   }
   return(NULL); /* oh oh */
}

static BOOL ChunkIs(ULONG Type,ULONG ID)
{
   struct ContextNode *cn;

   if (!(cn=CurrentChunk(Iff)))
      return(FALSE);
   if (ID!=cn->cn_ID)
      return(FALSE);
   if ((Type) && (Type!=cn->cn_Type))
      return(FALSE);
   return(TRUE);
}

void DoPrintAll()
{
   FILE        *fd;
   struct Node *FNode,*RNode;
   FLOPPY      *Floppy;
   RECORD      *Record;

   fd=fopen("PRT:","w");
   if (!fd) return;
   set(WI_Main,MUIA_Window_Sleep,TRUE);
   fprintf(fd,"\n\33[1mContents of Database:\33[22m\n\n");
   FNode=FloppyList.lh_Head;
   while (FNode->ln_Succ) {
      Floppy=(FLOPPY *)FNode;
      RNode=Floppy->Records.lh_Head;
      while (RNode->ln_Succ) {
         Record=(RECORD *)RNode;
         PrintRecord(fd,Record);
         RNode=RNode->ln_Succ;
      }
      fprintf(fd,"\n");
      FNode=FNode->ln_Succ;
   }
   fclose(fd);
   set(WI_Main,MUIA_Window_Sleep,FALSE);
}

void DoPrintTitles()
{
   FILE        *fd;
   struct Node *Node;
   RECORD      *Record;

   fd=fopen("PRT:","w");
   if (!fd) return;
   set(WI_Main,MUIA_Window_Sleep,TRUE);
   fprintf(fd,"\n\33[1mFloppies in Database:\33[22m\n\n");
   Node=FloppyList.lh_Head;
   while (Node->ln_Succ) {
      Record=((FLOPPY *)Node)->HeadRecord;
      fprintf(fd,"%s:\n",Record->FName);
      Node=Node->ln_Succ;
   }
   fclose(fd);
   set(WI_Main,MUIA_Window_Sleep,FALSE);
}

void DoPrintContents()
{
   FILE        *fd;
   struct Node *Node;
   RECORD      *Record;

   fd=fopen("PRT:","w");
   if (!fd) return;
   set(WI_Main,MUIA_Window_Sleep,TRUE);
   fprintf(fd,"\n\33[1mContents of Floppy:\33[22m\n\n");
   Node=MainFloppy->Records.lh_Head;
   while (Node->ln_Succ) {
      Record=(RECORD *)Node;
      PrintRecord(fd,Record);
      Node=Node->ln_Succ;
   }
   fclose(fd);
   set(WI_Main,MUIA_Window_Sleep,FALSE);
}

void DoPrintMatch()
{
   FILE   *fd;
   RECORD *Record;
   STRPTR  MatchString;
   LONG    MatchKeysFlag,AndKeysFlag;
   STRPTR *Key;
   WORD    Dummy;
   BOOL    FirstKey = TRUE;
   int     i;

   fd=fopen("PRT:","w");
   if (!fd) return;
   set(WI_Main,MUIA_Window_Sleep,TRUE);
   get(ST_Match,MUIA_String_Contents,&MatchString);
   fprintf(fd,"\n\33[1mFiles Matching <%s>\33[22m\n",MatchString);
	get(CM_Keys,MUIA_Selected,&MatchKeysFlag);
	get(CM_KeyAnd,MUIA_Selected,&AndKeysFlag);
	if (MatchKeysFlag) {
	   if (AndKeysFlag)
   	   fprintf(fd,"\33[1mWith Flags (AND) : ");
   	else
   	   fprintf(fd,"\33[1mWith Flags : ");
      InitKeyScan();
      while (Key=GetNextKey(&Dummy))
         if (CurKeyMask & (1<<KeyNumber(*Key)))
            if (FirstKey) {
               fprintf(fd,"%s",*Key);
               FirstKey=FALSE;
            } else
               fprintf(fd,",%s",*Key);
      fprintf(fd,"\33[22m\n\n");
   } else
      fprintf(fd,"\n");
   for (i=0; ; i++) {
      DoMethod(LV_Files,MUIM_List_GetEntry,i,&Record);
      if (!Record) break;
      PrintRecordFull(fd,Record);
   }
   fclose(fd);
   set(WI_Main,MUIA_Window_Sleep,FALSE);
}

void DoPrintFInfo()
{
   FILE *fd;
   fd=fopen("PRT:","w");
   if (!fd) return;
   PrintRecordFull(fd,MainRecord);
   fclose(fd);
}

static void PrintRecord(FILE *fd,RECORD *Record)
{
   static char tx[108],len[16],buf[128];
   static char space[]="                    ";

   strncpy(tx,space,Record->Depth);
   tx[Record->Depth]=0;
   if (Record->Flags & RECORD_DIRECTORY) {
      if (Record->Flags & RECORD_ROOT) {
         sprintf(buf,"\33[1m%s:",Record->FName);
         strcpy(len,"[root]\33[22m");
      } else {
         sprintf(buf,"\33[1m%s",Record->FName);
         strcpy(len,"[dir]\33[22m");
      }
   } else {
      sprintf(buf,"%s",Record->FName);
      sprintf(len,"(%ld)",Record->Size);
   }
   strcat(tx,buf);
   fprintf(fd,"%s %s\n",tx,len);
}

static void PrintRecordFull(FILE *fd,RECORD *Record)
{
   static char  tx[108];
   STRPTR      *Key;
   WORD         Dummy;
   BOOL         FirstKey = TRUE;

   BuildName(Record,tx);
   if (Record->Flags & RECORD_DIRECTORY)
      fprintf(fd,"FILE    %s\n",tx);
   else
      fprintf(fd,"FILE    %s (%ld)\n",tx,Record->Size);
   if (Record->FRem)
      fprintf(fd," REMARK %s\n",Record->FRem);
   fprintf(fd," KEYS   ");
   InitKeyScan();
   while (Key=GetNextKey(&Dummy))
      if (Record->Keys & (1<<KeyNumber(*Key)))
         if (FirstKey) {
            fprintf(fd,"%s",*Key);
            FirstKey=FALSE;
         } else
            fprintf(fd,",%s",*Key);
   fprintf(fd,"\n");
}
