#include "IPServer.H"

#include "Guids.H"
#include "Util.H"
#include "Globals.H"
#include "Resource.H"
#include "wsocxctl.H"
#include <WScom.h>
#include <WSCbase.h>
#include <WSDappDev.h>

#include <stdio.h>

SZTHISFILE
extern void DisplayAssert(char*,char*,char*,UINT);
WCHAR wszImage [] = L"WSocx";

static VARTYPE rgMouse[] = { VT_I2, VT_I2, VT_I4, VT_I4 };
enum { MouseMove=0, MouseDown, MouseUp, Click, DblClick };

#define WS_CALL1
#define WS_CALL2
#define WS_CALL3
#define WS_CALL4

static EVENTINFO rgEvents [] = {
    { DISPID_MOUSEMOVE, 4, rgMouse },
    { DISPID_MOUSEDOWN, 4, rgMouse },
    { DISPID_MOUSEUP, 4, rgMouse },
    { DISPID_CLICK, 0, NULL },
    { DISPID_DBLCLICK, 0, NULL },
};

const GUID *rgWSocxPropPages [] = {
    &CLSID_WSocxGeneralPage
};

WSClistData _instance_list;
WSClistData _base_list;

extern "C" {
extern void _ws_initialize(void*);
};

#pragma warning(disable:4355)  // using 'this' in constructor
WSDocxControl::WSDocxControl(IUnknown *pUnkOuter)
        :COleControl(pUnkOuter, OBJECT_TYPE_CTLWSOCX, (IDispatch *)this){
dbprintf("WSDocxControl::WSDocxControl %s:%d start\n",__FILE__,__LINE__);
#ifdef WS_CALL1
static long initialized = FALSE;
  if (initialized == FALSE){
    _ws_initialize(NULL);
    initialized = TRUE;
  }
  _instance_list.add((void*)this);
  _base_list.add((void*)NULL);
#endif
dbprintf("WSDocxControl::WSDocxControl %s:%d done\n",__FILE__,__LINE__);
}
#pragma warning(default:4355)  // using 'this' in constructor

WSDocxControl::~WSDocxControl(){
dbprintf("WSDocxControl::~WSDocxControl %s:%d start\n",__FILE__,__LINE__);
dbprintf("WSDocxControl::~WSDocxControl %s:%d done\n",__FILE__,__LINE__);
}

BOOL WSDocxControl::RegisterClassData(){
dbprintf("WSDocxControl::RegisterClassData %s:%d start\n",__FILE__,__LINE__);
    WNDCLASS wndclass;
    memset(&wndclass, 0, sizeof(WNDCLASS));
    wndclass.style          = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_OWNDC;
//    wndclass.lpfnWndProc    = COleControl::ControlWindowProc;
extern LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
    wndclass.lpfnWndProc    = WndProc;
    wndclass.hInstance      = g_hInstance;
    wndclass.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
    wndclass.lpszClassName  = WNDCLASSNAMEOFCONTROL(OBJECT_TYPE_CTLWSOCX);

dbprintf("WSDocxControl::RegisterClassData %s:%d done\n",__FILE__,__LINE__);
    return RegisterClass(&wndclass);
}

HRESULT WSDocxControl::InternalQueryInterface(REFIID  riid,void  **ppvObjOut){
dbprintf("WSDocxControl::InternalQueryInterface %s:%d start\n",__FILE__,__LINE__);
    IUnknown *pUnk;

    *ppvObjOut = NULL;
dbprintf("WSDocxControl::InternalQueryInterface %s:%d here1\n",__FILE__,__LINE__);
    if (DO_GUIDS_MATCH(riid, IID_IWSocx)) {
        pUnk = (IUnknown *)(IWSocx *)this;
    } else{
dbprintf("WSDocxControl::InternalQueryInterface %s:%d here4 this=0x%x\n",__FILE__,__LINE__,this);
        HRESULT ret = COleControl::InternalQueryInterface(riid, ppvObjOut);
dbprintf("WSDocxControl::InternalQueryInterface %s:%d done\n",__FILE__,__LINE__);
        return ret;
    }

dbprintf("WSDocxControl::InternalQueryInterface %s:%d here2\n",__FILE__,__LINE__);
    pUnk->AddRef();
dbprintf("WSDocxControl::InternalQueryInterface %s:%d here3\n",__FILE__,__LINE__);
    *ppvObjOut = (void *)pUnk;
dbprintf("WSDocxControl::InternalQueryInterface %s:%d done2\n",__FILE__,__LINE__);
    return S_OK;
}

STDMETHODIMP WSDocxControl::LoadTextState( IPropertyBag *pPropertyBag,IErrorLog    *pErrorLog){
dbprintf("WSDocxControl::LoadTextState %s:%d start\n",__FILE__,__LINE__);
	VARIANT v;
	HRESULT hr;
#if 0
	VariantInit(&v);    

	v.vt = VT_I2;
	hr = pPropertyBag->Read(L"Number",&v,pErrorLog);
	if (SUCCEEDED(hr))
		m_number = (enumWSocxNumber) v.iVal;
	ValidateNumber();
	VariantClear(&v);

	v.vt = VT_I2;
	hr = pPropertyBag->Read(L"Suite",&v,pErrorLog);
	if (SUCCEEDED(hr))
		m_suite = (enumWSocxSuite) v.iVal;
	ValidateSuite();
	VariantClear(&v);

	v.vt = VT_BOOL;
	hr = pPropertyBag->Read(L"Invert",&v,pErrorLog);
	if (SUCCEEDED(hr))
		m_invert = v.boolVal;
	VariantClear(&v);

	v.vt = VT_I2;
	hr = pPropertyBag->Read(L"WSocxAlignment",&v,pErrorLog);
	if (SUCCEEDED(hr))
		m_cardalignment = (enumWSocxAlignment) v.iVal;
	ValidateWSocxAlignment();
	VariantClear(&v);
#endif
dbprintf("WSDocxControl::LoadTextState %s:%d done\n",__FILE__,__LINE__);
    return S_OK;
}

#define		STREAMHDR_MAGIC		1296924267
STDMETHODIMP WSDocxControl::LoadBinaryState(IStream *pStream){
dbprintf("WSDocxControl::LoadBinaryState %s:%d start\n",__FILE__,__LINE__);
    HRESULT hr;
	DWORD sh;
#if 0
	hr = pStream->Read(&sh, sizeof(sh), NULL);
	RETURN_ON_FAILURE(hr);

	if (sh != STREAMHDR_MAGIC )
		return E_UNEXPECTED;

	hr = pStream->Read(&(m_number),sizeof(m_number),NULL);
	RETURN_ON_FAILURE(hr);
	ValidateNumber();

	hr = pStream->Read(&(m_suite),sizeof(m_suite),NULL);
	RETURN_ON_FAILURE(hr);
	ValidateSuite();

	hr = pStream->Read(&(m_invert),sizeof(m_invert),NULL);
	RETURN_ON_FAILURE(hr);

	hr = pStream->Read(&(m_cardalignment),sizeof(m_cardalignment),NULL);
	RETURN_ON_FAILURE(hr);
	ValidateWSocxAlignment();
#endif
dbprintf("WSDocxControl::LoadBinaryState %s:%d done\n",__FILE__,__LINE__);
	return S_OK;
}

STDMETHODIMP WSDocxControl::SaveTextState(IPropertyBag *pPropertyBag,BOOL fWriteDefaults){
	VARIANT v;
	HRESULT hr;
#if 0
	if( m_number != DefaultNumber )
	{
		VariantInit(&v);
		v.vt = VT_I2;
		v.iVal = m_number;
		hr = pPropertyBag->Write( L"Number", &v);
	}

	if( m_suite != DefaultSuite )
	{
		VariantInit(&v);
		v.vt = VT_I2;
		v.iVal = m_suite;
		hr = pPropertyBag->Write( L"Suite", &v);
	}

	if( m_invert != DefaultInvert )
	{
		VariantInit(&v);
		v.vt = VT_BOOL;
		v.boolVal = m_invert;
		hr = pPropertyBag->Write( L"Invert", &v);
	}

	if( m_cardalignment != DefaultWSocxAlignment )
	{
		VariantInit(&v);
		v.vt = VT_I2;
		v.iVal = m_cardalignment;
		hr = pPropertyBag->Write( L"WSocxAlignment", &v);
	}
#endif
	return S_OK;
}

STDMETHODIMP WSDocxControl::SaveBinaryState(IStream *pStream){
    HRESULT hr;
    DWORD sh = STREAMHDR_MAGIC;    
#if 0
    // write out the stream hdr.
    //
    hr = pStream->Write(&sh, sizeof(sh), NULL);
    RETURN_ON_FAILURE(hr);

    hr = pStream->Write(&(m_number),sizeof(m_number),NULL);
    RETURN_ON_FAILURE(hr);

    hr = pStream->Write(&(m_suite),sizeof(m_suite),NULL);
    RETURN_ON_FAILURE(hr);

    hr = pStream->Write(&(m_invert),sizeof(m_invert),NULL);
    RETURN_ON_FAILURE(hr);

    hr = pStream->Write(&(m_cardalignment),sizeof(m_cardalignment),NULL);
    RETURN_ON_FAILURE(hr);
#endif
    return S_OK;
}

HRESULT WSDocxControl::OnDraw(
    DWORD    dvaspect,        
    HDC      hdcDraw,
    LPCRECTL prcBounds,
    LPCRECTL prcWBounds,
    HDC      hicTargetDevice,
    BOOL     fOptimize ){
dbprintf("WSDocxControl::OnDraw %s:%d start\n",__FILE__,__LINE__);
	int OffsetX, OffsetY;
	RECTL rcBoundsDP;
	int width, height;
	int savedDC;
	HDC drawDc;
	HBITMAP drawDcMem;
	int c;
#if 0
	if( !Windowless() && !BackBrush )
	{
		OLE_COLOR BackColor;
		COLORREF BackColorCR;
		if( !GetAmbientProperty( DISPID_AMBIENT_BACKCOLOR, VT_I4, &BackColor ) )
			BackColor = 0;
		OleTranslateColor( BackColor, NULL, &BackColorCR );
		BackBrush = CreateSolidBrush( BackColorCR );
	}
		
	rcBoundsDP = *prcBounds;
	LPtoDP( hdcDraw, (POINT *) &rcBoundsDP, 2 );

	width = rcBoundsDP.right - rcBoundsDP.left;
	height = rcBoundsDP.bottom - rcBoundsDP.top;

	switch( m_cardalignment )
	{
		case WSocxTopCenter: 
		case WSocxCenter:
		case WSocxBottomCenter:
			OffsetX = (width - CWSocxDraw::WSocxSizeX) / 2;
			break;
		case WSocxTopRight: 
		case WSocxCenterRight:
		case WSocxBottomRight:
			OffsetX = (width - CWSocxDraw::WSocxSizeX);
			break;
		default:
			OffsetX = 0;
			break;
	}
	switch( m_cardalignment )
	{
		case WSocxCenterRight:
		case WSocxCenter:
		case WSocxCenterLeft:
			OffsetY = (height - CWSocxDraw::WSocxSizeY) / 2;
			break;
		case WSocxBottomRight: 
		case WSocxBottomCenter: 
		case WSocxBottomLeft:
			OffsetY = (height - CWSocxDraw::WSocxSizeY);
			break;
		default:
			OffsetY = 0;
			break;
	}

	if( BackBrush )
		FillRect( hdcDraw, (RECT *) prcBounds, BackBrush );

	if( m_suite == WSocxBlank )
		return S_OK;

	savedDC = SaveDC( hdcDraw );

	SetMapMode( hdcDraw, MM_TEXT );
	SetWindowOrgEx( hdcDraw, -(OffsetX + rcBoundsDP.left), 
				 -(OffsetY + rcBoundsDP.top), NULL );
	SetViewportOrgEx( hdcDraw, 0, 0, NULL );

	if( width < CWSocxDraw::WSocxSizeX || height < CWSocxDraw::WSocxSizeY )
	{
		HRGN clipRgn;
		clipRgn = CreateRectRgn( rcBoundsDP.left, 
					 rcBoundsDP.top, 
					 rcBoundsDP.left + width, 
					 rcBoundsDP.top + height );
		SelectClipRgn( hdcDraw, clipRgn );
		DeleteObject( clipRgn );
	}		

	SelectObject( hdcDraw, GetStockObject( m_invert ? WHITE_PEN : BLACK_PEN ) );

	drawDc = CreateCompatibleDC( NULL );

	if( m_suite > 0 )
		c = (m_suite-1)*14 + (m_number-1);
	else
		c = (m_suite < -12) ? 56 : -m_suite + 56;

	if( !cardcacheMem[c] )
	{
		CWSocxDraw cd( (c > 55 ? CWSocxDraw::DrawBack : (int) m_number) );

	        EnterCriticalSection( &drawCritSec );
		if( !cardcacheMem[c] )
		{
			cardcacheMem[c] = cd.CreateDIBWSocx( hdcDraw );
			drawDcMem = (HBITMAP) SelectObject( drawDc, cardcacheMem[c] );	
			cd.DrawWSocx( drawDc, m_suite, m_number );
		}
		else
			drawDcMem = (HBITMAP) SelectObject( drawDc, cardcacheMem[c] );	
		LeaveCriticalSection( &drawCritSec );
	}
	else
		drawDcMem = (HBITMAP) SelectObject( drawDc, cardcacheMem[c] );	

	if( m_cardalignment != WSocxStretch )
	{
	  	BitBlt( hdcDraw, 1, 1, CWSocxDraw::WSocxSizeX-2, CWSocxDraw::WSocxSizeY-2,
		 	   drawDc, 1, 1, m_invert ? NOTSRCCOPY : SRCCOPY );
		Polyline( hdcDraw, CWSocxDraw::WSocxBorder, 
				sizeof(CWSocxDraw::WSocxBorder)/sizeof(POINT) );
	}
	else
	{
		POINT border[9];
		memcpy( border, CWSocxDraw::WSocxBorder, sizeof(border) );
		border[2].x = border[5].x = width-3;
		border[3].x = border[4].x = width-1;
		border[4].y = border[7].y = height-3;
		border[5].y = border[6].y = height-1;
		StretchBlt( hdcDraw, 1, 1, width-2, height-2,
				drawDc, 1, 1, 
				CWSocxDraw::WSocxSizeX-2,
				CWSocxDraw::WSocxSizeY-2,
				m_invert ? NOTSRCCOPY : SRCCOPY );
		Polyline( hdcDraw, border, sizeof(border)/sizeof(POINT) );
	}

	RestoreDC( hdcDraw, savedDC );

	SelectObject( drawDc, drawDcMem );
	DeleteDC( drawDc );
#endif	
dbprintf("WSDocxControl::OnDraw %s:%d done\n",__FILE__,__LINE__);
    return S_OK;
}

LRESULT WSDocxControl::WindowProc( UINT msg, WPARAM wParam, LPARAM lParam){
dbprintf("WSDocxControl::WindowProc %s:%d start\n",__FILE__,__LINE__);
    POINTS pt;
    RECT rect;
    int b, s;
    int ev = -1;
#if 0
    switch( msg )
    {
	case WM_MOUSEMOVE:
		ev = MouseMove;
		// FALL THROUGH!
	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
		if( ev == -1 )	ev = MouseDown;
		// FALL THROUGH!
	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
		if( ev == -1 )  ev = MouseUp;
		pt = MAKEPOINTS( lParam );
		OcxGetWindowRect( &rect );
		b = (wParam & 3) == 3 ? 4 : wParam & 3;
		s = (wParam >> 2) & 7;
		FireEvent( &rgEvents[ev], b, s, pt.x-rect.left, pt.y-rect.top );
		if( msg == WM_LBUTTONUP )
			FireEvent( &rgEvents[Click] );
		break;
	case WM_LBUTTONDBLCLK:
		FireEvent( &rgEvents[DblClick] );
		break;
    }
#endif
    LRESULT ret = OcxDefWindowProc(msg, wParam, lParam);
dbprintf("WSDocxControl::WindowProc %s:%d done\n",__FILE__,__LINE__);
    return ret;
}

IUnknown *WSDocxControl::Create( IUnknown *pUnkOuter ){
dbprintf("WSDocxControl::Create %s:%d start\n",__FILE__,__LINE__);
    WSDocxControl *pNew = new WSDocxControl(pUnkOuter);
dbprintf("WSDocxControl::Create %s:%d done pNew=0x%x\n",__FILE__,__LINE__,pNew);
    return pNew->PrivateUnknown();
}

void dummy_wsocxctl(){
}
extern "C" {
extern void _ws_set_window(void*);
extern WSCbase* _ws_create_window();
};

BOOL WSDocxControl::AfterCreateWindow(){
dbprintf("WSDocxControl::AfterCreateWindow() %s:%d start m_hwnd=0x%x\n",__FILE__,__LINE__,m_hwnd);
#ifdef WS_CALL2
  if (m_hwnd == NULL){
dbprintf("WSDocxControl::AfterCreateWindow() %s:%d done. error.\n",__FILE__,__LINE__);
    return FALSE;
  }
  long num = _instance_list.getNum();
  long i;
  for(i=0; i<num; i++){
    WSDocxControl* item = (WSDocxControl*)_instance_list[i];
    WSCbase* inst = (WSCbase*)_base_list[i];
    if (item == this){
      if (inst == NULL){
        _ws_set_window(m_hwnd);
        _base_list.setData(i,_ws_create_window());
        break;
      }else{
        inst->setVisible(True);
      }
    }
  }
  InvalidateRect(m_hwnd,NULL,TRUE);
  UpdateWindow(m_hwnd);
#endif
  BOOL ret = COleControl::AfterCreateWindow();
dbprintf("WSDocxControl::AfterCreateWindow() %s:%d done.ret=%d \n",__FILE__,__LINE__,ret);
  return ret;
}

void WSDocxControl::BeforeDestroyWindow(){
dbprintf("WSDocxControl::BeforeDestroyWindow %s:%d start\n",__FILE__,__LINE__);
#if 0
#ifdef WS_CALL3
  long num = _instance_list.getNum();
  long i;
  for(i=0; i<num; i++){
    WSDocxControl* item = (WSDocxControl*)_instance_list[i];
    WSCbase* client = (WSCbase*)_base_list[i];
    if (item == this){
      _instance_list.delPos(i);
      _base_list.delPos(i);
dbprintf("WSDocxControl::BeforeDestroyWindow %s:%d delete client=%s\n",__FILE__,__LINE__,client->getInstanceName());
#ifdef WS_CALL4
      delete client;
#endif
      break;
    }
  }
  COleControl::BeforeDestroyWindow();
#endif
#endif
dbprintf("WSDocxControl::BeforeDestroyWindow %s:%d done\n",__FILE__,__LINE__);
  return;
}
void WSDocxControl::AfterDestroyWindow(){
dbprintf("WSDocxControl::AfterDestroyWindow %s:%d start\n",__FILE__,__LINE__);
#ifdef WS_CALL3
  long num = _instance_list.getNum();
  long i;
  for(i=0; i<num; i++){
    WSDocxControl* item = (WSDocxControl*)_instance_list[i];
    WSCbase* client = (WSCbase*)_base_list[i];
    if (item == this){
      _instance_list.delPos(i);
      _base_list.delPos(i);
dbprintf("WSDocxControl::AfterDestroyWindow %s:%d delete client=%s\n",__FILE__,__LINE__,client->getInstanceName());
#ifdef WS_CALL4
      delete client;
#endif
      break;
    }
  }
  COleControl::AfterDestroyWindow();
#endif
dbprintf("WSDocxControl::AfterDestroyWindow %s:%d done\n",__FILE__,__LINE__);
  return;
}



