/**
 * Scout - The Amiga System Monitor
 *
 *------------------------------------------------------------------
 *
 * 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
 * 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.
 *
 * You must not use this source code to gain profit of any kind!
 *
 *------------------------------------------------------------------
 *
 * @author Andreas Gelhausen
 * @author Richard Körber <rkoerber@gmx.de>
 */



#include "system_headers.h"

static APTR LockPool = NULL;

int lockanzahl, hiddenanzahl;

__asm __saveds LONG locklist_cmppathfunc (register __a1 struct LockEntry *le1, register __a2 struct LockEntry *le2)
{
   LONG cmp;

   cmp = strcmpi (le1->lock_path, le2->lock_path);
   if (cmp == 0) cmp = strcmpi(le1->lock_address, le2->lock_address);

   return cmp;
}

struct Hook locklist_cmppathhook = {
 {NULL, NULL},
 (ULONG (* )())locklist_cmppathfunc,
 NULL, NULL
};

__asm __saveds LONG locklist_dspfunc(register __a2 char **array, register __a1 struct LockEntry *lockentry, register __a0 struct Hook *hook)
{
   if (lockentry) {
      *array++ = lockentry->lock_address;
      *array++ = lockentry->lock_access;
      *array++ = lockentry->lock_path;
      *array   = NULL;
   } else {
      *array++ = ESC "bAddress";
      *array++ = ESC "bAccess";
      *array++ = ESC "bPath";
      *array   = NULL;
   }
   return(0);
}

struct Hook locklist_dsphook = {
 {NULL, NULL},
 (ULONG (* )())locklist_dspfunc,
 NULL, NULL
};

void FreeLocks (void)
{
    MyFreePoolStructs(&LockPool, locktext, locktext2, locklist);
}

APTR GetLocks (int sl_job, char *pattern, struct LockEntry **first) {
   struct   LockEntry      *lockentry,*previous = NULL;
   struct   DosList        *dev;
   unsigned long           lock, next, lock2remove = 0;
   struct   FileLock       *lockptr;
   struct   FileInfoBlock  *infoBlock;
   APTR     LocalPool;

   unsigned char  *dest;
   unsigned char  access[32];

   int   erster, pattern_correct, i = LOCKSUPDATERATE;

   if (first)
      *first = 0;

   lockanzahl = 0;
   hiddenanzahl = 0;
   NoReqOn();

   LocalPool = tbCreatePool(MEMF_CLEAR, 4096, 4096);

   if (dest = tbAllocVecPooled(LocalPool, PATHLENGTH * 2 + 3)) {
      pattern_correct = ((((!pattern) || (*pattern == '\0')) && \
         (ParsePatternNoCase ("#?", dest, PATHLENGTH*2+2) != -1)) || \
         (ParsePatternNoCase (pattern, dest, PATHLENGTH*2+2) != -1));

      if (clientstate) {
         char  tmpbuffer[256];
   
         if (SendDaemon ("GetLockList")) {
            while ((lockentry = tbAllocPooled(LocalPool, sizeof(struct LockEntry))) \
              && (ReceiveDecodedEntry ((UBYTE *) lockentry, sizeof (struct LockEntry) - 4)) \
              && (sgets (client_socket, tmpbuffer, 256))) {
   
               IsHex (lockentry->lock_address, (long *) &lockentry->lock_ptr);
   
               if (MatchPatternNoCase (dest, tmpbuffer)) {
                  if (lockentry->lock_path = tbAllocPooled(LocalPool, strlen (tmpbuffer) + 1)) {
                     strncpy (lockentry->lock_path, tmpbuffer, strlen (tmpbuffer) + 1);
                  }
                  lockanzahl ++;
   
                  if (! *first)
                     *first = lockentry;
                  if (previous)
                     previous->lock_next = lockentry;
   
                  previous = lockentry;
               } else {
                  hiddenanzahl++;
               }
               if (WI_Locks && !--i) {
                  _sprintf (tmpstr, "%ld shown, %ld hidden...", lockanzahl, hiddenanzahl);
                  i = LOCKSUPDATERATE;
                  DoMethod (AP_Scout,MUIM_Application_InputBuffered);
                  set (locktext2,MUIA_Text_Contents,tmpstr);
               }
            }
         }
      } else {
         if ((pattern_correct) && (infoBlock = AllocDosObject(DOS_FIB, TAG_DONE))) {
            dev = DOSLIST;
            while (dev != 0) {
               if (dev->dol_Type == DLT_VOLUME){
                  b2cstrn(dev->dol_Name, tmpstr, FILENAMELENGTH);
                  strcat (tmpstr, ":");
   
                  if (lock = Lock (tmpstr, SHARED_LOCK)) {
                     erster = TRUE; next = 0;
   
                     while ((next && (next != lock)) || (erster)) {
                        if (erster)
                           next = lock;
                        lockptr = (struct FileLock *) BADDR(next);
                        if (!TypeOfMem(lockptr))
                           break;
                        if (lockptr->fl_Volume != MKBADDR(dev))
                           break;
                        if (Examine (next, infoBlock)) {
                           if (sl_job == SL_LIST) {
                              if (erster) {
                                 strcpy(access, "OWN");
                              } else if (lockptr->fl_Access == SHARED_LOCK) {
                                 strcpy(access, "SHARED");
                              } else if (lockptr->fl_Access == EXCLUSIVE_LOCK) {
                                 strcpy(access, "EXCLUSIVE");
                              } else {
                                 _sprintf(access, "invalid (%ld)", lockptr->fl_Access);
                                 //strcpy(access, "----");
                              }
                           }
                           if (lockptr->fl_Access == EXCLUSIVE_LOCK) {
                              LONG oldAccess;

                              Forbid();

                              oldAccess = lockptr->fl_Access;
                              lockptr->fl_Access = SHARED_LOCK;

                              NameFromLock(next, tmpstr, PATHLENGTH);

                              lockptr->fl_Access = oldAccess;

                              Permit();
                           } else {
                              NameFromLock (next, tmpstr, PATHLENGTH);
                           }
                        
                           if (MatchPatternNoCase (dest, tmpstr)) {
                              if ((sl_job == SL_LIST) && (lockentry = tbAllocPooled (LocalPool, sizeof(struct LockEntry)))) {
                                 if (! *first)
                                    *first = lockentry;
                                 if (previous)
                                    previous->lock_next = lockentry;
   
                                 lockentry->lock_ptr = (char *) lockptr;
                                 _sprintf (lockentry->lock_address, "$%08lx", lockptr);
                                 strcpy (lockentry->lock_access, access);
   
                                 if (lockentry->lock_path = tbAllocPooled(LocalPool, strlen (tmpstr) + 1)) {
                                    strcpy (lockentry->lock_path, tmpstr);
                                 }
   
                                 lockanzahl ++;
                                 previous = lockentry;
                              } else if (sl_job == SL_REMOVE) {
                                 lockanzahl ++;
                                 lock2remove = next;
                              } else if (sl_job == SL_COUNT) {
                                 lockanzahl ++;
                              } else {
                                 hiddenanzahl++;
                              }
                           } else {
                              hiddenanzahl++;
                           }
                           if ((WI_Locks) && (sl_job == SL_LIST) && (!(--i))) {
                              _sprintf (tmpstr, "%ld shown, %ld hidden...", lockanzahl, hiddenanzahl);
                              i = LOCKSUPDATERATE;
                              DoMethod (AP_Scout,MUIM_Application_InputBuffered);
                              set (locktext2,MUIA_Text_Contents,tmpstr);
                           }
                        }
                        erster = FALSE;
                        next = lockptr->fl_Link;
                        if ((sl_job == SL_REMOVE) && (lock2remove)) {
                           UnLock (lock2remove);
                           lock2remove = (BPTR)NULL;
                        }
                     }
                     UnLock (lock);
                  }
               }
               dev = (struct DosList *) BADDR(dev->dol_Next);
            }
            FreeDosObject(DOS_FIB, infoBlock);
         }
         NoReqOff();
      }
      tbFreeVecPooled(LocalPool, dest);
   }
   return (LocalPool);
}

void PrintLocks (char *filename) {
   int   i=1;
   BPTR  handle;
   APTR  lockpool = NULL;
   struct LockEntry *entryp = NULL;

   handle = HandlePrintStart (filename);
   if ((handle) && (PrintOneLine (handle, "\n  Address  Access       Path\n\n"))) {
      if (! WI_Locks) {
         lockpool = GetLocks (SL_LIST, "#?", &entryp);
      }
      if (i) {
         for (i=0;;i++) {
            if (WI_Locks)
               DoMethod (locklist,MUIM_List_GetEntry,i,&entryp);
            if (!entryp) break;

            _sprintf (tmpstr2, " %s  %-10s       %s\n", entryp->lock_address, entryp->lock_access, entryp->lock_path);
            if (! (PrintOneLine (handle, tmpstr2)))
               break;

            if (! WI_Locks)
               entryp = entryp->lock_next;
         }
      }
   }
   if (lockpool) tbDeletePool(lockpool);
   HandlePrintStop();
}

void ShowLocks (void) {
   unsigned char        *pattern;
   struct   LockEntry   *lock;

   ApplicationSleep();
   set (locklist,MUIA_List_Quiet,TRUE);
   set (locklist,MUIA_List_CompareHook,locklist_cmphook_ptr);
   set (BT_LockRemove, MUIA_Disabled, TRUE);

   FreeLocks();

   get (lockpattern, MUIA_String_Contents, &pattern);
   LockPool = GetLocks (SL_LIST, pattern, &lock);

   while (lock) {
      InsertSortedEntry (locklist, (APTR *) &lock);
      lock = lock->lock_next;
   }

   AwakeApplication();
   set (locklist, MUIA_List_Quiet, FALSE);
   MySetContents (locktext2, "%ld shown, %ld hidden. Done.", lockanzahl, hiddenanzahl);
   set (WI_Locks,MUIA_Window_ActiveObject,locklist);
}

void SendLockList (void) {
   unsigned char        *pattern;
   struct   LockEntry   *lock;

   FreeLocks();

   get (lockpattern, MUIA_String_Contents, &pattern);
   LockPool = GetLocks (SL_LIST, pattern, &lock);

   while (lock) {
      SendEncodedEntry ((UBYTE *) lock, sizeof (struct LockEntry) - 4);
      if (lock->lock_path && lock->lock_path[0])
         SendClient ((UBYTE *) lock->lock_path);
      else
         SendClient (" ");
      lock = lock->lock_next;
   }
   FreeLocks();
}


APTR WI_Locks, locklist, locktext, lockcount, CY_LockSort;
int  locksortstate = 0;
APTR lockpattern,locktext2;
APTR BT_LockUpdate, BT_LockPrint, BT_LockRemove, BT_LockExit;

APTR LockSortList[] = {
   &list_cmpnormalhook,
   &locklist_cmppathhook,
   &list_cmpaddresshook,
   NULL
};

static const char *CYA_LockSortText[] = {
   "normal",
   "path",
   "address",
   NULL
};

char locks_title[WINDOWTITLELEN];

void LocksWindow (BOOL state) {
   if (state) {
      if (WI_Locks) {
         ShowLocks();
      } else {
         WI_Locks = WindowObject,
         MUIA_Window_Title, MyGetWindowTitle (locks_title, "LOCKS"),
         MUIA_HelpNode, LocksText,
         MUIA_Window_ID, MakeListID('L','O','C','K'),
         WindowContents, VGroup,
            Child, locklist = ListviewObject,
               MUIA_Listview_MultiSelect, TRUE,
               MUIA_Listview_List, ListObject,
               MUIA_List_Format, "COL=0 DELTA=8,COL=1 DELTA=8 P=\33c,COL=2",
               MUIA_List_Title, TRUE,
               MUIA_List_DisplayHook, &locklist_dsphook,
               InputListFrame,
               End,
            MUIA_CycleChain, TRUE,
            End,
            Child, HGroup,
               Child, locktext = MyTextObject5(SPACE40),
               Child, HGroup, MUIA_HorizWeight, 0,
                  Child, MyLabel ("sorted by:"),
                  Child, CY_LockSort = CycleObject,
                     MUIA_Cycle_Entries, CYA_LockSortText,
                     MUIA_Cycle_Active, locksortstate,
                     MUIA_HorizWeight, 0,
                  End,
               End,
            End,
            Child, locktext2 = MyTextObject(),
            Child, HGroup,
               Child, KeyLabel("Pattern:", 'p'),
               Child, lockpattern = StringObject,
                  MUIA_String_Contents, "#?", MUIA_ControlChar, 'p',
                  MUIA_String_MaxLen, TEXTLENGTH, StringFrame,
               End,
            End,
            Child, MyVSpace(2),
            Child, HGroup, MUIA_Group_SameSize, TRUE,
               Child, BT_LockUpdate   = KeyButtonA (UpdateText,ID_LOCKUPDATE),
               Child, BT_LockPrint    = KeyButtonA (PrintText ,ID_LOCKPRINT),
               Child, BT_LockRemove   = KeyButtonA (RemoveText,ID_LOCKREMOVE),
               Child, BT_LockExit     = KeyButtonA (ExitText  ,ID_LOCKEXIT),
            End,
         End, End;

         DoMethod (AP_Scout,OM_ADDMEMBER,WI_Locks);
         DoMethod (WI_Locks,MUIM_Window_SetCycleChain,locklist,CY_LockSort,lockpattern,BT_LockUpdate,BT_LockPrint,BT_LockRemove,BT_LockExit,NULL);
         DoMethod (CY_LockSort, MUIM_Notify, MUIA_Cycle_Active, MUIV_EveryTime, AP_Scout, 2, MUIM_Application_ReturnID, ID_LOCKSORT);
         DoMethod (lockpattern,MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,AP_Scout,2,MUIM_Application_ReturnID,ID_LOCKUPDATE);

         SetCloseRequest (WI_Locks,ID_LOCKEXIT);
         SetListActive (locklist,ID_LOCKLV_ACTIVE);

         SetWindowOpen (WI_Locks,lockpattern,ID_LOCKEXIT);
      }
   } else if ((! state) && (WI_Locks)) {
      SetWindowClose (WI_Locks,TRUE);

      FreeLocks();

      DoMethod (AP_Scout,OM_REMMEMBER,WI_Locks);
      MUI_DisposeObject (WI_Locks);
      WI_Locks = NULL;
      locklist = NULL;
   }
}

