/*
 *	shell change notification
 *
 *	Juergen Schmied <juergen.schmied@debitel.de>
 *
 */

#include <string.h>

#include "wine/debug.h"
#include "pidl.h"
#include "shell32_main.h"
#include "undocshell.h"

WINE_DEFAULT_DEBUG_CHANNEL(shell);

static CRITICAL_SECTION SHELL32_ChangenotifyCS;

void create_notify_cs(void)
{
  InitializeCriticalSection( &SHELL32_ChangenotifyCS );
  CRITICAL_SECTION_NAME( &SHELL32_ChangenotifyCS, "SHELL32_ChangenotifyCS" );
}

void destroy_notify_cs(void)
{
  DeleteCriticalSection( &SHELL32_ChangenotifyCS );
}


/* internal list of notification clients (internal) */
typedef struct _NOTIFICATIONLIST
{
	struct _NOTIFICATIONLIST *next;
	struct _NOTIFICATIONLIST *prev;
	HWND hwnd;		/* window to notify */
	DWORD uMsg;		/* message to send */
	LPNOTIFYREGISTER apidl; /* array of entries to watch*/
	UINT cidl;		/* number of pidls in array */
	LONG wEventMask;	/* subscribed events */
	DWORD dwFlags;		/* client flags */
} NOTIFICATIONLIST, *LPNOTIFICATIONLIST;

static NOTIFICATIONLIST head;
static NOTIFICATIONLIST tail;

void InitChangeNotifications()
{
	TRACE("head=%p tail=%p\n", &head, &tail);
	head.next = &tail;
	tail.prev = &head;
}

void FreeChangeNotifications()
{
	LPNOTIFICATIONLIST ptr, item;

	TRACE("\n");

	EnterCriticalSection(&SHELL32_ChangenotifyCS);
	ptr = head.next;

	while(ptr != &tail)
	{
	  int i;
	  item = ptr;
	  ptr = ptr->next;

	  TRACE("item=%p\n", item);

	  /* free the item */
	  for (i=0; i<item->cidl;i++) SHFree(item->apidl[i].pidlPath);
	  SHFree(item->apidl);
	  SHFree(item);
	}
	head.next = NULL;
	tail.prev = NULL;

	LeaveCriticalSection(&SHELL32_ChangenotifyCS);

	DeleteCriticalSection(&SHELL32_ChangenotifyCS);
}

static BOOL AddNode(LPNOTIFICATIONLIST item)
{
	LPNOTIFICATIONLIST last;

	EnterCriticalSection(&SHELL32_ChangenotifyCS);

	/* get last entry */
	last = tail.prev;

	/* link items */
	last->next = item;
	item->prev = last;
	item->next = &tail;
	tail.prev = item;
	TRACE("item=%p prev=%p next=%p\n", item, item->prev, item->next);

	LeaveCriticalSection(&SHELL32_ChangenotifyCS);

	return TRUE;
}

static BOOL DeleteNode(LPNOTIFICATIONLIST item)
{
	LPNOTIFICATIONLIST ptr;
	int ret = FALSE;

	TRACE("item=%p\n", item);

	EnterCriticalSection(&SHELL32_ChangenotifyCS);

	ptr = head.next;
	while((ptr != &tail) && (ret == FALSE))
	{
	  TRACE("ptr=%p\n", ptr);

	  if (ptr == item)
	  {
	    int i;

	    TRACE("item=%p prev=%p next=%p\n", item, item->prev, item->next);

	    /* remove item from list */
	    item->prev->next = item->next;
	    item->next->prev = item->prev;

	    /* free the item */
	    for (i=0; i<item->cidl;i++) SHFree(item->apidl[i].pidlPath);
	    SHFree(item->apidl);
	    SHFree(item);
	    ret = TRUE;
	  }
	  ptr = ptr->next;
	}

	LeaveCriticalSection(&SHELL32_ChangenotifyCS);
	return ret;

}

/*************************************************************************
 * SHChangeNotifyRegister			[SHELL32.2]
 *
 */
HANDLE WINAPI
SHChangeNotifyRegister(
    HWND hwnd,
    LONG dwFlags,
    LONG wEventMask,
    DWORD uMsg,
    int cItems,
    LPCNOTIFYREGISTER lpItems)
{
	LPNOTIFICATIONLIST item;
	int i;

	item = SHAlloc(sizeof(NOTIFICATIONLIST));

	TRACE("(0x%04x,0x%08lx,0x%08lx,0x%08lx,0x%08x,%p) item=%p\n",
		hwnd,dwFlags,wEventMask,uMsg,cItems,lpItems,item);

	item->next = NULL;
	item->prev = NULL;
	item->cidl = cItems;
	item->apidl = SHAlloc(sizeof(NOTIFYREGISTER) * cItems);
	for(i=0;i<cItems;i++)
	{
	  item->apidl[i].pidlPath = ILClone(lpItems[i].pidlPath);
	  item->apidl[i].bWatchSubtree = lpItems[i].bWatchSubtree;
	}
	item->hwnd = hwnd;
	item->uMsg = uMsg;
	item->wEventMask = wEventMask;
	item->dwFlags = dwFlags;
	AddNode(item);
	return (HANDLE)item;
}

/*************************************************************************
 * SHChangeNotifyDeregister			[SHELL32.4]
 */
BOOL WINAPI
SHChangeNotifyDeregister(
	HANDLE hNotify)
{
	TRACE("(0x%08x)\n",hNotify);

	return DeleteNode((LPNOTIFICATIONLIST)hNotify);
}

/*************************************************************************
 * SHChangeNotifyUpdateEntryList       		[SHELL32.5]
 */
BOOL WINAPI
SHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2,
			      DWORD unknown3, DWORD unknown4)
{
	FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx)\n",
	      unknown1, unknown2, unknown3, unknown4);

	return -1;
}

/*************************************************************************
 * SHChangeNotify				[SHELL32.@]
 */
void WINAPI SHChangeNotifyW (LONG wEventId, UINT  uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
{
	LPITEMIDLIST pidl1=(LPITEMIDLIST)dwItem1, pidl2=(LPITEMIDLIST)dwItem2;
	LPNOTIFICATIONLIST ptr;

	TRACE("(0x%08lx,0x%08x,%p,%p):stub.\n", wEventId,uFlags,dwItem1,dwItem2);

	/* convert paths in IDLists*/
	if(uFlags & SHCNF_PATHA)
	{
	  DWORD dummy;
	  if (dwItem1) SHILCreateFromPathA((LPCSTR)dwItem1, &pidl1, &dummy);
	  if (dwItem2) SHILCreateFromPathA((LPCSTR)dwItem2, &pidl2, &dummy);
	}

	EnterCriticalSection(&SHELL32_ChangenotifyCS);

	/* loop through the list */
	ptr = head.next;
	while(ptr != &tail)
	{
	  TRACE("trying %p\n", ptr);

	  if(wEventId & ptr->wEventMask)
	  {
	    TRACE("notifying\n");
	    SendMessageA(ptr->hwnd, ptr->uMsg, (WPARAM)pidl1, (LPARAM)pidl2);
	  }
	  ptr = ptr->next;
	}

	LeaveCriticalSection(&SHELL32_ChangenotifyCS);

	if(uFlags & SHCNF_PATHA)
	{
            if (pidl1) SHFree(pidl1);
            if (pidl2) SHFree(pidl2);
	}
}

/*************************************************************************
 * SHChangeNotify				[SHELL32.@]
 */
void WINAPI SHChangeNotifyA (LONG wEventId, UINT  uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
{
	LPITEMIDLIST Pidls[2];
	LPNOTIFICATIONLIST ptr;

	Pidls[0] = (LPITEMIDLIST)dwItem1;
	Pidls[1] = (LPITEMIDLIST)dwItem2;

	TRACE("(0x%08lx,0x%08x,%p,%p):stub.\n", wEventId,uFlags,dwItem1,dwItem2);

	/* convert paths in IDLists*/
	if(uFlags & SHCNF_PATHA)
	{
	  DWORD dummy;
	  if (Pidls[0]) SHILCreateFromPathA((LPCSTR)dwItem1, &Pidls[0], &dummy);
	  if (Pidls[1]) SHILCreateFromPathA((LPCSTR)dwItem2, &Pidls[1], &dummy);
	}

	EnterCriticalSection(&SHELL32_ChangenotifyCS);

	/* loop through the list */
	ptr = head.next;
	while(ptr != &tail)
	{
	  TRACE("trying %p\n", ptr);

	  if(wEventId & ptr->wEventMask)
	  {
	    TRACE("notifying\n");
	    SendMessageA(ptr->hwnd, ptr->uMsg, (WPARAM)&Pidls, (LPARAM)wEventId);
	  }
	  ptr = ptr->next;
	}

	LeaveCriticalSection(&SHELL32_ChangenotifyCS);

	/* if we allocated it, free it */
	if(uFlags & SHCNF_PATHA)
	{
            if (Pidls[0]) SHFree(Pidls[0]);
            if (Pidls[1]) SHFree(Pidls[1]);
	}
}

/*************************************************************************
 * SHChangeNotify				[SHELL32.@]
 */
void WINAPI SHChangeNotifyAW (LONG wEventId, UINT  uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
{
	if(SHELL_OsIsUnicode())
	  SHChangeNotifyW (wEventId, uFlags, dwItem1, dwItem2);
	else
	  SHChangeNotifyA (wEventId, uFlags, dwItem1, dwItem2);
}

/*************************************************************************
 * NTSHChangeNotifyRegister			[SHELL32.640]
 * NOTES
 *   Idlist is an array of structures and Count specifies how many items in the array
 *   (usually just one I think).
 */
DWORD WINAPI NTSHChangeNotifyRegister(
    HWND hwnd,
    LONG events1,
    LONG events2,
    DWORD msg,
    int count,
    LPNOTIFYREGISTER idlist)
{
	FIXME("(0x%04x,0x%08lx,0x%08lx,0x%08lx,0x%08x,%p):stub.\n",
		hwnd,events1,events2,msg,count,idlist);
	return 0;
}

/*************************************************************************
 * SHChangeNotification_Lock			[SHELL32.644]
 */
HANDLE WINAPI SHChangeNotification_Lock(
	HANDLE hMemoryMap,
	DWORD dwProcessId,
	LPCITEMIDLIST **lppidls,
	LPLONG lpwEventId)
{
	FIXME("\n");
	return 0;
}

/*************************************************************************
 * SHChangeNotification_Unlock			[SHELL32.645]
 */
BOOL WINAPI SHChangeNotification_Unlock (
	HANDLE hLock)
{
	FIXME("\n");
	return 0;
}

/*************************************************************************
 * NTSHChangeNotifyDeregister			[SHELL32.641]
 */
DWORD WINAPI NTSHChangeNotifyDeregister(LONG x1)
{
	FIXME("(0x%08lx):stub.\n",x1);
	return 0;
}

