/* Copyright (c) Microsoft Corporation. All rights reserved. */

/* Handle various WS-* headers */

#include <mmlite.h>
#include <web/cont.h>
#include "ws.h"
#include <base/cobdesc.h>

typedef struct _WsAddressing {
    //CONST_VTBL struct IWsAddressingVtbl *v;

    struct _DefCob super;

    CONST_VTBL struct IWsEventingVtbl *wsev;
    CONST_VTBL struct IWsManagementVtbl *wsmanv;
    CONST_VTBL struct IWsDeviceProfileVtbl *wsdpv;
    CONST_VTBL struct IWsDsspVtbl *dsv;
} WsAddressing;

#include <s_WsAddressing_v.c>

/* Convert between interface and instance pointer and vice versa */
#define pwsa(_i_) ((WsAddressing *)((_i_)))
#define iwsa(_p_) ((PIWSADDRESSING)((_p_)))
#define pwse(_i_) ((WsAddressing *)((_i_) ? (ADDRESS)(_i_) - offsetof(struct _WsAddressing, wsev) : 0))
#define iwse(_p_) ((PIWSEVENTING)((_p_) ? &(_p_)->wsev : NULL))
#define pwsman(_i_) ((WsAddressing *)((_i_) ? (ADDRESS)(_i_) - offsetof(struct _WsAddressing, wsmanv) : 0))
#define iwsman(_p_) ((PIWSMANAGEMENT)((_p_) ? &(_p_)->wsmanv : NULL))
#define pwsdp(_i_) ((WsAddressing *)((_i_) ? (ADDRESS)(_i_) - offsetof(struct _WsAddressing, wsdpv) : 0))
#define iwsdp(_p_) ((PIWSDEVICEPROFILE)((_p_) ? &(_p_)->wsdpv : NULL))
#define pdssp(_i_) ((WsAddressing *)((_i_) ? (ADDRESS)(_i_) - offsetof(struct _WsAddressing, dsv) : 0))
#define idssp(_p_) ((PIWSDEVICEPROFILE)((_p_) ? &(_p_)->dsv : NULL))

PRIVATE SCODE wsaQueryInterface (
    PIWSADDRESSING iThis,
    REFIID iid,
    /*out*/ void** ppObject)
{
    SCODE sc;
    sc = GenericQueryInterface((IUnknown *)iThis, iid, ppObject,
                               &IID_IWsAddressing);
    if ((sc == E_NO_INTERFACE) && UuidCmp(&IID_IWsEventing, iid)) {
        iThis->v->AddRef(iThis);
        *ppObject = (void*) iwse(pwsa(iThis));
        sc = S_OK;
    }

    if ((sc == E_NO_INTERFACE) && UuidCmp(&IID_IWsManagement, iid)) {
        iThis->v->AddRef(iThis);
        *ppObject = (void*) iwsman(pwsa(iThis));
        sc = S_OK;
    }

    if ((sc == E_NO_INTERFACE) && UuidCmp(&IID_IWsDeviceProfile, iid)) {
        iThis->v->AddRef(iThis);
        *ppObject = (void*) iwsdp(pwsa(iThis));
        sc = S_OK;
    }

    if ((sc == E_NO_INTERFACE) && UuidCmp(&IID_IDssp, iid)) {
        iThis->v->AddRef(iThis);
        *ppObject = (void*) idssp(pwsa(iThis));
        sc = S_OK;
    }

    return sc;
}

#if 0
PRIVATE UINT wsaAddRef (
    PIWSADDRESSING pThis)
{
    return AtomicInc(&pwsa(pThis)->RefCnt);
}

PRIVATE UINT wsaRelease (
    PIWSADDRESSING pThis)
{
    WsAddressing * This = pwsa(pThis);
    UINT Refs = AtomicDec(&This->RefCnt);
    if (Refs == 0)
        WsAddressingDestroy((IUnknown *) This);

    return Refs;
}
#endif

PRIVATE SCODE WsAddressingDestroy(IUnknown *iThis)
{
    return S_OK;
}

/* Memory management */

PRIVATE PWSCONTEXT WsContextNew()
{
    PIHEAP pHeap = CurrentHeap();
    PWSCONTEXT pCtx = (PWSCONTEXT) pHeap->v->
        Alloc(pHeap,
              HEAP_ZERO_MEMORY | HEAP_DEBUG_NAME("wsaddr.c:WsContextNew"), 
              sizeof(WSCONTEXT), 0);
//    DBG(("wsaddr.c:WsContextNew: ctx=0x%x\n", pCtx));

    return pCtx;
}

void WsContextDestroy(PWSCONTEXT pCtx)
{
    PIHEAP Heap = CurrentHeap();

    if (pCtx->To)
        WsEndpointDelete(pCtx->To, Heap);
    if (pCtx->Action)
        Heap->v->Free(Heap, 0, pCtx->Action);
    if (pCtx->ReplyTo)
        WsEndpointDelete(pCtx->ReplyTo, Heap);
    if (pCtx->RelatesTo)
        Heap->v->Free(Heap, 0, pCtx->RelatesTo);
    if (pCtx->taskName)
        Heap->v->Free(Heap, 0, pCtx->taskName);
    if (pCtx->ReservationName)
        Heap->v->Free(Heap, 0, pCtx->ReservationName);
    if (pCtx->TaskName)
        Heap->v->Free(Heap, 0, pCtx->TaskName);
    if (pCtx->MessageId)
        Heap->v->Free(Heap, 0, pCtx->MessageId);
    Heap->v->Free(Heap, 0, pCtx);
}

/* WS-Addressing header field processing */

#define WSA_INIT \
    PCONTINFO cont = pCONT(iCont); \
    PWSCONTEXT *ppCtx = &cont->pContext; \
    do if (!*ppCtx) { \
        *ppCtx = WsContextNew(); \
        if ((*ppCtx)->MessageId) \
            return E_NOT_ENOUGH_MEMORY; \
    } while(0)


/* Writes the parameter message identifier into the context structure. */
PRIVATE SCODE MCT wsaMessageID(
    /*in*/ PIWSADDRESSING pThis,
    PICONTINUATION iCont,
    /*in*/ const _TCHAR * MessageId)
{
    WSA_INIT;

    if ((*ppCtx)->MessageId)
        return E_FAIL;

    (*ppCtx)->MessageId = _tcsdup(MessageId);
    if (!(*ppCtx)->MessageId) 
        return E_NOT_ENOUGH_MEMORY;

//    DBG(("wsaddr.c:WsAddressingMessageId: pRpc->pCtx->MessageId=%s\n", 

//         (*ppCtx)->MessageId));


    return S_OK;
}

PRIVATE SCODE MCT wsaTo(
    /*in*/ PIWSADDRESSING pThis,
    PICONTINUATION iCont,
    /*in*/ const _TCHAR * Address)
{
    WSA_INIT;

    if ((*ppCtx)->To)
        return E_FAIL;

    /* Support for WS-Discovery special URI */
    if (!_tcscmp(Address, _T("urn:schemas-xmlsoap-org:ws:2004:10:discovery"))
        || !_tcscmp(Address,
                    _T("urn:schemas-xmlsoap-org:ws:2005:04:discovery"))
        || (!_tcscmp(Address,
            _T("http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous")))) // XXX

        Address = _T("http://0.0.0.0/COB/wsdiscov.cob");

    (*ppCtx)->To = WsEndpointNew(Address);

    if (cont->MayOverrideAddr) {
        /* The arg has RPC lifetime (like cont) --> no need to copy */
        cont->To = Address;
    } else {
        //verify that new Address matches To or Reply-To in some loose way...;

    }

    return S_OK;
}

PRIVATE SCODE MCT wsaAction(
    /*in*/ PIWSADDRESSING pThis,
    PICONTINUATION iCont,
    /*in*/ const _TCHAR * Action)
{
    WSA_INIT;

    if ((*ppCtx)->Action)
        return E_FAIL;

    (*ppCtx)->Action = _tcsdup(Action);
    if (!(*ppCtx)->Action)
        return E_NOT_ENOUGH_MEMORY;

    return S_OK;
}

PRIVATE SCODE MCT wsaRelatesTo(
    /*in*/ PIWSADDRESSING pThis,
    PICONTINUATION iCont,
    /*in*/ const _TCHAR *Relates)
{
    WSA_INIT;

    if ((*ppCtx)->RelatesTo)
        return E_FAIL;

    (*ppCtx)->RelatesTo = _tcsdup(Relates);
    if (!(*ppCtx)->RelatesTo)
        return E_NOT_ENOUGH_MEMORY;

    return S_OK;
}

PRIVATE SCODE MCT wsaReplyTo(
    /*in*/ PIWSADDRESSING pThis,
    PICONTINUATION iCont,
    /*in*/ const _TCHAR * Address)
{
    WSA_INIT;

    if ((*ppCtx)->ReplyTo)
        return E_FAIL;

#if 0
    /* Treat the special anonymous value the same as empty */
    if (!_tcscmp(Address,
        _T("http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous")))
        return S_OK;
#endif

    (*ppCtx)->ReplyTo = WsEndpointNew(Address);
    return S_OK;
}

PRIVATE SCODE MCT wsaFrom(
    /*in*/ PIWSADDRESSING pThis,
    PICONTINUATION iCont,
    /*in*/ const _TCHAR * Address)
{
    /* Ignore it */
    return S_OK;
}

PRIVATE SCODE wseQueryInterface (
    PIWSEVENTING iThis,
    REFIID iid,
    /*out*/ void** ppObject)
{
    IUnknown *unk = (IUnknown *) pwse(iThis);
    return unk->v->QueryInterface(unk, iid, ppObject);
}

PRIVATE UINT wseAddRef (
    PIWSEVENTING iThis)
{
    IUnknown *unk = (IUnknown *) pwse(iThis);
    return unk->v->AddRef(unk);
}

PRIVATE UINT wseRelease (
    PIWSEVENTING iThis)
{
    IUnknown *unk = (IUnknown *) pwse(iThis);
    return unk->v->Release(unk);
}

PRIVATE SCODE wseIdentifier (
    PIWSEVENTING iThis,
    PICONTINUATION iCont,
    const _TCHAR * Value)
{
    WSA_INIT;

    if ((*ppCtx)->Identifier)
        return E_FAIL;

    (*ppCtx)->Identifier = _tcsdup(Value);
    if (!(*ppCtx)->Identifier) 
        return E_NOT_ENOUGH_MEMORY;

    return S_OK;
}

PRIVATE SCODE wseSubscribe (
    PIWSEVENTING iThis,
    PEndpointReference_QUIET NotifyTo,
    PEndpointReference_QUIET EndTo,
    const _TCHAR *Filter,
    PDelivery DeliveryList,
    PConnectionRetry ConnectionRetry,
    /*out*/ PEndpointReference_QUIET SubscriptionManager,
    /*out*/ _TCHAR * EnumerationContext, 
    UINT EnumerationContextSize,
   /*out*/ TIME *Expires)
{
    WsAddressing * This = pwse(iThis);

    UnusedParameter(This);
    UnusedParameter(EndTo);
    UnusedParameter(DeliveryList);
    UnusedParameter(SubscriptionManager);
    UnusedParameter(EnumerationContext);
    UnusedParameter(Expires);

    return E_NOT_IMPLEMENTED;
}

PRIVATE SCODE wseRenew (
    PIWSEVENTING iThis,
    TIME Expires)
{
    WsAddressing * This = pwse(iThis);

    UnusedParameter(This);
    UnusedParameter(Expires);

    return E_NOT_IMPLEMENTED;
}

PRIVATE SCODE wseUnsubscribe (
    PIWSEVENTING iThis,
    /*out*/ _TCHAR * Id,
    UINT IdSize,
    /*out*/ _TCHAR * Code,
    UINT CodeSize,
    /*out*/ PReason ReasonList)
{
    WsAddressing * This = pwse(iThis);

    UnusedParameter(This);
    UnusedParameter(Id);
    UnusedParameter(Code);
    UnusedParameter(ReasonList);

    return E_NOT_IMPLEMENTED;
}

PRIVATE SCODE wseGetStatus (
    PIWSEVENTING iThis)
{
    WsAddressing * This = pwse(iThis);

    UnusedParameter(This);

    return E_NOT_IMPLEMENTED;
}

PRIVATE SCODE wsmanQueryInterface (
    PIWSMANAGEMENT iThis,
    REFIID iid,
    /*out*/ void** ppObject)
{
    return wsaQueryInterface(iwsa(pwsman(iThis)), iid, ppObject);
}

PRIVATE UINT wsmanAddRef (
    PIWSMANAGEMENT iThis)
{
    IUnknown *unk = (IUnknown *) pwsman(iThis);
    return unk->v->AddRef(unk);
}

PRIVATE UINT wsmanRelease (
    PIWSMANAGEMENT iThis)
{
    IUnknown *unk = (IUnknown *) pwsman(iThis);
    return unk->v->Release(unk);
}

PRIVATE SCODE wsmanMaxEnvelopeSize (
    PIWSMANAGEMENT iThis,
    PICONTINUATION iCont,
    const _TCHAR *Policy,
    UINT Value)
{
    WSA_INIT;

    (*ppCtx)->MaxEnvelopeSizeValue = Value;

    return S_OK;
}


PRIVATE SCODE wsmanResourceURI (
    PIWSMANAGEMENT iThis,
    PICONTINUATION iCont,
    const _TCHAR * Value)
{
    WSA_INIT;

    (*ppCtx)->ResourceURI = (_TCHAR *) Value;

    return S_OK;
}

PRIVATE SCODE wsmanLocale (
    PIWSMANAGEMENT iThis,
    PICONTINUATION iCont,
    const _TCHAR *Value)
{
    WSA_INIT;

    //(*ppCtx)->ResourceURI = (_TCHAR *) Value;


    return S_OK;
}

PRIVATE SCODE wsmanOperationTimeout (
    PIWSMANAGEMENT iThis,
    PICONTINUATION iCont,
    TIME Value)
{
    WSA_INIT;

    (*ppCtx)->OperationTimeout = Value;

    return S_OK;
}

PRIVATE SCODE wsmanSelectorSet (
    PIWSMANAGEMENT iThis,
    PICONTINUATION iCont,
    PSelector SelectorList)
{
    WSA_INIT;

    (*ppCtx)->SelectorSet = (PSelector_wsa) SelectorList;

    return S_OK;
}

PRIVATE SCODE wsmanBookmark (
    PIWSMANAGEMENT iThis,
    PICONTINUATION iCont,
    const _TCHAR *Value)
{
    WSA_INIT;

    //(*ppCtx)->Bookmark = Value;


    return E_NOT_IMPLEMENTED;
}

PRIVATE SCODE wsdpQueryInterface (
    PIWSDEVICEPROFILE iThis,
    REFIID iid,
    /*out*/ void** ppObject)
{
    return wsaQueryInterface(iwsa(pwsdp(iThis)), iid, ppObject);
}

PRIVATE UINT wsdpAddRef (
    PIWSDEVICEPROFILE iThis)
{
    IUnknown *unk = (IUnknown *) pwsdp(iThis);
    return unk->v->AddRef(unk);
}

PRIVATE UINT wsdpRelease (
    PIWSDEVICEPROFILE iThis)
{
    IUnknown *unk = (IUnknown *) pwsdp(iThis);
    return unk->v->Release(unk);
}

PRIVATE SCODE wsdpServiceId (
    PIWSDEVICEPROFILE iThis,
    PICONTINUATION iCont,
    const _TCHAR * SvcId)
{
    WSA_INIT;

/*
    if ((*ppCtx)->RelatesTo)
        return E_FAIL;

    (*ppCtx)->RelatesTo = _tcsdup(Relates);
    if (!(*ppCtx)->RelatesTo)
        return E_NOT_ENOUGH_MEMORY;
*/

    DBG(("wsdpServiceId SvcId = %s\n", SvcId));
	
    return S_OK;
}

PRIVATE SCODE dsQueryInterface (
    PIDSSP iThis,
    REFIID iid,
    /*out*/ void** ppObject)
{
    return wsaQueryInterface(iwsa(pdssp(iThis)), iid, ppObject);
}

PRIVATE UINT dsAddRef (
    PIDSSP iThis)
{
    IUnknown *unk = (IUnknown *) pdssp(iThis);
    return unk->v->AddRef(unk);
}

PRIVATE UINT dsRelease (
    PIDSSP iThis)
{
    IUnknown *unk = (IUnknown *) pdssp(iThis);
    return unk->v->Release(unk);
}

PRIVATE SCODE dsTimestamp (
    PIDSSP iThis,
    PICONTINUATION iCont,
    TIME Value)
{
    WSA_INIT;

    (*ppCtx)->sendTime = Value;

    return S_OK;
}

PRIVATE SCODE dsNotificationHeader (
    PIDSSP iThis,
    PICONTINUATION iCont,
    const _TCHAR *ForwardedAction)
{
    WSA_INIT;

    (*ppCtx)->ForwardedAction = (_TCHAR *) ForwardedAction;

    return S_OK;
}