#pragma once

#define REPLY_FAIL 0x88888888
#define REPLY_OK   0x00000000

#define REQUEST_ICONS      1
#define REQUEST_GROUPS    (REQUEST_ICONS << 1)
#define REQUEST_CONTACTS  (REQUEST_GROUPS << 1)
#define REQUEST_XFRFILES  (REQUEST_CONTACTS << 1)
#define REQUEST_NEWICONS  (REQUEST_XFRFILES << 1)
#define REQUEST_CLEARMRU  (REQUEST_NEWICONS << 1)

#define ICONS_NOTIMPL    0x00000008
#define GROUPS_NOTIMPL   0x00000080
#define CONTACTS_NOTIMPL 0x00000800

#define STATUS_PROFILENAME 2

#define ICMF_NORMAL      0x00000000
#define ICMF_DEFAULTONLY 0x00000001
#define ICMF_VERBSONLY   0x00000002
#define ICMF_EXPLORE     0x00000004
#define ICMF_NOVERBS     0x00000008
#define ICMF_CANRENAME   0x00000010
#define ICMF_NODEFAULT   0x00000020
#define ICMF_INCLUDESTATIC 0x00000040
#define ICMF_RESERVED    0xFFFF0000

// IContextMenu*:GetCommandString() uType flags

#define IGCS_VERBA     0x00000000 // canonical verb
#define IGCS_HELPTEXTA 0x00000001 // help text (for status bar)
#define IGCS_VALIDATEA 0x00000002 // validate command exists
#define IGCS_VERBW     0x00000004 // canonical verb (unicode)
#define IGC_HELPTEXTW  0x00000005 // help text (unicode version)
#define IGCS_VALIDATEW 0x00000006 // validate command exists (unicode)
#define IGCS_UNICODE   0x00000004 // for bit testing - Unicode string
#define IGCS_VERB      GCS_VERBA
#define IGCS_HELPTEXT  GCS_HELPTEXTA
#define IGCS_VALIDATE  GCS_VALIDATEA

#define HIPC_NOICONS   1

#define IPC_PACKET_SIZE (0x1000 * 32)
#define IPC_PACKET_NAME "m.mi.miranda.ipc.server"

/////////////////////////////////////////////////////////////////////////////////////////

struct TGroupNode
{
	TGroupNode *Left, *Right, *_prev, *_next;
	int Depth;
   UINT Hash; // hash of the group name alone
   char *szGroup;						  
   int cchGroup;
   HMENU hMenu;
   int hMenuGroupID;
   DWORD dwItems;
};

struct TGroupNodeList
{
	TGroupNode *First, *Last;
};

struct TStrTokRec
{
	char *szStr, *szSet;
	// need a delimiter after the token too?, e.g. FOO^BAR^ if FOO^BAR
	// is the string then only FOO^ is returned, could cause infinite loops
	// if the condition isn't accounted for thou.
	bool bSetTerminator;
};

/////////////////////////////////////////////////////////////////////////////////////////

struct TSlotProtoIcons
{
	UINT pid; // pid of Miranda this protocol was on
	UINT hProto; // hash of the protocol
	HICON hIcons[10]; // each status in order of ID_STATUS_*
	HBITMAP hBitmaps[10]; // each status "icon" as a bitmap
};

struct TSlotIPC
{
	BYTE cbSize;
	int  fType; // a REQUEST_* type
	TSlotIPC *Next;
	MCONTACT hContact;
	UINT hProto; // hash of the protocol the user is on
	UINT hGroup; // hash of the entire path (not defined for REQUEST_GROUPS slots)
	WORD Status;
	// only used for contacts -- can be STATUS_PROFILENAME -- but that is because returning the profile name is optional
   BYTE MRU; // if set, contact has been recently used
   int cbStrSection;
};

struct THeaderIPC
{
	int    cbSize;
	DWORD  dwVersion;
	void  *pServerBaseAddress, *pClientBaseAddress;
	int    fRequests;
	DWORD  dwFlags;
	int    Slots;
	HANDLE Param;
	char   SignalEventName[64];
	char   MirandaName[64];
	char   MRUMenuName[64];
	char   ClearEntries[64];
	TSlotIPC *IconsBegin, *ContactsBegin, *GroupsBegin, *NewIconsBegin;
	// start of an flat memory stack, which is referenced as a linked list
	int DataSize;
	TSlotIPC *DataPtr, *DataPtrEnd;
   void *DataFramePtr;
};

/////////////////////////////////////////////////////////////////////////////////////////

struct TShellExt : public IShellExtInit, public IContextMenu3, public MZeroedObject
{
	TShellExt();
	~TShellExt();

	ULONG RefCount;
	// this is owned by the shell after items are added 'n' is used to
	// grab menu information directly via id rather than array indexin'
	HMENU hRootMenu;
	int   idCmdFirst;
	// most of the memory allocated is on this heap object so HeapDestroy()
	// can do most of the cleanup, extremely lazy I know.
	HANDLE hDllHeap;
	// This is a submenu that recently used contacts are inserted into
	// the contact is inserted twice, once in its normal list (or group) and here
	// Note: These variables are global data, but refered to locally by each instance
	// Do not rely on these variables outside the process enumeration.
	HMENU hRecentMenu;
	UINT  RecentCount; // number of added items
	// array of all the protocol icons, for every running instance!
	TSlotProtoIcons *ProtoIcons;
	UINT ProtoIconsCount;
	// maybe null, taken from IShellExtInit_Initalise() and AddRef()'d
	// only used if a Miranda instance is actually running and a user
	// is selected
	IDataObject *pDataObject;
	// DC is used for font metrics and saves on creating and destroying lots of DC handles
	// during WM_MEASUREITEM
	HDC hMemDC;

	HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);
	ULONG   STDMETHODCALLTYPE AddRef(void);
	ULONG   STDMETHODCALLTYPE Release(void);

	HRESULT STDMETHODCALLTYPE Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);

	HRESULT STDMETHODCALLTYPE QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
	HRESULT STDMETHODCALLTYPE InvokeCommand(CMINVOKECOMMANDINFO *pici);
	HRESULT STDMETHODCALLTYPE GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pReserved, LPSTR pszName, UINT cchMax);
	HRESULT STDMETHODCALLTYPE HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
	HRESULT STDMETHODCALLTYPE HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
};

struct TEnumData
{
	TShellExt *Self;

   // autodetected, don't hard code since shells that don't support it
   // won't send WM_MEASUREITETM/WM_DRAWITEM at all.
	BOOL bOwnerDrawSupported;
	// as per user setting (maybe of multiple Mirandas)
	BOOL bShouldOwnerDraw;
	int idCmdFirst;
	THeaderIPC *ipch;
	// OpenEvent()'d handle to give each IPC server an object to set signalled
	HANDLE hWaitFor;
	DWORD pid; // sub-unique value used to make work object name
};

struct TClassFactoryRec : public IClassFactory
{
	TClassFactoryRec();

	LONG RefCount;

	HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);
	ULONG   STDMETHODCALLTYPE AddRef(void);
	ULONG   STDMETHODCALLTYPE Release(void);

	HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
	HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock);
};

/////////////////////////////////////////////////////////////////////////////////////////

enum TSlotDrawType {dtEntry = 0x01, dtGroup = 0x02, dtContact = 0x04, dtCommand = 0x08 };
typedef int TSlotDrawTypes;

typedef int (__stdcall TMenuCommandCallback)(
	THeaderIPC *pipch,          // IPC header info, already mapped
   HANDLE hWorkThreadEvent,    // event object being waited on on miranda thread
   HANDLE hAckEvent);          // ack event object that has been created

struct TMenuDrawInfo
{
	char *szText, *szProfile;
	int cch;
	UINT wID;
   TSlotDrawTypes fTypes;
   MCONTACT hContact;
	HICON hStatusIcon; // HICON from Self->ProtoIcons[index].hIcons[status]; Do not DestroyIcon()
   HBITMAP hStatusBitmap; // HBITMAP, don't free.
   int pid;
   TMenuCommandCallback *MenuCommandCallback; // dtCommand must be set also.
};

/////////////////////////////////////////////////////////////////////////////////////////

void  ipcPrepareRequests(int ipcPacketSize, THeaderIPC *pipch, DWORD fRequests);
DWORD ipcSendRequest(HANDLE hSignal, HANDLE hWaitFor, THeaderIPC *pipch, DWORD dwTimeoutMsecs);
TSlotIPC* ipcAlloc(THeaderIPC *pipch, int nSize);
void ipcFixupAddresses(THeaderIPC *pipch);

TGroupNode* AllocGroupNode(TGroupNodeList *list, TGroupNode *Root, int Depth);
TGroupNode* FindGroupNode(TGroupNode* p, const DWORD Hash, int Depth);

char* CreateProcessUID(int pid, char *buf, size_t bufLen);