/************************************************************************/
/*									*/
/*  Copyright (C) 1994  Christian Stieber				*/
/*									*/
/* This program is free software; you can redistribute it and/or modify	*/
/* it under the terms of the GNU General Public License as published by	*/
/* the Free Software Foundation; either version 2 of the License, or	*/
/* (at your option) any later version.					*/
/*									*/
/* This program is distributed in the hope that it will be useful,	*/
/* but WITHOUT ANY WARRANTY; without even the implied warranty of	*/
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the	*/
/* GNU General Public License for more details.				*/
/*									*/
/* You should have received a copy of the GNU General Public License	*/
/* along with this program; if not, write to the Free Software		*/
/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.		*/
/*									*/
/************************************************************************/
/*									*/
/* Author address:							*/
/*   Christian Stieber							*/
/*   Konradstraße 41							*/
/*   D-85055 Ingolstadt							*/
/*   (Germany)								*/
/*   Phone: 0841-59896							*/
/*									*/
/************************************************************************/

#ifndef V39
   #define CreatePool LibCreatePool
   #define DeletePool LibDeletePool
   #define AllocPooled LibAllocPooled
   #define FreePooled LibFreePooled
#endif

#ifndef DOS_DOSEXTENS_H
#include <dos/dosextens.h>
#endif

#ifndef DOS_EXALL_H
#include <dos/exall.h>
#endif

#ifndef INTUITION_INTUITION_H
#include <intuition/intuition.h>
#endif

#ifndef LIBRARIES_AMIGAGUIDE_H
#include <libraries/amigaguide.h>
#endif

#ifndef WORKBENCH_STARTUP_H
#include <workbench/startup.h>
#endif

#ifndef WORKBENCH_WORKBENCH_H
#include <workbench/workbench.h>
#endif

#ifndef __GNUC__
   #ifndef CLIB_EXEC_PROTOS_H
   #include <clib/exec_protos.h>
   #endif

   #ifndef CLIB_DOS_PROTOS_H
   #include <clib/dos_protos.h>
   #endif

   #ifndef CLIB_UTILITY_PROTOS_H
   #include <clib/utility_protos.h>
   #endif

   #ifndef CLIB_INTUITION_PROTOS_H
   #include <clib/intuition_protos.h>
   #endif

   #ifndef CLIB_ICON_PROTOS_H
   #include <clib/icon_protos.h>
   #endif

   #ifndef CLIB_AMIGAGUIDE_PROTOS_H
   #include <clib/amigaguide_protos.h>
   #endif

   #include <pragmas/exec_pragmas.h>
   #include <pragmas/dos_pragmas.h>
   #include <pragmas/intuition_pragmas.h>
   #include <pragmas/utility_pragmas.h>
   #include <pragmas/icon_pragmas.h>
   #include <pragmas/amigaguide_pragmas.h>
#endif

#include <string.h>

#include "Globals.h"

/************************************************************************/

#ifdef __GNUC__

#include "Inlines.h"

#ifndef V39
   APTR AllocPooled(APTR, ULONG);
   void FreePooled(APTR, APTR, ULONG);
   APTR CreatePool(ULONG, ULONG, ULONG);
   void DeletePool(APTR);
#endif

#endif   /* __GNUC__ */

/************************************************************************/

#define MESSAGE_NAME	".Message"
#define CONFIG_NAME     ".Config"

/************************************************************************/

static char AboutText[]=
   "\n"
   "  This is the manual browser " PROGVERSION " (" PROGDATE "), " CPU " version\n"
#ifdef V39
   "  This executable was compiled for AmigaOS 3.0 (V39) and up.\n"
#endif
   "  Copyright © 1994 Christian Stieber\n"
   "\n"
   "\n"
   "  This program is free software; you can redistribute it and/or modify\n"
   "  it under the terms of the @{\x22GNU General Public License\x22 LINK COPYING/Main} as published by\n"
   "  the Free Software Foundation; either version 2 of the License, or\n"
   "  (at your option) any later version.\n"
   "\n"
   "  This program is distributed in the hope that it will be useful,\n"
   "  but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
   "  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
   "  @{\x22GNU General Public License\x22 LINK COPYING/Main} for more details.\n"
   "\n"
   "  You should have received a copy of the @{\x22GNU General Public License\x22 LINK COPYING/Main}\n"
   "  along with this program; if not, write to the Free Software\n"
   "  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
   "\n"
   "\n"
   "  Author: Christian Stieber\n"
   "          Konradstraße 41\n"
   "          D-85055 Ingolstadt\n"
   "          (Germany)\n"
   "\n"
   "  Currently (1994) I'm a student and may reached at the following address:\n"
   "\n"
   "          Christian Stieber\n"
   "          Seitzstraße 6, Zi. 306\n"
   "          D-80538 München\n"
   "          (Germany)\n"
   "\n"
   "          EMail: stieber@informatik.tu-muenchen.de\n"
   "\n";

/************************************************************************/

struct DirNode
   {
      struct DirNode *Next;
      struct ActionNode *ActionNode;
      char Name[1];
   };

/************************************************************************/

static struct DirNode *DirectoryList;
static struct DirNode *FileList;
static char *NodeData;
static ULONG NodeLength;

static char *MessageText;

#ifdef __GNUC__
   static struct FileInfoBlock FileInfoBlock __attribute__((aligned(4)));
#else
   static struct FileInfoBlock __aligned FileInfoBlock;
#endif

static struct ActionNode *LocalActionList;

/************************************************************************/

static void InsertDirNode(struct DirNode *DirNode, struct DirNode **DirList)

{
   if (!*DirList)
      {
         *DirList=DirNode;
         DirNode->Next=NULL;
      }
   else
      {
         struct DirNode *Prev, *Current;

         Prev=(struct DirNode *)DirList;
         Current=Prev->Next;
         while (Current && (Stricmp(Current->Name,DirNode->Name)<0))
            {
               Prev=Current;
               Current=Current->Next;
            }
         Prev->Next=DirNode;
         DirNode->Next=Current;
      }
}

/************************************************************************/

static void FreeDirNodes(struct DirNode **DirList)

{
   while (*DirList)
      {
         struct DirNode *Next;
         Next=(*DirList)->Next;
         Free(*DirList);
         *DirList=Next;
      }
}

/************************************************************************/
/*									*/
/* Look for an action node matching a given filename.			*/
/* Checks the local list first, followed by the global list.		*/
/* Returns NULL for "no action assigned"				*/
/*									*/
/************************************************************************/

static struct ActionNode *FindActionNode(char *Filename)

{
   struct ActionNode *ActionNode;

   for (ActionNode=LocalActionList; ActionNode; ActionNode=ActionNode->Next)
      {
         if (MatchPatternNoCase(ActionNode->Pattern,Filename))
            {
               return ActionNode;
            }
      }

   for (ActionNode=ActionList; ActionNode; ActionNode=ActionNode->Next)
      {
         if (MatchPatternNoCase(ActionNode->Pattern,Filename))
            {
               return ActionNode;
            }
      }

   return NULL;
}

/************************************************************************/
/*									*/
/* Free the local action list						*/
/*									*/
/************************************************************************/

static void FreeLocalActionList(void)

{
   while (LocalActionList)
      {
         struct ActionNode *ThisNode;

         ThisNode=LocalActionList;
         LocalActionList=ThisNode->Next;
         Free(ThisNode);
      }
}

/************************************************************************/
/*									*/
/* Read the local action list for a directory, if available.		*/
/*									*/
/************************************************************************/

static void ReadLocalConfig(BPTR DirLock)

{
   BPTR OldCurrDir;
   BPTR FileHandle;

   OldCurrDir=CurrentDir(DirLock);
   if (FileHandle=Open(CONFIG_NAME,MODE_OLDFILE))
      {
         int Result;
         Result=ReadConfigFile(FileHandle,&LocalActionList);
         Close(FileHandle);
         if (!Result)
            {
               FreeLocalActionList();
            }
      }
   CurrentDir(OldCurrDir);
}

/************************************************************************/
/*									*/
/*									*/
/************************************************************************/

#define BUFFER_SIZE (4*1024)

static long ReadDir(BPTR DirLock)

{
   struct ExAllControl *ExAllControl;
   long DosError;

   DosError=0;
   if ((ExAllControl=AllocDosObject(DOS_EXALLCONTROL,NULL)))
      {
         struct ExAllData *Buffer;
         if ((Buffer=Malloc(BUFFER_SIZE)))
            {
               int more;
               ReadLocalConfig(DirLock);
               ExAllControl->eac_LastKey=0;
               ExAllControl->eac_MatchString=NULL;
               ExAllControl->eac_MatchFunc=NULL;
               do
                  {
                     more=ExAll(DirLock,Buffer,BUFFER_SIZE,ED_TYPE,ExAllControl);
                     if ((!more) && (IoErr()!=ERROR_NO_MORE_ENTRIES))
                        {
                           DosError=IoErr();
                        }
                     else
                        {
                           if (ExAllControl->eac_Entries)
                              {
                                 struct ExAllData *ExAllData;
                                 ExAllData=Buffer;
                                 do
                                    {
                                       if (Stricmp(ExAllData->ed_Name,MESSAGE_NAME) || Stricmp(ExAllData->ed_Name,CONFIG_NAME))
                                          {
                                             struct ActionNode *ActionNode;
                                             ActionNode=FindActionNode(ExAllData->ed_Name);
                                             if (!ActionNode || Stricmp(ActionNode->Action,"IGNORE"))
                                                {
                                                   struct DirNode *DirNode;
                                                   if (ActionNode && !Stricmp(ActionNode->Action,"DEFAULT"))
                                                      {
                                                         ActionNode=NULL;
                                                      }
                                                   if ((DirNode=Malloc(sizeof(struct DirNode)+strlen(ExAllData->ed_Name))))
                                                      {
                                                         DirNode->ActionNode=ActionNode;
                                                         strcpy(DirNode->Name,ExAllData->ed_Name);
                                                         if (ExAllData->ed_Type==ST_SOFTLINK)
                                                            {
                                                               BPTR LinkLock;
                                                               BPTR OldCurrDir;
                                                               OldCurrDir=CurrentDir(DirLock);
                                                               if ((LinkLock=Lock(ExAllData->ed_Name,SHARED_LOCK)))
                                                                  {
                                                                     if (Examine(LinkLock,&FileInfoBlock))
                                                                        {
                                                                           InsertDirNode(DirNode,FileInfoBlock.fib_DirEntryType>=0 ? &DirectoryList : &FileList);
                                                                        }
                                                                     UnLock(LinkLock);
                                                                  }
                                                               CurrentDir(OldCurrDir);
                                                            }
                                                         else
                                                            {
                                                               InsertDirNode(DirNode,ExAllData->ed_Type>=0 ? &DirectoryList : &FileList);
                                                            }
                                                      }
                                                   else
                                                      {
                                                         DosError=IoErr();
                                                      }
                                                }
                                          }
                                       ExAllData=ExAllData->ed_Next;
                                    }
                                 while (!DosError && ExAllData);
                              }
                        }
                  }
               while (!DosError && more);
               if (more)
                  {
#ifdef V39
                     ExAllEnd(DirLock,Buffer,BUFFER_SIZE,ED_TYPE,ExAllControl);
#else
                     while (ExAll(DirLock,Buffer,BUFFER_SIZE,ED_TYPE,ExAllControl)) ;
#endif
                  }
               if (DosError)
                  {
                     FreeDirNodes(&DirectoryList);
                     FreeDirNodes(&FileList);
                  }
               Free(Buffer);
               FreeLocalActionList();
            }
         else
            {
               DosError=IoErr();
            }
         FreeDosObject(DOS_EXALLCONTROL,ExAllControl);
      }
   else
      {
         DosError=IoErr();
      }
   return DosError;
}

#undef BUFFER_SIZE

/***********************************************/

static char *NodeDataTmp;

static int PrintNodeText(char *Text)

{
   if (NodeDataTmp)
      {
         NodeDataTmp=Stpcpy(NodeDataTmp,Text);
      }
   return strlen(Text);
}

/***********************************************/

static int PrintDir(char *ThisDir, char *PathName)

{
   int Length;
   struct DirNode *Current;
   short ColWidth, Columns;
   short MaxLength;
   short Column;

   /* Max. length of the buttons */
   MaxLength=0;
   for (Current=DirectoryList; Current; Current=Current->Next)
      {
         Length=strlen(Current->Name)+1;
         if (Length>MaxLength) MaxLength=Length;
      }
   for (Current=FileList; Current; Current=Current->Next)
      {
         Length=strlen(Current->Name);
         if (Length>MaxLength) MaxLength=Length;
      }
   if (AmigaGuideBase->lib_Version>=39) MaxLength++;

   Columns=LineLength/(MaxLength+1);
   ColWidth=LineLength/Columns;
   if (AmigaGuideBase->lib_Version>=39) ColWidth--;

   Length=0;

   Column=0;
   for (Current=DirectoryList; Current; Current=Current->Next)
      {
         int CurWidth;
         CurWidth=0;
         Length+=PrintNodeText("@{\x22");
         CurWidth+=PrintNodeText(Current->Name);
         CurWidth+=PrintNodeText("/");
         Length+=PrintNodeText("\x22 LINK \x22");
         if (*ThisDir)
            {
               Length+=PrintNodeText(ThisDir);
               Length+=PrintNodeText("/");
            }
         Length+=PrintNodeText(Current->Name);
         Length+=PrintNodeText("\x22}");
         Column++;
         if (Column>=Columns)
            {
               Length+=PrintNodeText("\n");
               Column=0;
            }
         else
            {
               while (CurWidth<ColWidth) CurWidth+=PrintNodeText(" ");
            }
         Length+=CurWidth;
      }
   if (Column)
      {
         Length+=PrintNodeText("\n");
      }

   if (DirectoryList && FileList)
      {
         Length+=PrintNodeText("\n\n");
      }

   Column=0;
   for (Current=FileList; Current; Current=Current->Next)
      {
         int CurWidth;
         CurWidth=0;
         Length+=PrintNodeText("@{\x22");
         CurWidth+=PrintNodeText(Current->Name);
         Length+=PrintNodeText("\x22 ");
         if (Current->ActionNode)
            {
               char *t;
               char x[4];

               x[1]='\0';
               for (t=Current->ActionNode->Action; *t; t++)
                  {
                     if (*t=='%')
                        {
                           switch(*(t+1))
                              {
                                 case '%': Length+=PrintNodeText("%");
                                           t++;
                                           break;

                                 case 'p': Length+=PrintNodeText(PathName);
                                           t++;
                                           break;

                                 case 'f': Length+=PrintNodeText(Current->Name);
                                           t++;
                                           break;

                                 case 'F': {
                                              char *u, *v;
                                              v=NULL;
                                              for (u=Current->Name; *u; u++)
                                                 {
                                                    if (*u=='.') v=u;
                                                 }
                                              if (!v) v=u;
                                              for (u=Current->Name; u!=v; u++)
                                                 {
                                                    x[0]=*u;
                                                    Length+=PrintNodeText(x);
                                                 }
                                           }
                                           t++;
                                           break;

                                 default:  x[0]=*t;
                                           Length+=PrintNodeText(x);
                                           break;
                              }
                        }
                     else
                        {
                           x[0]=*t;
                           Length+=PrintNodeText(x);
                        }
                  }
            }
         else
            {
               Length+=PrintNodeText("LINK \x22");
               if (*ThisDir)
                  {
                     Length+=PrintNodeText(ThisDir);
                     Length+=PrintNodeText("/");
                  }
               Length+=PrintNodeText(Current->Name);
               Length+=PrintNodeText("/Main");
               Length+=PrintNodeText("\x22");
            }
         Length+=PrintNodeText("}");
         Column++;
         if (Column>=Columns)
            {
               Length+=PrintNodeText("\n");
               Column=0;
            }
         else
            {
               while (CurWidth<ColWidth) CurWidth+=PrintNodeText(" ");
            }
         Length+=CurWidth;
      }
   if (Column)
      {
         Length+=PrintNodeText("\n");
      }

   return Length;
}

/***********************************************/

static int PrintMessage(void)

{
   if (MessageText)
      {
         int Length;
         Length=PrintNodeText(MessageText);
         Length+=PrintNodeText("\n");
         return Length;
      }
   return 0;
}

/***********************************************/

static void ReadMessage(BPTR DirLock)

{
   BPTR OldCurrDir;
   BPTR FileHandle;

   OldCurrDir=CurrentDir(DirLock);
   if ((FileHandle=Open(MESSAGE_NAME,MODE_OLDFILE)))
      {
         if (ExamineFH(FileHandle,&FileInfoBlock))
            {
               ULONG Length;

               Length=FileInfoBlock.fib_Size+1;
               if (MessageText=Malloc(Length))
                  {
                     Read(FileHandle,MessageText,Length-1);
                     MessageText[Length-1]='\0';
                  }
            }
         Close(FileHandle);
      }
   CurrentDir(OldCurrDir);
}

/***********************************************/

#ifdef __GNUC__
ULONG AmigaGuideHostDispatcher(Msg Message)
#else
ULONG __saveds __asm AmigaGuideHostDispatcher(register __a1 Msg Message)
#endif

{
   switch(Message->MethodID)
      {
         case HM_FINDNODE:  {
                               char *Name;

                               Name=((struct opFindHost *)Message)->ofh_Node;
                               while (!Strnicmp(Name,DatabaseName,strlen(DatabaseName)))
                                  {
                                     Name+=strlen(DatabaseName)+1;
                                  }

                               ((struct opFindHost *)Message)->ofh_Next=((struct opFindHost *)Message)->ofh_Prev=((struct opFindHost *)Message)->ofh_Node;

                               if (!Stricmp("main",Name))
                                  {
                                     ((struct opFindHost *)Message)->ofh_Title="Manual browser " PROGVERSION;
                                  }
                               else if (!strcmp("AbOuT",Name))
                                  {
                                     ((struct opFindHost *)Message)->ofh_Title="About the manual browser";
                                  }
                               else
                                  {
                                     BPTR OldCurrDir;
                                     BPTR TestLock;

                                     OldCurrDir=CurrentDir(ManDir);
                                     if ((TestLock=Lock(Name,SHARED_LOCK)))
                                        {
                                           UnLock(TestLock);
                                        }
                                     CurrentDir(OldCurrDir);
                                     if (!TestLock) return FALSE;
                                     ((struct opFindHost *)Message)->ofh_Title=Name;
                                     ((struct opFindHost *)Message)->ofh_TOC="main";
                                  }
                               return TRUE;
                            }
                            break;

         case HM_OPENNODE:  {
                               BPTR DirLock;
                               BPTR OldCurrDir;
                               char *Name;
                               int Main;
                               long DosError;

                               DosError=0;
                               ((struct opNodeIO *)Message)->onm_Flags=HTNF_CLEAN;
                               ((struct opNodeIO *)Message)->onm_FileName=NULL;
                               Name=((struct opNodeIO *)Message)->onm_Node;
                               while (!Strnicmp(Name,DatabaseName,strlen(DatabaseName)))
                                  {
                                     Name+=strlen(DatabaseName)+1;
                                  }

                               if ((Main=!Stricmp("main",Name)))
                                  {
                                     Name+=4;
                                  }
                               else if (!strcmp("AbOuT",Name))
                                  {
                                     ((struct opNodeIO *)Message)->onm_DocBuffer=AboutText;
                                     ((struct opNodeIO *)Message)->onm_BuffLen=sizeof(AboutText)-1;
                                     return TRUE;
                                  }

                               OldCurrDir=CurrentDir(ManDir);
                               if ((DirLock=Lock(Name,SHARED_LOCK)))
                                  {
                                     char *DirName;
                                     int DirNameLength;

                                     DirNameLength=256;
                                     do
                                        {
                                           if (DirName=Malloc(DirNameLength))
                                              {
                                                 if (NameFromLock(DirLock,DirName,DirNameLength))
                                                    {
                                                       break;
                                                    }
                                                 if (IoErr()!=ERROR_LINE_TOO_LONG)
                                                    {
                                                       DosError=IoErr();
                                                    }
                                                 Free(DirName);
                                                 DirName=NULL;
                                                 DirNameLength+=256;
                                              }
                                           else
                                              {
                                                 DosError=IoErr();
                                              }
                                        }
                                     while (!DosError);
                                     if (!DosError && !(DosError=ReadDir(DirLock)))
                                        {
                                           NodeDataTmp=NULL;
                                           NodeLength=0;
                                           ReadMessage(DirLock);
                                           NodeLength+=PrintMessage();
                                           NodeLength+=PrintDir(Name,DirName);
                                           if (Main)
                                              {
                                                 NodeLength+=PrintNodeText("\n\n"
                                                                           "@{\x22""About the manual browser\x22 LINK \x22""AbOuT\x22}\n");
                                              }

                                           if ((NodeData=Malloc(NodeLength+1)))
                                              {
                                                 NodeDataTmp=NodeData;
                                                 PrintMessage();
                                                 PrintDir(Name,DirName);
                                                 if (Main)
                                                    {
                                                       PrintNodeText("\n\n"
                                                                     "@{\x22""About the manual browser\x22 LINK \x22""AbOuT\x22}\n");
                                                    }
                                                 ((struct opNodeIO *)Message)->onm_DocBuffer=NodeData;
                                                 ((struct opNodeIO *)Message)->onm_BuffLen=NodeLength;
                                                 UnLock(OldCurrDir);
                                                 OldCurrDir=DirLock;
                                              }
                                           else
                                              {
                                                 DosError=IoErr();
                                              }
                                           Free(MessageText);
                                           MessageText=NULL;
                                        }
                                     if (!NodeData) UnLock(DirLock);
                                     if (DirName)
                                        {
                                           Free(DirName);
                                        }
                                  }
                               else
                                  {
                                     DosError=IoErr();
                                  }
                               CurrentDir(OldCurrDir);
                               if (DosError)
                                  {
                                     static char FaultString[84];
                                     ((struct opNodeIO *)Message)->onm_DocBuffer=FaultString;
                                     ((struct opNodeIO *)Message)->onm_BuffLen=Fault(DosError,NULL,FaultString,sizeof(FaultString));
                                  }
                               return TRUE;
                            }
                            break;

         case HM_CLOSENODE: {
                               if (NodeData) Free(NodeData);
                               NodeData=NULL;
                               FreeDirNodes(&DirectoryList);
                               FreeDirNodes(&FileList);
                               return TRUE;
                            }
                            break;
      }
   return FALSE;
}
