/***
*crempoly.cpp
*
*  Copyright (C) 1992, Microsoft Corporation.  All Rights Reserved.
*  Information Contained Herein Is Proprietary and Confidential.
*
*Purpose:
*  This file contains the implementation of CRemPoly, the remote polygon
*  class. This class presents a standard C++ vtable interface to the
*  rest of the application, and hides the details of talking to the
*  actual remote CPoly class exposed by the SPoly server. Each of
*  the introduced methods is simply a cover for an IDispatch invocation
*  of the actual method on the remote object.
*
*Implementation Notes:
*
*****************************************************************************/

#include "dispdemo.h"
#include "crempoly.h"

extern int g_fTrace;

// method names on the CPoly class.
//
char FAR* CRemPoly::m_rgszMethods[] = {
	"draw",
	"dump",
	"reset",
	"addpoint",
	"enumpoints",
	"getxorigin",
	"setxorigin",
	"getyorigin",
	"setyorigin",
	"getwidth",
	"setwidth",
	"get_red",
	"set_red",
	"get_green",
	"set_green",
	"get_blue",
	"set_blue"
};

#ifdef _MAC
# define IfMac(X) (X)
# define IfWin(X)
#else
# define IfMac(X)
# define IfWin(X) (X)
#endif


CRemPoly::CRemPoly()
{
	m_refs = 0;
	m_pdisp = (IDispatch FAR*)NULL;
}


// A useful pre-initialized DISPATCHPARAMS, used on all the methods that
// take 0 arguments.
//
DISPPARAMS NEAR g_dispparamsNoArgs = {NULL, NULL, 0, 0};


/***
*HRESULT CRemPoly::Create(clsid, CRemPoly**)
*
*Purpose:
*  This function creates an instance of the CRemPoly class, connects
*  it to the IDispatch interface of the remote CPoly class, and learns
*  the DISPIDs for the members (that we know about) exposed by that
*  class.
*
*Entry:
*  clsid = The CLSID of the CPoly we are to create. (taking this as a
*    param is a bit weird, but allows us to connect to several remote
*    versions.
*
*Exit:
*  return value = HRESULT
*
*  *pprempoly = pointer to the newly created CRemPoly, if successfyl.
*
***********************************************************************/
HRESULT
CRemPoly::Create(CLSID clsid, CRemPoly FAR* FAR* pprempoly)
{
	int i;
	HRESULT hresult;
	IUnknown FAR* punk;
	CRemPoly FAR* prempoly;


	prempoly = new FAR CRemPoly();
	if(prempoly == (CRemPoly FAR*)NULL){
	  hresult = ResultFromScode(E_OUTOFMEMORY);
	  goto LError;
	}
	prempoly->AddRef();

	// create an instance of the remote CPoly class.
	//
	IfMac(DbPrintf("CoCreateInstance(CLSID_CPoly)"));
	hresult = CoCreateInstance(
	  clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void FAR* FAR*)&punk);
	if(hresult != NOERROR){
	  IfMac(DbPrintf("CoCreateInstance() = 0x%x", hresult));
	  IfWin(MessageBox(NULL, "Unable to create polygon object", NULL, MB_OK));
	  goto LFreeCRemPoly;
	}

	// were going to talk to this remote instance via IDispatch.
	//
	IfMac(DbPrintf("QueryInterface(IID_IDispatch)"));
	hresult = punk->QueryInterface(
	  IID_IDispatch, (void FAR* FAR*)&prempoly->m_pdisp);
	if(hresult != NOERROR){
	  IfMac(DbPrintf("QueryInterface(IID_IDispatch) = 0x%x", hresult));
	  IfWin(MessageBox(NULL, "Unable to QueryInterface to IDispatch", NULL, MB_OK));
	  goto LReleaseUnk;
	}

	// We learn *all* the member IDs up front. A more sophisticated
	// implementation might defer learning about the IDs for a given
	// method until the first time the method is invoked, thereby
	// amortizing the creation costs.
	//
	IfMac(DbPrintf("GetIDsOfNames()"));
	for(i = 0; i < IMETH_CREMPOLY_MAX; ++i){
	  hresult = prempoly->m_pdisp->GetIDsOfNames(
	IID_NULL,
		&prempoly->m_rgszMethods[i],
	1, LOCALE_USER_DEFAULT,
	&prempoly->m_rgdispid[i]);
	  if(hresult != NOERROR){
	IfMac(DbPrintf("GetIDsOfNames() = 0x%x", hresult));
	IfWin(MessageBox(NULL, "Unrecognized member name", NULL, MB_OK));
	goto LReleaseUnk;
	  }
	}

	punk->Release();

	*pprempoly = prempoly;

	IfMac(DbPrintf("Object created."));

	return NOERROR;

LReleaseUnk:;
	punk->Release();

LFreeCRemPoly:;
	prempoly->Release();

LError:;
	return hresult;
}


//---------------------------------------------------------------------
//                     IUnknown methods
//---------------------------------------------------------------------


/***
*HRESULT CRemPoly::QueryInterface(REFIID, void**)
*
*Purpose:
*  Standard Ole2 implementation of QueryInterface. This class
*  supports the IUnknown interface, and introduces a number of
*  nonvirtual members.
*
*Entry:
*  riid = reference to the requested interface id
*
*Exit:
*  return value = HRESULT
*  *ppv = pointer to the requested interface, if successful.
*
***********************************************************************/
STDMETHODIMP
CRemPoly::QueryInterface(REFIID riid, void FAR* FAR* ppv)
{
	if(riid == IID_IUnknown){
	  *ppv = this;
	  AddRef();
	  return NOERROR;
	}
	return ResultFromScode(E_NOINTERFACE);
}


/***
*unsigned long CRemPoly::AddRef(void)
*
*Purpose:
*  Add a reference to the instance.
*
*Entry:
*  None
*
*Exit:
*  return value = unsigned long. The resulting reference count.
*
***********************************************************************/
STDMETHODIMP_(unsigned long)
CRemPoly::AddRef(void)
{
	return ++m_refs;
}


/***
*unsigned long CRemPoly::Release(void)
*
*Purpose:
*  Release a reference to the instance. If the reference count goes
*  to zero, delete the instance.
*
*Entry:
*  None
*
*Exit:
*  return value = unsigned long. The resulting reference count.
*
***********************************************************************/
STDMETHODIMP_(unsigned long)
CRemPoly::Release(void)
{
	if(--m_refs == 0){
	  if(m_pdisp != (IDispatch FAR*)NULL){
		m_pdisp->Release();
	  }
	  delete this;
	  return 0;
	}
	return m_refs;
}


//---------------------------------------------------------------------
//                    Introduced methods
//---------------------------------------------------------------------


/*
 * Each of these methods is simply a cover for an IDispatch Invocation
 * of the actual method on the remote CPoly class. This allows CRemPoly
 * to present an interface that looks and acts just like the CPoly
 * object, even though the actual work is being done in another process.
 *
 */

/***
*HRESULT CRemPoly::Draw(void)
*
*Purpose:
*  Invoke the Draw method on the remote CPoly instance.
*
*Entry:
*  None
*
*Exit:
*  return value = HRESULT
*
***********************************************************************/
HRESULT
CRemPoly::Draw()
{
	return m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_DRAW],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &g_dispparamsNoArgs, NULL, NULL, NULL);
}


/***
*HRESULT CRemPoly::Dump(void)
*
*Purpose:
*  Invoke the Dump() method on the remote CPoly instance. This method
*  dumps the contained CPoints and writes the properties of the remote
*  CPoly instance to the debug window.
*
*Entry:
*  None
*
*Exit:
*  return value = HRESULT
*
***********************************************************************/
HRESULT
CRemPoly::Dump()
{
	return m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_DUMP],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &g_dispparamsNoArgs, NULL, NULL, NULL);
}


/***
*HRESULT CRemPoly::Reset(void)
*
*Purpose:
*  Invoke the Reset() method on the remote CPoly instance. The Reset()
*  method causes the remote CPoly to release all contained CPoints.
*
*Entry:
*  None
*
*Exit:
*  return value = HRESULT
*
***********************************************************************/
HRESULT
CRemPoly::Reset()
{
	return m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_RESET],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &g_dispparamsNoArgs, NULL, NULL, NULL);
}


/***
*HRESULT CRemPoly::AddPoint(short, short)
*
*Purpose:
*  Invoke the AddPoint method in the remote CPoly object to add a
*  new point with the given coordinates to this instance.
*
*Entry:
*  x,y = the x and y coordinates of the new point.
*
*Exit:
*  return value = HRESULT
*
***********************************************************************/
HRESULT
CRemPoly::AddPoint(short x, short y)
{
	HRESULT hresult;
	VARIANTARG varg[2];
	DISPPARAMS dispparams;

	V_VT(&varg[0]) = VT_I2;
	V_I2(&varg[0]) = y;

	V_VT(&varg[1]) = VT_I2;
	V_I2(&varg[1]) = x;

	dispparams.cArgs = 2;
	dispparams.rgvarg = varg;
	dispparams.cNamedArgs = 0;
	dispparams.rgdispidNamedArgs = NULL;

	hresult = m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_ADDPOINT],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &dispparams, NULL, NULL, NULL);

	return hresult;
}


/***
*HRESULT CRemPoly::EnumPoints(IEnumVARIANT**)
*Purpose:
*  Inoke the EnumPoints() method in the remote object to
*  get a enumerator for the points contained in the current poly.
*
*Entry:
*  None
*
*Exit:
*  return value = HRESULT
*
*  *ppenum = pointer to the point enumerator
*
***********************************************************************/
HRESULT
CRemPoly::EnumPoints(IEnumVARIANT FAR* FAR* ppenum)
{
	HRESULT hresult;
	IEnumVARIANT FAR* penum;
	VARIANT varResult, FAR* pvarResult;


	pvarResult = &varResult;
	VariantInit(pvarResult);
	hresult = m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_ENUMPOINTS],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &g_dispparamsNoArgs, pvarResult, NULL, NULL);

	if(hresult != NOERROR)
	  return hresult;

	if(V_VT(pvarResult) != VT_UNKNOWN)
	  return ResultFromScode(E_FAIL);

	hresult = V_UNKNOWN(pvarResult)->QueryInterface(
	  IID_IEnumVARIANT, (void FAR* FAR*)&penum);

	if(hresult == NOERROR)
	  *ppenum = penum;

	VariantClear(pvarResult);

	return NOERROR;
}


/***
*HRESULT CRemPoly::GetXOrigin(short*)
*
*Purpose:
*  Invoke the GetXOrigin() method on the remote object to extract
*  the current value of the XOrigin property.
*
*Entry:
*  None
*
*Exit:
*  return value = HRESULT
*
*  *pxorg = the current X origin of the polygon.
*
***********************************************************************/
HRESULT
CRemPoly::GetXOrigin(short FAR* pxorg)
{
	HRESULT hresult;
	VARIANT varResult;

	VariantInit(&varResult);
	hresult = m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_GETXORIGIN],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &g_dispparamsNoArgs, &varResult, NULL, NULL);

	if(hresult != NOERROR)
	  return hresult;

	*pxorg = V_I2(&varResult);
	VariantClear(&varResult);

	return NOERROR;
}


/***
*HRESULT CRemPoly::SetXOrigin(short)
*
*Purpose:
*  Invoke the SetXOrigin method on the remote object to set the
*  XOrigin property of the polygon to the given value.
*
*Entry:
*  xorg = the new X origin
*
*Exit:
*  return value = HRESULT
*
***********************************************************************/
HRESULT
CRemPoly::SetXOrigin(short xorg)
{
	VARIANTARG varg;
	DISPPARAMS dispparams;


	V_VT(&varg) = VT_I2;
	V_I2(&varg) = xorg;

	dispparams.cArgs = 1;
	dispparams.cNamedArgs = 0;
	dispparams.rgvarg = &varg;

	return m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_SETXORIGIN],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &dispparams, NULL, NULL, NULL);
}


/***
*HRESULT CRemPoly::GetYOrigin(short*)
*
*Purpose:
*  Invoke the GetYOrigin() method on the remote object to extract
*  the current value of the YOrigin property.
*
*Entry:
*  None
*
*Exit:
*  return value = HRESULT
*
*  *pyorg = the current Y origin of the polygon
*
***********************************************************************/
HRESULT
CRemPoly::GetYOrigin(short FAR* pyorg)
{
	HRESULT hresult;
	VARIANT varResult;


	VariantInit(&varResult);
	hresult = m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_GETYORIGIN],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &g_dispparamsNoArgs, &varResult, NULL, NULL);

	if(hresult != NOERROR)
	  return hresult;

	*pyorg = V_I2(&varResult);
	VariantClear(&varResult);

	return hresult;
}


/***
*HRESULT CRemPoly::SetYOrigin(short)
*
*Purpose:
*  Invoke the SetYOrigin method on the remote object to set the
*  YOrigin property of the polygon to the given value.
*
*Entry:
*  yorg = the new Y origin
*
*Exit:
*  return value = HRESULT
*
***********************************************************************/
HRESULT
CRemPoly::SetYOrigin(short yorg)
{
	VARIANTARG varg;
	DISPPARAMS dispparams;

	V_VT(&varg) = VT_I2;
	V_I2(&varg) = yorg;

	dispparams.cArgs = 1;
	dispparams.cNamedArgs = 0;
	dispparams.rgvarg = &varg;

	return m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_SETYORIGIN],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &dispparams, NULL, NULL, NULL);
}


/***
*HRESULT CRemPoly::GetWidth(short*)
*
*Purpose:
*  Invoke the GetWidth() method on the remote object to extract
*  the current value of the line width property.
*
*Entry:
*  None
*
*Exit:
*  return value = HRESULT
*
*  *pwidth = short, the current line width of the polygon
*
***********************************************************************/
HRESULT
CRemPoly::GetWidth(short FAR* pwidth)
{
	HRESULT hresult;
	VARIANT varResult;

	VariantInit(&varResult);
	hresult = m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_GETWIDTH],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &g_dispparamsNoArgs, &varResult, NULL, NULL);

	if(hresult != NOERROR)
	  return hresult;

	*pwidth = V_I2(&varResult);
	VariantClear(&varResult);
	return NOERROR;
}


/***
*HRESULT CRemPoly::SetWidth(short)
*
*Purpose:
*  Invoke the SetWidth method on the remote object to set the
*  line width property of the polygon to the given value.
*
*Entry:
*  width = the new value for the line width property.
*
*Exit:
*  return value = HRESULT
*
***********************************************************************/
HRESULT
CRemPoly::SetWidth(short width)
{
	VARIANTARG varg;
	DISPPARAMS dispparams;


	V_VT(&varg) = VT_I2;
	V_I2(&varg) = width;

	dispparams.cArgs = 1;
	dispparams.cNamedArgs = 0;
	dispparams.rgvarg = &varg;

	return m_pdisp->Invoke(
	  m_rgdispid[IMETH_CREMPOLY_SETWIDTH],
	  IID_NULL,
	  LOCALE_USER_DEFAULT,
	  DISPATCH_METHOD,
	  &dispparams, NULL, NULL, NULL);
}


HRESULT CRemPoly::get_red(short FAR* psRed)
{
	return get_i2(m_rgdispid[IMETH_CREMPOLY_GETRED], psRed);
}

HRESULT CRemPoly::set_red(short sRed)
{
	return set_i2(m_rgdispid[IMETH_CREMPOLY_SETRED], sRed);
}


HRESULT CRemPoly::get_green(short FAR* psGreen)
{
	return get_i2(m_rgdispid[IMETH_CREMPOLY_GETGREEN], psGreen);
}

HRESULT CRemPoly::set_green(short sGreen)
{
	return set_i2(m_rgdispid[IMETH_CREMPOLY_SETGREEN], sGreen);
}


HRESULT CRemPoly::get_blue(short FAR* psBlue)
{
	return get_i2(m_rgdispid[IMETH_CREMPOLY_GETBLUE], psBlue);
}

HRESULT CRemPoly::set_blue(short sBlue)
{
	return set_i2(m_rgdispid[IMETH_CREMPOLY_SETBLUE], sBlue);
}

HRESULT
CRemPoly::get_i2(DISPID dispid, short FAR* ps)
{
	HRESULT hresult;
	VARIANT varResult;

	VariantInit(&varResult);

	hresult = m_pdisp->Invoke(
	  dispid,
	  IID_NULL,
	  LOCALE_SYSTEM_DEFAULT,
	  DISPATCH_METHOD,
	  &g_dispparamsNoArgs,
	  &varResult, NULL, NULL);

	if(hresult != NOERROR)
	  return hresult;

	hresult = VariantChangeType(&varResult, &varResult, 0, VT_I2);
	if(hresult != NOERROR){
	  VariantClear(&varResult);
	  return hresult;
	}

	*ps = V_I2(&varResult);
	VariantClear(&varResult);
	return NOERROR;
}

HRESULT
CRemPoly::set_i2(DISPID dispid, short s)
{
	VARIANTARG varg;
	DISPPARAMS dispparams;

	V_VT(&varg) = VT_I2;
	V_I2(&varg) = s;

	dispparams.cArgs = 1;
	dispparams.cNamedArgs = 0;
	dispparams.rgvarg = &varg;

	return m_pdisp->Invoke(
	  dispid,
	  IID_NULL,
	  LOCALE_SYSTEM_DEFAULT,
	  DISPATCH_METHOD,
	  &dispparams, NULL, NULL, NULL);
}


/***
*void DoPoly(CLSID)
*
*Purpose:
*  This function simply exercises our CRemPoly class by creating an
*  instance and invoking a number of its methods.
*
*Entry:
*  None
*
*Exit:
*  None
*
***********************************************************************/
STDAPI
DoPoly(CLSID clsid)
{
	HRESULT hr;
	int numpoly, i, j;

static struct {
	short x;
	short y;
} rgptPoly[] = {
	  { 25,   0}
	, { 75,   0}
	, {100,  25}
	, {100,  75}
	, { 75, 100}
	, { 25, 100}
	, {  0,  75}
	, {  0,  25}
};

static struct {
	short red;
	short green;
	short blue;
} rgrgbColors[] = {
#ifdef _MAC
	  {     0,      0,      0}
	, {     0,      0, 0x7fff}
	, {     0, 0x7fff,      0}
	, {0x7fff,      0,      0}
	, {0x7fff,      0, 0x7fff}
	, {0x7fff, 0x7fff,      0}
	, {0x7fff, 0x7fff, 0x7fff}
#else
	  {  0,   0,   0}
	, {  0,   0, 127}
	, {  0, 127,   0}
	, {127,   0,   0}
	, {127,   0, 127}
	, {127, 127,   0}
	, {127, 127, 127}
#endif
};

	CRemPoly FAR* rgprempoly[DIM(rgrgbColors)];

	numpoly = DIM(rgprempoly);

	// init
	for(i = 0; i < numpoly; ++i)
	  rgprempoly[i] = (CRemPoly FAR*)NULL;

	for(i = 0; i < numpoly; ++i){
	  hr = CRemPoly::Create(clsid, &rgprempoly[i]);
	  if(hr != NOERROR)
		goto LError0;
	  IfMac(DbPrintf("CRemPoly::Create()"));

	  for(j = 0; j < DIM(rgptPoly); ++j){
		short x = rgptPoly[j].x;
	short y = rgptPoly[j].y;
		IfMac(DbPrintf("CRemPoly::AddPoint(%d,%d)", x, y));
		hr = rgprempoly[i]->AddPoint(x, y);
	ASSERT(hr == NOERROR);
	  }

	  for(j = 0; j < DIM(rgrgbColors); ++j){
		hr = rgprempoly[i]->SetWidth(i + j);
	ASSERT(hr == NOERROR);
	IfMac(DbPrintf("CRemPoly::SetWidth()"));

		hr = rgprempoly[i]->set_red(rgrgbColors[j].red);
	ASSERT(hr == NOERROR);
	IfMac(DbPrintf("CRemPoly::set_red()"));

		hr = rgprempoly[i]->set_green(rgrgbColors[j].green);
	ASSERT(hr == NOERROR);
	IfMac(DbPrintf("CRemPoly::set_green()"));

		hr = rgprempoly[i]->set_blue(rgrgbColors[j].blue);
	ASSERT(hr == NOERROR);
	IfMac(DbPrintf("CRemPoly::set_blue()"));

		hr = rgprempoly[i]->SetXOrigin((2*i) + j << 4);
	ASSERT(hr == NOERROR);
	IfMac(DbPrintf("CRemPoly::SetXOrigin()"));

		hr = rgprempoly[i]->SetYOrigin(j << 4);
	ASSERT(hr == NOERROR);
	IfMac(DbPrintf("CRemPoly::SetYOrigin()"));

		hr = rgprempoly[i]->Draw();
	ASSERT(hr == NOERROR);
	IfMac(DbPrintf("CRemPoly::Draw()"));
	  }
	}

	hr = NOERROR;

LError0:;
	for(i = 0; i < numpoly; ++i){
	  if(rgprempoly[i] != (CRemPoly FAR*)NULL){
	rgprempoly[i]->Release();
	  }
	}

	return hr;
}
