#include "stdafx.h"
#undef print
#include "WebCtrl.h"
#include <Mshtml.h>
#include "utilities.h"

CWebContainer::CWebContainer( HWND hwnd )
{
   m_cRef = 0;
   m_hwnd = hwnd;
   m_punk = NULL;

   SetRectEmpty( &m_rect );

   m_pEvent = new CWebEventSink();

   m_pEvent->AddRef();
}

CWebContainer::~CWebContainer()
{
   m_pEvent->Release();

   m_punk->Release();
}

void CWebContainer::add()
{
   CLSID clsid;

   CLSIDFromString( L"Shell.Explorer", &clsid );

   CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IUnknown, (PVOID *) &m_punk );

   IOleObject *pioo;

   m_punk->QueryInterface( IID_IOleObject, (PVOID *) &pioo );

   pioo->SetClientSite( this );

   pioo->Release();

   IPersistStreamInit *ppsi;

   m_punk->QueryInterface( IID_IPersistStreamInit, (PVOID *) &ppsi );

   ppsi->InitNew();
   ppsi->Release();

   ConnectEvents();

   setVisible( true );

   setFocus( true );
}

/**************************************************************************

   CWebContainer::remove()

**************************************************************************/

void CWebContainer::remove()
{
   IOleObject *pioo;
   
   m_punk->QueryInterface( IID_IOleObject, (PVOID *) &pioo );

   pioo->Close( OLECLOSE_NOSAVE );

   pioo->SetClientSite( NULL );

   pioo->Release();

   IOleInPlaceObject *pipo;

   m_punk->QueryInterface( IID_IOleInPlaceObject, (PVOID *) &pipo );

   pipo->UIDeactivate();

   pipo->InPlaceDeactivate();

   pipo->Release();

   DisconnectEvents();
}

/**************************************************************************

   CWebContainer::setLocation()

**************************************************************************/

void CWebContainer::setLocation( int x, int y, int cx, int cy )
{
   m_rect.left   = x;
   m_rect.top    = y;
   m_rect.right  = cx;
   m_rect.bottom = cy;

   IOleInPlaceObject *pipo;

   m_punk->QueryInterface( IID_IOleInPlaceObject, (PVOID *) &pipo );

   pipo->SetObjectRects( &m_rect, &m_rect );

   pipo->Release();
}

   HRESULT STDMETHODCALLTYPE CWebContainer::Scroll( SIZE sz )
   {

    HRESULT hr;

    // get the document dispatch from browser
	IWebBrowser2 * pwb;
	getWeb(&pwb);
    IDispatch *pDisp;
	pwb->get_Document(&pDisp);
    //ASSERT( pDisp ); //if NULL, we failed
    
    // get document interface
    IHTMLDocument2 *pDocument = NULL;
    hr = pDisp->QueryInterface( IID_IHTMLDocument2, (void**)&pDocument );
    //ASSERT( SUCCEEDED( hr ) );
    //ASSERT( pDocument );

    //
    // this is the trick! 
    // take the body element from document...
    //
    IHTMLElement *pBody = NULL;
    hr = pDocument->get_body( &pBody );
	if(pBody==NULL) return S_OK; // no body so nothing to scroll
    //ASSERT( SUCCEEDED( hr ) );
    //ASSERT( pBody );

    // from body we can get element2 interface, which allows us to do scrolling
    IHTMLElement2 *pElement = NULL;
    hr = pBody->QueryInterface(IID_IHTMLElement2,(void**)&pElement);
    //ASSERT(SUCCEEDED(hr));
    //ASSERT( pElement );

    // now we are ready to scroll

    pElement->put_scrollTop( sz.cy ); // scroll down to sz.cy pixels from top
	pElement->put_scrollLeft( sz.cx ); // scroll right to sz.cx pixel from left

	pElement->Release();
	pBody->Release();
	pDocument->Release();
	pDisp->Release();
	pwb->Release();

	return S_OK;
   }

/**************************************************************************

   CWebContainer::setVisible()

**************************************************************************/

void CWebContainer::setVisible( bool bVisible )
{
   IOleObject *pioo;

   m_punk->QueryInterface( IID_IOleObject, (PVOID *) &pioo );

   if ( bVisible )
   {
      pioo->DoVerb( OLEIVERB_INPLACEACTIVATE, NULL, this, 0, m_hwnd, &m_rect );
      pioo->DoVerb( OLEIVERB_SHOW, NULL, this, 0, m_hwnd, &m_rect );
   }
   else
      pioo->DoVerb( OLEIVERB_HIDE, NULL, this, 0, m_hwnd, NULL );

   pioo->Release();
}

/**************************************************************************

   CWebContainer::setFocus()

**************************************************************************/

void CWebContainer::setFocus( bool bFocus )
{
   IOleObject *pioo;

   if ( bFocus )
   {
      m_punk->QueryInterface( IID_IOleObject, (PVOID *) &pioo );

      pioo->DoVerb( OLEIVERB_UIACTIVATE, NULL, this, 0, m_hwnd, &m_rect );

      pioo->Release();
   }
}

void CWebContainer::ConnectEvents()
{
   IConnectionPoint *pcp = GetConnectionPoint( DIID_DWebBrowserEvents2 );

   pcp->Advise( m_pEvent, &m_eventCookie );

   pcp->Release();
}

void CWebContainer::DisconnectEvents()
{
   IConnectionPoint *pcp;

   pcp = GetConnectionPoint( DIID_DWebBrowserEvents2 );

   pcp->Unadvise( m_eventCookie );

   pcp->Release();
}

IConnectionPoint* CWebContainer::GetConnectionPoint( REFIID riid )
{
   IConnectionPointContainer *pcpc;
   
   m_punk->QueryInterface( IID_IConnectionPointContainer, (PVOID *) &pcpc );

   IConnectionPoint *pcp;

   pcpc->FindConnectionPoint( riid, &pcp );

   pcpc->Release();

   return pcp;
}

HRESULT CWebContainer::getWeb( IWebBrowser2 **ppweb )
{
   return m_punk->QueryInterface( IID_IWebBrowser2, (PVOID *) ppweb );
}

// WebCtrl

CWebCtrl::CWebCtrl( HWND hwnd )
{
   m_hwnd = hwnd;
   m_cxScroll = GetSystemMetrics( SM_CXHSCROLL ) + 2;
   m_cyScroll = GetSystemMetrics( SM_CYVSCROLL ) + 2;
}

static LRESULT CALLBACK WebCtrlProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
   CWebCtrl *pthis = (CWebCtrl *) GetProp( hwnd, THIS_PROP );

   if ( !pthis )
   {
      if ( uMsg != WM_CREATE )
         return DefWindowProc( hwnd, uMsg, wParam, lParam );

      pthis = new CWebCtrl( hwnd );

      SetProp( hwnd, THIS_PROP, (HANDLE) pthis );
   }

   LRESULT lResult = pthis->WebCtrlProc( uMsg, wParam, lParam );

   if ( uMsg == WM_DESTROY )
   {
      RemoveProp( hwnd, THIS_PROP );
      delete pthis;
   }

   return lResult;
}

void CWebCtrl::Navigate( LPTSTR psz )
{
   if ( !psz )
      return;

   if ( !*psz )
      return;

   int len = lstrlen( psz ) + 1;

   WCHAR *pszW;

#ifndef UNICODE

   pszW = new WCHAR[len];

   MultiByteToWideChar( CP_ACP, 0, psz, -1, pszW, len );

#else

   pszW = psz;

#endif

   VARIANT v;

   VariantInit( &v );

   v.vt = VT_BSTR;
   v.bstrVal = SysAllocString( pszW );

#ifndef UNICODE

   delete []pszW;

#endif

   m_pweb->Navigate2( &v, NULL, NULL, NULL, NULL );

   VariantClear( &v );
}

void CWebCtrl::Scroll(int x, int y)
{
	
	SIZE sz;
	sz.cx = x;
	sz.cy = y;
	m_pContainer->Scroll(sz);
	m_pContainer->setLocation(0,0,width,height);
	
}

void CWebCtrl::SetHTML( const TCHAR * html)
{
	
}

void CWebCtrl::AppendHTML( const TCHAR * html)
{
	IHTMLDocument2 *document = 0; 
	HRESULT hresult = S_OK;
	VARIANT *param = 0;
	SAFEARRAY *sfArray = 0;
	BSTR bstr = SysAllocString(html);

	IDispatch * pd = 0;
	m_pweb->get_Document(&pd);
	pd->QueryInterface(IID_IHTMLDocument2,(void **)&document);


	// Creates a new one-dimensional array
	sfArray = SafeArrayCreateVector(VT_VARIANT, 0, 1);
	
	if (sfArray == NULL || document == NULL) {
		goto cleanup;
	}

	hresult = SafeArrayAccessData(sfArray,(LPVOID*) & param);
	param->vt = VT_BSTR;
	param->bstrVal = bstr;
	hresult = SafeArrayUnaccessData(sfArray);
	hresult = document->writeln(sfArray);

	
	SIZE sz;
	sz.cx = -100;
	sz.cy = -100;
	m_pContainer->Scroll(sz);
	
	/*
	INPUT keys[4];
	HWND h,before = GetFocus();
	m_pweb->get_HWND((SHANDLE_PTR*)&h);
	SetFocus(h);
	

	keys[0].type = INPUT_KEYBOARD;
	ZeroMemory(&(keys[0].ki),sizeof(KEYBDINPUT));
	keys[0].ki.wVk = VK_CONTROL;
	keys[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;

	keys[1].type = INPUT_KEYBOARD;
	ZeroMemory(&(keys[1].ki),sizeof(KEYBDINPUT));
	keys[1].ki.wVk = VK_END;

	keys[2].type = INPUT_KEYBOARD;
	ZeroMemory(&(keys[2].ki),sizeof(KEYBDINPUT));
	keys[2].ki.wVk = VK_END;
	keys[2].ki.dwFlags = KEYEVENTF_KEYUP;

	keys[3].type = INPUT_KEYBOARD;
	ZeroMemory(&(keys[3].ki),sizeof(KEYBDINPUT));
	keys[3].ki.wVk = VK_CONTROL;
	keys[3].ki.dwFlags = KEYEVENTF_KEYUP|KEYEVENTF_EXTENDEDKEY;

	UINT sent = SendInput(4,keys,sizeof(INPUT));
	SetFocus(before);
	*/

cleanup:
	SysFreeString(bstr);
	if (sfArray != NULL) {
		SafeArrayDestroy(sfArray);
	}
	document->Release();
}

void CWebCtrl::InsertHTML( const TCHAR * html)
{
	IHTMLDocument2 *document = 0; 
	HRESULT hresult = S_OK;
	BSTR bstr = SysAllocString(html);

	IDispatch * pd = 0;
	m_pweb->get_Document(&pd);
	pd->QueryInterface(IID_IHTMLDocument2,(void **)&document);

	BSTR state;
	document->get_readyState(&state);
	//while(_tcscmp(state,L"complete")!=0){
	//	DoEvents();
	//	document->get_readyState(&state);
	//}
	IHTMLElement *pBody = NULL;
	document->get_body( &pBody );
	if(pBody==NULL) goto cleanup; // no body so nothing to scroll
	pBody->insertAdjacentHTML(L"beforeEnd",(BSTR)bstr);
	pBody->Release();



cleanup:
	SysFreeString(bstr);
	document->Release();
}


void CWebCtrl::SetText(const TCHAR * html)
{
	SafeSendMessage(m_hwnd,WC_SET,0,(LPARAM)html);
}

void CWebCtrl::Write(const TCHAR * html)
{
	SafeSendMessage(m_hwnd,WC_APPEND,0,(LPARAM)html);
}

void CWebCtrl::Clear()
{
	SafeSendMessage(m_hwnd,WM_SETTEXT,0,(LPARAM)L"about:blank");
}

LRESULT CWebCtrl::WebCtrlProc( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
   LRESULT lResult = 0;

   switch ( uMsg )
   {
      case WM_CREATE:

         m_pContainer = new CWebContainer( m_hwnd );

         m_pContainer->AddRef();

         m_pContainer->add();

         m_pContainer->getWeb( &m_pweb );

		 m_pweb->put_RegisterAsDropTarget(false);

         CREATESTRUCT *pcs;

         pcs = (CREATESTRUCT *) lParam;

         Navigate( (LPTSTR) pcs->lpszName );

         break;

      case WM_SETTEXT:

         Navigate( (LPTSTR) lParam );

         break;

	  case WC_APPEND:
		  {
		  AppendHTML((LPTSTR) lParam);
		  }
		  break;
	  case WC_SET:
		  SetHTML((LPTSTR) lParam);
		  break;
	  case WC_INSERT:
		  InsertHTML((LPTSTR) lParam);
		  break;
	  case WC_SCROLL:
		  Scroll((int)wParam,(int)lParam);
		  break;
      case WM_SIZE:

         m_pContainer->setLocation( 0, 0, LOWORD(lParam)/* + m_cxScroll*/, HIWORD(lParam) /*+ m_cyScroll*/ );
		width = LOWORD(lParam);
		height = HIWORD(lParam);
         break;

      case WM_DESTROY:

         m_pweb->Release();

         m_pContainer->remove();

         m_pContainer->Release();

         break;

      default:

         lResult = DefWindowProc( m_hwnd, uMsg, wParam, lParam );

         break;
   }

   return lResult;
}

bool WINAPI InitWebCtrl()
{
   CoInitialize( NULL );

   WNDCLASS wc;

   HINSTANCE hinst = GetModuleHandle( NULL );

   if ( !GetClassInfo( hinst, WC_WEBCTRL, &wc ) )
   {
      memset( &wc, 0, sizeof(wc) );

      wc.style         = CS_DBLCLKS | CS_GLOBALCLASS | CS_NOCLOSE;
      wc.lpfnWndProc   = ::WebCtrlProc;
      wc.hInstance     = hinst;
      wc.hCursor       = LoadCursor( NULL, IDC_ARROW );
      wc.lpszClassName = WC_WEBCTRL;

      return RegisterClass( &wc ) ? true : false;
   }
   
   return true;
}

bool WINAPI UninitWebCtrl()
{
   WNDCLASS wc;

   bool bResult = false;

   if ( GetClassInfo( GetModuleHandle( NULL ), WC_WEBCTRL, &wc ) )
      bResult = UnregisterClass( WC_WEBCTRL, wc.hInstance ) ? true : false;

   CoUninitialize();

   return bResult;
}

#ifdef WEBCTRL_AUTOINIT

HWND WINAPI CreateWebControl( LPCTSTR pszText, DWORD dwStyles, int x, int y, int cx, int cy, HWND hwndParent, UINT uId )
{
   return CreateWindow( WC_WEBCTRL, pszText, dwStyles, x, y, cx, cy, hwndParent, (HMENU) uId, GetModuleHandle( NULL ), NULL );
}

static class AutoInit
{
public:

   AutoInit()
   {
      InitWebCtrl();
   }

   ~AutoInit()
   {
      UninitWebCtrl();
   }

} a;

#endif