/*
 * Misc Toolhelp functions
 *
 * Copyright 1996 Marcus Meissner
 */

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <assert.h>
#include "windows.h"
#include "win.h"
#include "winerror.h"
#include "process.h"
#include "tlhelp32.h"
#include "toolhelp.h"
#include "heap.h"
#include "k32obj.h"
#include "debug.h"

/*
 * Support for toolhelp's snapshots.  They
 * are supposed to be Kernel32 Objects.
 * Only the Destroy() method is implemented
 */

static void SNAPSHOT_Destroy( K32OBJ *obj );

const K32OBJ_OPS SNAPSHOT_Ops =
{
  SNAPSHOT_Destroy	/* destroy */
};

/* The K32 snapshot object object */
/* Process snapshot kernel32 object */
typedef struct _Process32Snapshot
{
  K32OBJ		header;

  DWORD			numProcs;
  DWORD			arrayCounter;
  /*
   * Store a reference to the PDB list.  
   * Insuure in the alloc and dealloc routines for this structure that
   * I increment and decrement the pdb->head.refcount, so that the
   * original pdb will stay around for as long as I use it, but it's
   * not locked forver into memory.
   */
  PDB32		**processArray;
}
SNAPSHOT_OBJECT;

/* FIXME: to make this working, we have to callback all these registered 
 * functions from all over the WINE code. Someone with more knowledge than
 * me please do that. -Marcus
 */
static struct notify
{
    HTASK16   htask;
    FARPROC16 lpfnCallback;
    WORD     wFlags;
} *notifys = NULL;

static int nrofnotifys = 0;

static FARPROC16 HookNotify = NULL;

BOOL16 WINAPI NotifyRegister( HTASK16 htask, FARPROC16 lpfnCallback,
                              WORD wFlags )
{
    int	i;

    TRACE(toolhelp, "(%x,%lx,%x) called.\n",
                      htask, (DWORD)lpfnCallback, wFlags );
    if (!htask) htask = GetCurrentTask();
    for (i=0;i<nrofnotifys;i++)
        if (notifys[i].htask==htask)
            break;
    if (i==nrofnotifys) {
        if (notifys==NULL)
            notifys=(struct notify*)HeapAlloc( SystemHeap, 0,
                                               sizeof(struct notify) );
        else
            notifys=(struct notify*)HeapReAlloc( SystemHeap, 0, notifys,
                                        sizeof(struct notify)*(nrofnotifys+1));
        if (!notifys) return FALSE;
        nrofnotifys++;
    }
    notifys[i].htask=htask;
    notifys[i].lpfnCallback=lpfnCallback;
    notifys[i].wFlags=wFlags;
    return TRUE;
}

BOOL16 WINAPI NotifyUnregister( HTASK16 htask )
{
    int	i;
    
    TRACE(toolhelp, "(%x) called.\n", htask );
    if (!htask) htask = GetCurrentTask();
    for (i=nrofnotifys;i--;)
        if (notifys[i].htask==htask)
            break;
    if (i==-1)
        return FALSE;
    memcpy(notifys+i,notifys+(i+1),sizeof(struct notify)*(nrofnotifys-i-1));
    notifys=(struct notify*)HeapReAlloc( SystemHeap, 0, notifys,
                                        (nrofnotifys-1)*sizeof(struct notify));
    nrofnotifys--;
    return TRUE;
}

BOOL16 WINAPI StackTraceCSIPFirst(STACKTRACEENTRY *ste, WORD wSS, WORD wCS, WORD wIP, WORD wBP)
{
    return TRUE;
}

BOOL16 WINAPI StackTraceFirst(STACKTRACEENTRY *ste, HTASK16 Task)
{
    return TRUE;
}

BOOL16 WINAPI StackTraceNext(STACKTRACEENTRY *ste)
{
    return TRUE;
}

/***********************************************************************
 *           ToolHelpHook                             (KERNEL.341)
 *	see "Undocumented Windows"
 */
FARPROC16 WINAPI ToolHelpHook(FARPROC16 lpfnNotifyHandler)
{
FARPROC16 tmp;
	tmp = HookNotify;
	HookNotify = lpfnNotifyHandler;
	/* just return previously installed notification function */
	return tmp;
}

/***********************************************************************
 *	     SNAPSHOT_Destroy
 *
 * Deallocate K32 snapshot objects
 */
static void SNAPSHOT_Destroy (K32OBJ *obj)
{
  int	i;
  SNAPSHOT_OBJECT *snapshot = (SNAPSHOT_OBJECT *) obj;
  assert (obj->type == K32OBJ_CHANGE);

  if (snapshot->processArray)
    {	
      for (i = 0; snapshot->processArray[i] && i <snapshot->numProcs; i++)
	{
	  K32OBJ_DecCount (&snapshot->processArray[i]->header);
	}
      HeapFree (GetProcessHeap (), 0, snapshot->processArray);
      snapshot->processArray = NULL;      
    }	

  obj->type = K32OBJ_UNKNOWN;
  HeapFree (GetProcessHeap (), 0, snapshot);
}

/***********************************************************************
 *           CreateToolHelp32Snapshot			(KERNEL32.179)
 *	see "Undocumented Windows"
 */
HANDLE32 WINAPI CreateToolhelp32Snapshot(DWORD dwFlags, DWORD
					 th32ProcessID) 
{
  HANDLE32		ssHandle;
  SNAPSHOT_OBJECT	*snapshot;
  int			numProcesses;
  int			i;
  PDB32*		pdb;

  TRACE(toolhelp, "%lx & TH32CS_INHERIT (%x) = %lx %s\n", dwFlags, 
	  TH32CS_INHERIT,
	  dwFlags & TH32CS_INHERIT, 
	  dwFlags & TH32CS_INHERIT ? "TRUE" : "FALSE");  
  TRACE(toolhelp, "%lx & TH32CS_SNAPHEAPLIST (%x) = %lx %s\n", dwFlags, 
	  TH32CS_SNAPHEAPLIST,
	  dwFlags & TH32CS_SNAPHEAPLIST, 
	  dwFlags & TH32CS_SNAPHEAPLIST ? "TRUE" : "FALSE");  
  TRACE(toolhelp, "%lx & TH32CS_SNAPMODULE (%x) = %lx %s\n", dwFlags, 
	  TH32CS_SNAPMODULE,
	  dwFlags & TH32CS_SNAPMODULE, 
	  dwFlags & TH32CS_SNAPMODULE ? "TRUE" : "FALSE");  
  TRACE(toolhelp, "%lx & TH32CS_SNAPPROCESS (%x) = %lx %s\n", dwFlags, 
	  TH32CS_SNAPPROCESS,
	  dwFlags & TH32CS_SNAPPROCESS, 
	  dwFlags & TH32CS_SNAPPROCESS ? "TRUE" : "FALSE");  
  TRACE(toolhelp, "%lx & TH32CS_SNAPTHREAD (%x) = %lx %s\n", dwFlags, 
	  TH32CS_SNAPTHREAD,
	  dwFlags & TH32CS_SNAPTHREAD,
	  dwFlags & TH32CS_SNAPTHREAD ? "TRUE" : "FALSE");  

  /**** FIXME: Not implmented ***/
  if (dwFlags & TH32CS_INHERIT)
    {
      FIXME(toolhelp,"(0x%08lx (TH32CS_INHERIT),0x%08lx), stub!\n",
	    dwFlags,th32ProcessID);

      return INVALID_HANDLE_VALUE32;
    }
  if (dwFlags & TH32CS_SNAPHEAPLIST)
    {
      FIXME(toolhelp,"(0x%08lx (TH32CS_SNAPHEAPLIST),0x%08lx), stub!\n",
	    dwFlags,th32ProcessID);
      return INVALID_HANDLE_VALUE32;
    }
  if (dwFlags & TH32CS_SNAPMODULE)
    {
      FIXME(toolhelp,"(0x%08lx (TH32CS_SNAPMODULE),0x%08lx), stub!\n",
	    dwFlags,th32ProcessID);
      return INVALID_HANDLE_VALUE32;
    }

  if (dwFlags & TH32CS_SNAPPROCESS)
    {
      TRACE (toolhelp, "(0x%08lx (TH32CS_SNAPMODULE),0x%08lx)\n",
	    dwFlags,th32ProcessID);
      snapshot = HeapAlloc (GetProcessHeap (), 0, sizeof
			    (SNAPSHOT_OBJECT)); 
      if (!snapshot)
	{
	  return INVALID_HANDLE_VALUE32;
	}

      snapshot->header.type = K32OBJ_TOOLHELP_SNAPSHOT;
      snapshot->header.refcount = 1;
      snapshot->arrayCounter = 0;
  
      /*
       * Lock here, to prevent processes from being created or
       * destroyed while the snapshot is gathered
       */

      SYSTEM_LOCK ();
      numProcesses = PROCESS_PDBList_Getsize ();

      snapshot->processArray = (PDB32**) 
	HeapAlloc (GetProcessHeap (), 0, sizeof (PDB32*) * numProcesses); 

      if (!snapshot->processArray)
        {
          HeapFree (GetProcessHeap (), 0, snapshot->processArray);
	  SetLastError (INVALID_HANDLE_VALUE32);
	  ERR (toolhelp, "Error allocating %d bytes for snapshot\n", 
	       sizeof (PDB32*) * numProcesses); 	       
          return INVALID_HANDLE_VALUE32;
        }

      snapshot->numProcs = numProcesses;

      pdb = PROCESS_PDBList_Getfirst ();
      for (i = 0; pdb && i < numProcesses; i++)
	{
	  TRACE (toolhelp, "Saving ref to pdb %ld\n", PDB_TO_PROCESS_ID(pdb));
	  snapshot->processArray[i] = pdb;
	  K32OBJ_IncCount (&pdb->header);
	  pdb = PROCESS_PDBList_Getnext (pdb);
	}
      SYSTEM_UNLOCK ();

      ssHandle = HANDLE_Alloc (PROCESS_Current (), &snapshot->header,
			       FILE_ALL_ACCESS, TRUE, -1);
      if (ssHandle == INVALID_HANDLE_VALUE32) 
	{
	  /*  HANDLE_Alloc is supposed to deallocate the 
	   *  heap memory if it fails.  This code doesn't need to.
	   */
	  SetLastError (INVALID_HANDLE_VALUE32);
	  ERR (toolhelp, "Error allocating handle\n");
	  return INVALID_HANDLE_VALUE32;
	}
      
      TRACE (toolhelp, "snapshotted %d processes, expected %d\n",
	    i, numProcesses);
      return ssHandle;
    }

  if (dwFlags & TH32CS_SNAPTHREAD)
    {
      FIXME(toolhelp,"(0x%08lx (TH32CS_SNAPMODULE),0x%08lx), stub!\n",
	    dwFlags,th32ProcessID);
      return INVALID_HANDLE_VALUE32;
    }

  return INVALID_HANDLE_VALUE32;
}

/***********************************************************************
 *		Process32First
 * Return info about the first process in a toolhelp32 snapshot
 */
BOOL32 WINAPI Process32First(HANDLE32 hSnapshot, LPPROCESSENTRY32 lppe)
{
  PDB32           *pdb; 
  SNAPSHOT_OBJECT *snapshot;
  int             i;

  TRACE (toolhelp, "(0x%08lx,0x%08lx)\n", (DWORD) hSnapshot,
	 (DWORD) lppe);

  if (lppe->dwSize < sizeof (PROCESSENTRY32))
    {
      SetLastError (ERROR_INSUFFICIENT_BUFFER);
      ERR (toolhelp, "Result buffer too small\n");
      return FALSE;
    }

  SYSTEM_LOCK ();
  snapshot = (SNAPSHOT_OBJECT*) HANDLE_GetObjPtr (PROCESS_Current (),
						  hSnapshot,
						  K32OBJ_UNKNOWN,
						  FILE_ALL_ACCESS,
						  NULL); 
  if (!snapshot)
    {
      SYSTEM_UNLOCK ();
      SetLastError (ERROR_INVALID_HANDLE);
      ERR (toolhelp, "Error retreiving snapshot\n");
      return FALSE;
    }

  snapshot->arrayCounter = i = 0;
  pdb = snapshot->processArray[i];

  if (!pdb)
    {
      SetLastError (ERROR_NO_MORE_FILES);
      ERR (toolhelp, "End of snapshot array\n");
      return FALSE;
    }

  TRACE (toolhelp, "Returning info on process %d, id %ld\n", 
	 i, PDB_TO_PROCESS_ID (pdb));

  lppe->cntUsage = 1;
  lppe->th32ProcessID = PDB_TO_PROCESS_ID (pdb);
  lppe->th32DefaultHeapID = (DWORD) pdb->heap; 
  lppe->cntThreads = pdb->threads; 
  lppe->th32ParentProcessID = PDB_TO_PROCESS_ID (pdb->parent); 
  lppe->pcPriClassBase = 6; /* FIXME: this is a made-up value */
  lppe->dwFlags = -1;       /* FIXME: RESERVED by Microsoft :-) */
  if (pdb->exe_modref) 
    {
      lppe->th32ModuleID = (DWORD) pdb->exe_modref->module;
      strncpy (lppe->szExeFile, pdb->exe_modref->longname, 
	       sizeof (lppe->szExeFile)); 
    }
  else
    {
      lppe->th32ModuleID = (DWORD) 0;
      strcpy (lppe->szExeFile, "");
    }

  SYSTEM_UNLOCK ();

  return TRUE;
}

/***********************************************************************
 *		Process32Next
 * Return info about the "next" process in a toolhelp32 snapshot
 */
BOOL32 WINAPI Process32Next(HANDLE32 hSnapshot, LPPROCESSENTRY32 lppe)
{
  PDB32           *pdb; 
  SNAPSHOT_OBJECT *snapshot;
  int             i;

  TRACE (toolhelp, "(0x%08lx,0x%08lx)\n", (DWORD) hSnapshot,
	 (DWORD) lppe);

  if (lppe->dwSize < sizeof (PROCESSENTRY32))
    {
      SetLastError (ERROR_INSUFFICIENT_BUFFER);
      ERR (toolhelp, "Result buffer too small\n");
      return FALSE;
    }

  SYSTEM_LOCK ();
  snapshot = (SNAPSHOT_OBJECT*) HANDLE_GetObjPtr (PROCESS_Current (),
						  hSnapshot,
						  K32OBJ_UNKNOWN,
						  FILE_ALL_ACCESS,
						  NULL); 
  if (!snapshot)
    {
      SYSTEM_UNLOCK ();
      SetLastError (ERROR_INVALID_HANDLE);
      ERR (toolhelp, "Error retreiving snapshot\n");
      return FALSE;
    }

  snapshot->arrayCounter ++;
  i = snapshot->arrayCounter;
  pdb = snapshot->processArray[i];

  if (!pdb || snapshot->arrayCounter >= snapshot->numProcs)
    {
      SetLastError (ERROR_NO_MORE_FILES);
      ERR (toolhelp, "End of snapshot array\n");
      return FALSE;
    }

  TRACE (toolhelp, "Returning info on process %d, id %ld\n", 
	 i, PDB_TO_PROCESS_ID (pdb));

  lppe->cntUsage = 1;
  lppe->th32ProcessID = PDB_TO_PROCESS_ID (pdb);
  lppe->th32DefaultHeapID = (DWORD) pdb->heap; 
  lppe->cntThreads = pdb->threads; 
  lppe->th32ParentProcessID = PDB_TO_PROCESS_ID (pdb->parent); 
  lppe->pcPriClassBase = 6; /* FIXME: this is a made-up value */
  lppe->dwFlags = -1;       /* FIXME: RESERVED by Microsoft :-) */
  if (pdb->exe_modref) 
    {
      lppe->th32ModuleID = (DWORD) pdb->exe_modref->module;
      strncpy (lppe->szExeFile, pdb->exe_modref->longname, 
	       sizeof (lppe->szExeFile)); 
    }
  else
    {
      lppe->th32ModuleID = (DWORD) 0;
      strcpy (lppe->szExeFile, "");
    }

  SYSTEM_UNLOCK ();

  return TRUE;
}