#ifndef UTILS_H_INCLUDED
#define UTILS_H_INCLUDED

#include <stdio.h>
#include <xcircuit.h>
#include <prototypes.h>

#define RELEASE_CODE
#define QUERY_CODE

#define BEGIN_XC_IMPL(cls) \
STDMETHODIMP cls##_QueryInterface(I##cls *This, REFIID iid, void **ppvObject)		\
{											\
	if (InlineIsEqualGUID(iid, &IID_IErrorInfo)) \
		printf("Looking for error info iface on %p\n", This); \
	if (InlineIsEqualGUID(iid, &IID_IUnknown) ||					\
	    InlineIsEqualGUID(iid, &IID_IDispatch) ||					\
	    InlineIsEqualGUID(iid, &IID_I##cls))					\
		*ppvObject = This;							\
	QUERY_CODE									\
	else										\
		return E_NOINTERFACE;							\
											\
	I##cls##_AddRef(This);								\
											\
	return S_OK;									\
}											\
											\
STDMETHODIMP_(ULONG) cls##_AddRef(I##cls *This)						\
{											\
	((cls*)This)->m_refCount++;							\
	return S_OK;									\
}											\
											\
STDMETHODIMP_(ULONG) cls##_Release(I##cls *This)					\
{											\
	if (--(((cls*)This)->m_refCount) == 0) {					\
		RELEASE_CODE								\
		LocalFree(This);							\
	}										\
	return S_OK;									\
}											\
											\
static ITypeInfo *ti;									\
											\
STDMETHODIMP cls##_GetTypeInfoCount(I##cls *This, UINT *pctinfo)			\
{											\
	*pctinfo = 1;									\
	return S_OK;									\
}											\
											\
STDMETHODIMP cls##_GetTypeInfo(I##cls *This, UINT itinfo, LCID lcid,			\
		ITypeInfo **pptinfo)							\
{											\
	if (itinfo != 0)								\
		return DISP_E_BADINDEX;							\
	*pptinfo = ti;									\
	ITypeInfo_AddRef(ti);								\
	return S_OK;									\
}											\
											\
STDMETHODIMP cls##_GetIDsOfNames(I##cls *This, REFIID iid, LPOLESTR *rgszNames,		\
		UINT cNames, LCID lcid, DISPID *rgDispId)				\
{											\
	return DispGetIDsOfNames(ti, rgszNames, cNames, rgDispId);			\
}											\
											\
STDMETHODIMP cls##_Invoke(I##cls *This, DISPID dispIdMember, REFIID iid,		\
		LCID lcid, WORD wFlags,	DISPPARAMS *pDispParams, VARIANT *pVarResult,	\
		EXCEPINFO *pExcepInfo, UINT *puArgErr)					\
{											\
	return DispInvoke(This, ti, dispIdMember, wFlags, pDispParams, pVarResult,	\
			pExcepInfo, puArgErr);						\
}

#define XCOBJ_QUERY_CODE					\
	else if (InlineIsEqualGUID(iid, &IID_IXCObject))	\
		*ppvObject = This;
#define XCLINEOBJ_QUERY_CODE					\
	XCOBJ_QUERY_CODE					\
	else if (InlineIsEqualGUID(iid, &IID_IXCLineObject))	\
		*ppvObject = This;

#define END_XC_IMPL(cls)						\
HRESULT init_##cls(ITypeLib *tl)					\
{									\
	HRESULT hres;							\
									\
	hres = ITypeLib_GetTypeInfoOfGuid(tl, &IID_I##cls, &ti);	\
	if (hres == S_OK)						\
		ITypeInfo_AddRef(ti);					\
	return hres;							\
}									\
									\
HRESULT close_##cls()							\
{									\
	ITypeInfo_Release(ti);						\
	return S_OK;							\
}									\
									\
cls* make_##cls()							\
{									\
	cls *ptr = (cls*)LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,		\
				    sizeof(cls));			\
	ptr->iface.lpVtbl = &vtbl;					\
	return ptr;							\
}

#define COM_VTBL_BEGIN(cls) 		\
static I##cls##Vtbl vtbl = {		\
	cls##_QueryInterface,		\
	cls##_AddRef,			\
	cls##_Release,			\
	cls##_GetTypeInfoCount,		\
	cls##_GetTypeInfo,		\
	cls##_GetIDsOfNames,		\
	cls##_Invoke,

#define XCOBJ_VTBL_BEGIN(cls)		\
COM_VTBL_BEGIN(cls)			\
	XCObject_get_object_handle,	\
	XCObject_get_color,		\
	XCObject_put_color,		\
	XCObject_select,		\
	XCObject_update,

#define XCLINEOBJ_VTBL_BEGIN(cls)	\
XCOBJ_VTBL_BEGIN(cls)			\
	XCLineObject_get_width,		\
	XCLineObject_put_width,		\
	XCLineObject_put_border,	\
	XCLineObject_get_border,	\
	XCLineObject_put_closed,	\
	XCLineObject_get_closed,

#define COM_VTBL_END() \
};

#define XCOBJ_VTBL_END COM_VTBL_END
#define XCLINEOBJ_VTBL_END XCOBJ_VTBL_END

#define BEGIN_XC_CLASS(cls)	\
typedef struct cls {		\
	interface I##cls iface;	\
	int m_refCount;

#define BEGIN_XCOBJ_CLASS(cls)	\
BEGIN_XC_CLASS(cls)		\
	long handle;		\
	int type;

#define END_XC_CLASS(cls)		\
} cls, *cls##Ptr;			\
cls* make_##cls();			\
HRESULT init_##cls(ITypeLib *tl);	\
HRESULT close_##cls();

#define END_XCOBJ_CLASS END_XC_CLASS

#define GET_METHOD(cls, member, xc_member, type, castfn, code) 		\
STDMETHODIMP cls##_get_##member(I##cls *This, type *pVal)		\
{									\
	HRESULT hres;							\
	long handle;							\
									\
	if ((hres = I##cls##_get_object_handle(This, &handle)) != S_OK)	\
		return hres;						\
	code								\
	*pVal = castfn(&handle)->xc_member;				\
									\
	return S_OK;							\
}

#define PUT_METHOD(cls, member, xc_member, type, castfn, code)		\
STDMETHODIMP cls##_put_##member(I##cls *This, type newVal)		\
{									\
	HRESULT hres;							\
	long handle;							\
									\
	if ((hres = I##cls##_get_object_handle(This, &handle)) != S_OK)	\
		return hres;						\
	code								\
	castfn(&handle)->xc_member = newVal;				\
	I##cls##_update(This, 1);					\
									\
	return S_OK;							\
}

#define PROPERTY_IMPL2(cls, member, xc_member, type, castfn, get_code, put_code)	\
GET_METHOD(cls, member, xc_member, type, castfn, get_code)				\
PUT_METHOD(cls, member, xc_member, type, castfn, put_code)

#define PROPERTY_IMPL(cls, member, xc_member, type, castfn)	\
PROPERTY_IMPL2(cls, member, xc_member, type, castfn, ;, ;)

enum xc_error_code {
	XC_E_TYPEMISMATCH	= 0xD,
	XC_E_FIRST		= 600,
	XC_E_HANDLE,
	XC_E_COLORINDEX,
	XC_E_ARCNOTCIRCLE,
	XC_E_ARCINVALIDANGLES,
	XC_E_INVALIDBORDER,
	XC_E_OUTOFRANGE,
	XC_E_NUMARGS,
	XC_E_JUSTIFY,
	XC_E_LAST
};
extern wchar_t *error_msg[];
#define XC_ERROR_MSG(code) error_msg[code-XC_E_FIRST]
#define ECODE(code) MAKE_SCODE(SEVERITY_ERROR, FACILITY_CONTROL, code)

int get_string_index_from_variant(VARIANT *val, char **sarray, int ssize);
int get_string_index(BSTR val, char **sarray, int ssize);
int get_wide_string_index(BSTR val, wchar_t **sarray, int ssize);
char* convert_from_bstr(BSTR val);
BSTR convert_to_bstr(char *str);
char *string_from_parts(stringpart *parts);
stringpart* string_to_parts(char *str);

#endif /* UTILS_H_INCLUDED */
