/*
 * Copyright 2012 Piotr Caban for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */
#define COBJMACROS
#define CONST_VTABLE

#include <stdio.h>
#include <assert.h>

#include "windows.h"
#include "ole2.h"
#include "mshtml.h"
#include "mshtmdid.h"
#include "initguid.h"
#include "perhist.h"
#include "docobj.h"
#include "urlmon.h"
#include "xmlparser.h"

#include "wine/test.h"

HRESULT (WINAPI *pCreateURLMoniker)(IMoniker*, LPCWSTR, IMoniker**);

static const char xmlview_html[] =
"\r\n"
"<BODY><H2>Generated HTML</H2>\r\n"
"<TABLE>\r\n"
"<TBODY>\r\n"
"<TR bgColor=green>\r\n"
"<TH>Title</TH>\r\n"
"<TH>Value</TH></TR>\r\n"
"<TR>\r\n"
"<TD>title1</TD>\r\n"
"<TD>value1</TD></TR>\r\n"
"<TR>\r\n"
"<TD>title2</TD>\r\n"
"<TD>value2</TD></TR></TBODY></TABLE></BODY>";

IHTMLDocument2 *html_doc;
BOOL loaded;

static int html_src_compare(LPCWSTR strw, const char *stra)
{
    char buf[2048], *p1;
    const char *p2;

    WideCharToMultiByte(CP_ACP, 0, strw, -1, buf, sizeof(buf), NULL, NULL);

    p1 = buf;
    p2 = stra;
    while(1) {
        while(*p1=='\r' || *p1=='\n' || *p1=='\"') p1++;
        while(*p2=='\r' || *p2=='\n') p2++;

        if(!*p1 || !*p2 || tolower(*p1)!=tolower(*p2))
            break;

        p1++;
        p2++;
    }

    return *p1 != *p2;
}

static HRESULT WINAPI HTMLEvents_QueryInterface(IDispatch *iface, REFIID riid, void **ppv)
{
    if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IDispatch, riid)) {
        *ppv = iface;
        return S_OK;
    }

    ok(0, "Unexpected call\n");
    return E_NOINTERFACE;
}

static ULONG WINAPI HTMLEvents_AddRef(IDispatch *iface)
{
    return 2;
}

static ULONG WINAPI HTMLEvents_Release(IDispatch *iface)
{
    return 1;
}

static HRESULT WINAPI HTMLEvents_GetTypeInfoCount(IDispatch *iface, UINT *pctinfo)
{
    ok(0, "unexpected call\n");
    return E_NOTIMPL;
}

static HRESULT WINAPI HTMLEvents_GetTypeInfo(IDispatch *iface, UINT iTInfo, LCID lcid,
        ITypeInfo **ppTInfo)
{
    ok(0, "unexpected call\n");
    return E_NOTIMPL;
}

static HRESULT WINAPI HTMLEvents_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *rgszNames,
        UINT cNames, LCID lcid, DISPID *rgDispId)
{
    ok(0, "unexpected call\n");
    return E_NOTIMPL;
}

static HRESULT WINAPI HTMLEvents_Invoke(IDispatch *iface, DISPID dispIdMember, REFIID riid,
        LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
        EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
    if (dispIdMember == DISPID_HTMLDOCUMENTEVENTS2_ONREADYSTATECHANGE)
    {
        static const WCHAR completeW[] = L"complete";
        HRESULT hr;
        BSTR state;

        hr = IHTMLDocument2_get_readyState(html_doc, &state);
        ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
        if (!memcmp(state, completeW, sizeof(completeW)))
            loaded = TRUE;
        SysFreeString(state);
    }

    return S_OK;
}

static const IDispatchVtbl HTMLEventsVtbl = {
    HTMLEvents_QueryInterface,
    HTMLEvents_AddRef,
    HTMLEvents_Release,
    HTMLEvents_GetTypeInfoCount,
    HTMLEvents_GetTypeInfo,
    HTMLEvents_GetIDsOfNames,
    HTMLEvents_Invoke
};

static IDispatch HTMLEvents = { &HTMLEventsVtbl };

static void test_QueryInterface(void)
{
    IUnknown *xmlview, *unk;
    IHTMLDocument *htmldoc;
    HRESULT hres;

    hres = CoCreateInstance(&CLSID_XMLView, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
            &IID_IUnknown, (void**)&xmlview);
    if(FAILED(hres)) {
        win_skip("Failed to create XMLView instance\n");
        return;
    }
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);

    hres = IUnknown_QueryInterface(xmlview, &IID_IPersistMoniker, (void**)&unk);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    IUnknown_Release(unk);

    hres = IUnknown_QueryInterface(xmlview, &IID_IPersistHistory, (void**)&unk);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    IUnknown_Release(unk);

    hres = IUnknown_QueryInterface(xmlview, &IID_IOleCommandTarget, (void**)&unk);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    IUnknown_Release(unk);

    hres = IUnknown_QueryInterface(xmlview, &IID_IOleObject, (void**)&unk);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    IUnknown_Release(unk);

    hres = IUnknown_QueryInterface(xmlview, &IID_IHTMLDocument, (void**)&htmldoc);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    hres = IHTMLDocument_QueryInterface(htmldoc, &IID_IUnknown, (void**)&unk);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    ok(unk == xmlview, "Aggregation is not working as expected\n");
    IUnknown_Release(unk);
    IHTMLDocument_Release(htmldoc);

    IUnknown_Release(xmlview);
}

static void test_Load(void)
{
    WCHAR buf[1024];
    IPersistMoniker *pers_mon;
    IConnectionPointContainer *cpc;
    IConnectionPoint *cp;
    IMoniker *mon;
    IBindCtx *bctx;
    IHTMLElement *elem;
    HRESULT hres;
    MSG msg;
    BSTR source;

    lstrcpyW(buf, L"res://");
    GetModuleFileNameW(NULL, buf+lstrlenW(buf), ARRAY_SIZE(buf)-ARRAY_SIZE(L"res://"));
    lstrcatW(buf, L"/xml/xmlview.xml");

    if(!pCreateURLMoniker) {
        win_skip("CreateURLMoniker not available\n");
        return;
    }

    hres = CoCreateInstance(&CLSID_XMLView, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
            &IID_IPersistMoniker, (void**)&pers_mon);
    if(FAILED(hres)) {
        win_skip("Failed to create XMLView instance\n");
        return;
    }
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);

    hres = IPersistMoniker_QueryInterface(pers_mon, &IID_IHTMLDocument2, (void**)&html_doc);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    hres = IPersistMoniker_QueryInterface(pers_mon, &IID_IConnectionPointContainer, (void**)&cpc);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    hres = IConnectionPointContainer_FindConnectionPoint(cpc, &IID_IDispatch, &cp);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    hres = IConnectionPoint_Advise(cp, (IUnknown*)&HTMLEvents, NULL);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    IConnectionPoint_Release(cp);
    IConnectionPointContainer_Release(cpc);

    hres = CreateBindCtx(0, &bctx);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    hres = pCreateURLMoniker(NULL, buf, &mon);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    loaded = FALSE;
    hres = IPersistMoniker_Load(pers_mon, TRUE, mon, bctx, 0);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    IBindCtx_Release(bctx);
    IMoniker_Release(mon);

    while(!loaded && GetMessageA(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessageA(&msg);
    }

    hres = IHTMLDocument2_get_body(html_doc, &elem);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    hres = IHTMLElement_get_outerHTML(elem, &source);
    ok(hres == S_OK, "Unexpected hr %#lx.\n", hres);
    ok(!html_src_compare(source, xmlview_html), "Incorrect HTML source: %s\n", wine_dbgstr_w(source));
    IHTMLElement_Release(elem);
    SysFreeString(source);

    IHTMLDocument2_Release(html_doc);
    html_doc = NULL;
    IPersistMoniker_Release(pers_mon);
}

START_TEST(xmlview)
{
    HMODULE urlmon = LoadLibraryA("urlmon.dll");
    pCreateURLMoniker = (void*)GetProcAddress(urlmon, "CreateURLMoniker");

    CoInitialize(NULL);
    test_QueryInterface();
    test_Load();
    CoUninitialize();
}