// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package walk

import (
	"syscall"
	"unsafe"
)

import (
	"time"

	"github.com/lxn/win"
)

var webViewDWebBrowserEvents2Vtbl *win.DWebBrowserEvents2Vtbl

func init() {
	AppendToWalkInit(func() {
		webViewDWebBrowserEvents2Vtbl = &win.DWebBrowserEvents2Vtbl{
			syscall.NewCallback(webView_DWebBrowserEvents2_QueryInterface),
			syscall.NewCallback(webView_DWebBrowserEvents2_AddRef),
			syscall.NewCallback(webView_DWebBrowserEvents2_Release),
			syscall.NewCallback(webView_DWebBrowserEvents2_GetTypeInfoCount),
			syscall.NewCallback(webView_DWebBrowserEvents2_GetTypeInfo),
			syscall.NewCallback(webView_DWebBrowserEvents2_GetIDsOfNames),
			syscall.NewCallback(webView_DWebBrowserEvents2_Invoke),
		}
	})
}

type webViewDWebBrowserEvents2 struct {
	win.DWebBrowserEvents2
}

func webView_DWebBrowserEvents2_QueryInterface(wbe2 *webViewDWebBrowserEvents2, riid win.REFIID, ppvObject *unsafe.Pointer) uintptr {
	// Just reuse the QueryInterface implementation we have for IOleClientSite.
	// We need to adjust object, which initially points at our
	// webViewDWebBrowserEvents2, so it refers to the containing
	// webViewIOleClientSite for the call.
	var clientSite win.IOleClientSite
	var webViewInPlaceSite webViewIOleInPlaceSite
	var docHostUIHandler webViewIDocHostUIHandler

	ptr := uintptr(unsafe.Pointer(wbe2)) -
		uintptr(unsafe.Sizeof(clientSite)) -
		uintptr(unsafe.Sizeof(webViewInPlaceSite)) -
		uintptr(unsafe.Sizeof(docHostUIHandler))

	return webView_IOleClientSite_QueryInterface((*webViewIOleClientSite)(unsafe.Pointer(ptr)), riid, ppvObject)
}

func webView_DWebBrowserEvents2_AddRef(args *uintptr) uintptr {
	return 1
}

func webView_DWebBrowserEvents2_Release(args *uintptr) uintptr {
	return 1
}

func webView_DWebBrowserEvents2_GetTypeInfoCount(args *uintptr) uintptr {
	/*	p := (*struct {
			wbe2    *webViewDWebBrowserEvents2
			pctinfo *uint
		})(unsafe.Pointer(args))

		*p.pctinfo = 0

		return S_OK*/

	return win.E_NOTIMPL
}

func webView_DWebBrowserEvents2_GetTypeInfo(args *uintptr) uintptr {
	/*	p := (*struct {
				wbe2         *webViewDWebBrowserEvents2
			})(unsafe.Pointer(args))

		    unsigned int  iTInfo,
		    LCID  lcid,
		    ITypeInfo FAR* FAR*  ppTInfo*/

	return win.E_NOTIMPL
}

func webView_DWebBrowserEvents2_GetIDsOfNames(args *uintptr) uintptr {
	/*	p := (*struct {
		wbe2      *webViewDWebBrowserEvents2
		riid      REFIID
		rgszNames **uint16
		cNames    uint32
		lcid      LCID
		rgDispId  *DISPID
	})(unsafe.Pointer(args))*/

	return win.E_NOTIMPL
}

/*
func webView_DWebBrowserEvents2_Invoke(
	wbe2 *webViewDWebBrowserEvents2,
	dispIdMember win.DISPID,
	riid win.REFIID,
	lcid uint32, // LCID
	wFlags uint16,
	pDispParams *win.DISPPARAMS,
	pVarResult *win.VARIANT,
	pExcepInfo unsafe.Pointer, // *EXCEPINFO
	puArgErr *uint32) uintptr {
*/
func webView_DWebBrowserEvents2_Invoke(
	arg0 uintptr,
	arg1 uintptr,
	arg2 uintptr,
	arg3 uintptr,
	arg4 uintptr,
	arg5 uintptr,
	arg6 uintptr,
	arg7 uintptr,
	arg8 uintptr) uintptr {

	wbe2 := (*webViewDWebBrowserEvents2)(unsafe.Pointer(arg0))
	dispIdMember := *(*win.DISPID)(unsafe.Pointer(&arg1))
	//riid := *(*win.REFIID)(unsafe.Pointer(&arg2))
	//lcid := *(*uint32)(unsafe.Pointer(&arg3))
	//wFlags := *(*uint16)(unsafe.Pointer(&arg4))
	pDispParams := (*win.DISPPARAMS)(unsafe.Pointer(arg5))
	//pVarResult := (*win.VARIANT)(unsafe.Pointer(arg6))
	//pExcepInfo := unsafe.Pointer(arg7)
	//puArgErr := (*uint32)(unsafe.Pointer(arg8))

	var wb WidgetBase
	var wvcs webViewIOleClientSite

	wv := (*WebView)(unsafe.Pointer(uintptr(unsafe.Pointer(wbe2)) +
		uintptr(unsafe.Sizeof(*wbe2)) -
		uintptr(unsafe.Sizeof(wvcs)) -
		uintptr(unsafe.Sizeof(wb))))

	switch dispIdMember {
	case win.DISPID_BEFORENAVIGATE2:
		rgvargPtr := (*[7]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		eventData := &WebViewNavigatingEventData{
			pDisp:           (*rgvargPtr)[6].MustPDispatch(),
			url:             (*rgvargPtr)[5].MustPVariant(),
			flags:           (*rgvargPtr)[4].MustPVariant(),
			targetFrameName: (*rgvargPtr)[3].MustPVariant(),
			postData:        (*rgvargPtr)[2].MustPVariant(),
			headers:         (*rgvargPtr)[1].MustPVariant(),
			cancel:          (*rgvargPtr)[0].MustPBool(),
		}
		wv.navigatingPublisher.Publish(eventData)

	case win.DISPID_NAVIGATECOMPLETE2:
		rgvargPtr := (*[2]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		url := (*rgvargPtr)[0].MustPVariant()
		urlStr := ""
		if url != nil && url.MustBSTR() != nil {
			urlStr = win.BSTRToString(url.MustBSTR())
		}
		wv.navigatedPublisher.Publish(urlStr)

		wv.urlChangedPublisher.Publish()

	case win.DISPID_DOWNLOADBEGIN:
		wv.downloadingPublisher.Publish()

	case win.DISPID_DOWNLOADCOMPLETE:
		wv.downloadedPublisher.Publish()

	case win.DISPID_DOCUMENTCOMPLETE:
		rgvargPtr := (*[2]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		url := (*rgvargPtr)[0].MustPVariant()
		urlStr := ""
		if url != nil && url.MustBSTR() != nil {
			urlStr = win.BSTRToString(url.MustBSTR())
		}

		// FIXME: Horrible hack to avoid glitch where the document is not displayed.
		time.AfterFunc(time.Millisecond*100, func() {
			wv.Synchronize(func() {
				b := wv.BoundsPixels()
				b.Width++
				wv.SetBoundsPixels(b)
				b.Width--
				wv.SetBoundsPixels(b)
			})
		})

		wv.documentCompletedPublisher.Publish(urlStr)

	case win.DISPID_NAVIGATEERROR:
		rgvargPtr := (*[5]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		eventData := &WebViewNavigatedErrorEventData{
			pDisp:           (*rgvargPtr)[4].MustPDispatch(),
			url:             (*rgvargPtr)[3].MustPVariant(),
			targetFrameName: (*rgvargPtr)[2].MustPVariant(),
			statusCode:      (*rgvargPtr)[1].MustPVariant(),
			cancel:          (*rgvargPtr)[0].MustPBool(),
		}
		wv.navigatedErrorPublisher.Publish(eventData)

	case win.DISPID_NEWWINDOW3:
		rgvargPtr := (*[5]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		eventData := &WebViewNewWindowEventData{
			ppDisp:         (*rgvargPtr)[4].MustPPDispatch(),
			cancel:         (*rgvargPtr)[3].MustPBool(),
			dwFlags:        (*rgvargPtr)[2].MustULong(),
			bstrUrlContext: (*rgvargPtr)[1].MustBSTR(),
			bstrUrl:        (*rgvargPtr)[0].MustBSTR(),
		}
		wv.newWindowPublisher.Publish(eventData)

	case win.DISPID_ONQUIT:
		wv.quittingPublisher.Publish()

	case win.DISPID_WINDOWCLOSING:
		rgvargPtr := (*[2]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		eventData := &WebViewWindowClosingEventData{
			bIsChildWindow: (*rgvargPtr)[1].MustBool(),
			cancel:         (*rgvargPtr)[0].MustPBool(),
		}
		wv.windowClosingPublisher.Publish(eventData)

	case win.DISPID_ONSTATUSBAR:
		rgvargPtr := (*[1]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		statusBar := (*rgvargPtr)[0].MustBool()
		if statusBar != win.VARIANT_FALSE {
			wv.statusBarVisible = true
		} else {
			wv.statusBarVisible = false
		}
		wv.statusBarVisibleChangedPublisher.Publish()

	case win.DISPID_ONTHEATERMODE:
		rgvargPtr := (*[1]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		theaterMode := (*rgvargPtr)[0].MustBool()
		if theaterMode != win.VARIANT_FALSE {
			wv.isTheaterMode = true
		} else {
			wv.isTheaterMode = false
		}
		wv.theaterModeChangedPublisher.Publish()

	case win.DISPID_ONTOOLBAR:
		rgvargPtr := (*[1]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		toolBar := (*rgvargPtr)[0].MustBool()
		if toolBar != win.VARIANT_FALSE {
			wv.toolBarVisible = true
		} else {
			wv.toolBarVisible = false
		}
		wv.toolBarVisibleChangedPublisher.Publish()

	case win.DISPID_ONVISIBLE:
		rgvargPtr := (*[1]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		vVisible := (*rgvargPtr)[0].MustBool()
		if vVisible != win.VARIANT_FALSE {
			wv.browserVisible = true
		} else {
			wv.browserVisible = false
		}
		wv.browserVisibleChangedPublisher.Publish()

	case win.DISPID_COMMANDSTATECHANGE:
		rgvargPtr := (*[2]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		command := (*rgvargPtr)[1].MustLong()
		enable := (*rgvargPtr)[0].MustBool()
		enableBool := (enable != win.VARIANT_FALSE)
		switch command {
		case win.CSC_UPDATECOMMANDS:
			wv.toolBarEnabled = enableBool
			wv.toolBarEnabledChangedPublisher.Publish()

		case win.CSC_NAVIGATEFORWARD:
			wv.canGoForward = enableBool
			wv.canGoForwardChangedPublisher.Publish()

		case win.CSC_NAVIGATEBACK:
			wv.canGoBack = enableBool
			wv.canGoBackChangedPublisher.Publish()
		}

	case win.DISPID_PROGRESSCHANGE:
		rgvargPtr := (*[2]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		wv.progressValue = (*rgvargPtr)[1].MustLong()
		wv.progressMax = (*rgvargPtr)[0].MustLong()
		wv.progressChangedPublisher.Publish()

	case win.DISPID_STATUSTEXTCHANGE:
		rgvargPtr := (*[1]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		sText := (*rgvargPtr)[0].MustBSTR()
		if sText != nil {
			wv.statusText = win.BSTRToString(sText)
		} else {
			wv.statusText = ""
		}
		wv.statusTextChangedPublisher.Publish()

	case win.DISPID_TITLECHANGE:
		rgvargPtr := (*[1]win.VARIANTARG)(unsafe.Pointer(pDispParams.Rgvarg))
		sText := (*rgvargPtr)[0].MustBSTR()
		if sText != nil {
			wv.documentTitle = win.BSTRToString(sText)
		} else {
			wv.documentTitle = ""
		}
		wv.documentTitleChangedPublisher.Publish()
	}

	return win.DISP_E_MEMBERNOTFOUND
}
