/* Copyright (c) Microsoft Corporation. All rights reserved. */
#include <mmlite.h>
#include <tchar.h>
#include <mmmacros.h>
#include <base/cobdesc.h>

extern const struct CUnknown_ClassDesc CUnknown_ClassDesc;

/* IUnknown::<this> points to v-table
 * pDC (pointer of Def Cob) converts interface ptr to implementation.
 * iDC (interface of DC)  converts implementation ptr to interface ptr.
 * dDC (interface to descriptor)
 */
#define pCUnknown(_i_) ((struct CUnknown *)(_i_))
#define iCUnknown(_p_) ((PIUNKNOWN)(_p_))
#define dCUnknown(_i_) (((struct CUnknownVtbl *)(((BYTE*)(_i_)->v) - offsetof(struct CUnknownVtbl, v)))->classd);

static SCODE MCT CUnknownQueryInterface(PIUNKNOWN iThis, REFIID Iid,
                                       void **ppObj)
{
    UINT i;
    const struct _ClassDesc *classd = dCUnknown(iThis);

    for (i = 0; i < classd->Count; i++) {
        if (UuidCmp(Iid, classd->iface[i].Iid)) {
            PIUNKNOWN pUnk;
            pUnk = (PIUNKNOWN) ((ADDRESS) iThis + classd->iface[i].Offset);
            pUnk->v->AddRef(pUnk);
            *ppObj = (void*) pUnk;
            return S_OK;
        }
    }
    return E_NO_INTERFACE;
}

static UINT MCT CUnknownAddRef(PIUNKNOWN iThis)
{
    struct CUnknown *This = pCUnknown(iThis);

    return AtomicInc(&This->RefCnt);
}

static UINT MCT CUnknownRelease(PIUNKNOWN iThis)
{
    struct CUnknown *This = pCUnknown(iThis);
    UINT RefCnt = AtomicDec(&This->RefCnt);

    if ((RefCnt & ~REFCNT_STATIC) == 0) {
        const struct _ClassDesc *classd = dCUnknown(iThis);
        if (classd->Destroy)
            classd->Destroy(iThis);
    }
    return RefCnt;
}

static const struct CUnknownVtbl CUnknownVtable = {
    (struct _ClassDesc *) &CUnknown_ClassDesc,
    {
        __VTABLE_COMPATIBILITY_FILLER_INITIALIZER

        CUnknownQueryInterface,
        CUnknownAddRef,
        CUnknownRelease
    }
};

#define C_QI(_off_) \
static SCODE MCT CUnknown_ ## _off_ ## _QueryInterface(PIUNKNOWN iThis, REFIID iid, void **ppObj) \
{ \
    iThis = (PIUNKNOWN) ((ADDRESS) iThis - _off_); \
    return CUnknownQueryInterface(iThis, iid, ppObj); \
}

#define C_ADDREF(_off_) \
static UINT MCT CUnknown_ ## _off_ ## _AddRef(PIUNKNOWN iThis) \
{ \
    iThis = (PIUNKNOWN) ((ADDRESS) iThis - _off_); \
    return CUnknownAddRef(iThis); \
}

#define C_RELEASE(_off_) \
static UINT MCT CUnknown_ ## _off_ ## _Release(PIUNKNOWN iThis) \
{ \
    iThis = (PIUNKNOWN) ((ADDRESS) iThis - _off_); \
    return CUnknownRelease(iThis); \
}

#define C_VTAB(_off_) static const struct CUnknownVtbl CUnknown_ ## _off_ ## _Vtable = { \
    (struct _ClassDesc *) &CUnknown_ClassDesc, \
    { \
        __VTABLE_COMPATIBILITY_FILLER_INITIALIZER \
        \
        CUnknown_ ## _off_ ## _QueryInterface, \
        CUnknown_ ## _off_ ## _AddRef, \
        CUnknown_ ## _off_ ## _Release \
    } \
};

#define C_DEFINE(_ix_) \
C_QI(_ix_) \
C_ADDREF(_ix_) \
C_RELEASE(_ix_) \
C_VTAB(_ix_)

C_DEFINE(8)
C_DEFINE(12)
C_DEFINE(16)
C_DEFINE(20)
C_DEFINE(24)
C_DEFINE(28)
C_DEFINE(32)

PTR MCT CUnknownInit(const struct _ClassDesc *classd,
		     /*optional*/ struct CUnknown *This)
{
    /* Fill in v-tables and RefCnt */
    UINT i;

    if (!This) {
        PIHEAP pHeap = CurrentHeap();
        This = (struct CUnknown *) pHeap->v->Alloc(pHeap, HEAP_ZERO_MEMORY,
                                                   classd->Size, 0);
        if (!This)
            return NULL;
    }

    /* Works when object inherits CUnknown (RefCnt is second item, other
     * v-tables follow later)
     */
    This->RefCnt = 1;
    for (i = 0; i < classd->Count; i++) {
        *(ADDRESS**) ((ADDRESS) This + classd->iface[i].Offset)
            = (ADDRESS*) classd->iface[i].v;
    }
    return This;
}

static SCODE MCT CUnknownDestroy(PIUNKNOWN iThis)
{
    struct CUnknown *This = pCUnknown(iThis);
    PIHEAP pHeap = CurrentHeap();

    /* Do not free if marked static */
    if (This->RefCnt & REFCNT_STATIC)
        return S_OK;

    return pHeap->v->Free(pHeap, 0, iThis);
}

const CLASSDESC(CUnknown, 8) = {
    _T("CUnknown"), &IID_IUnknown,
    NULL, NULL,
    CUnknownDestroy,
    sizeof(struct CUnknown),
    8, {
        {(struct IUnknownVtbl *) &CUnknownVtable.v, &IID_IUnknown, 3, 0},
        {(struct IUnknownVtbl *) &CUnknown_8_Vtable.v, &IID_IUnknown, 3, 8},
        {(struct IUnknownVtbl *) &CUnknown_12_Vtable.v, &IID_IUnknown, 3, 12},
        {(struct IUnknownVtbl *) &CUnknown_16_Vtable.v, &IID_IUnknown, 3, 16},
        {(struct IUnknownVtbl *) &CUnknown_20_Vtable.v, &IID_IUnknown, 3, 20},
        {(struct IUnknownVtbl *) &CUnknown_24_Vtable.v, &IID_IUnknown, 3, 24},
        {(struct IUnknownVtbl *) &CUnknown_28_Vtable.v, &IID_IUnknown, 3, 28},
        {(struct IUnknownVtbl *) &CUnknown_32_Vtable.v, &IID_IUnknown, 3, 32}
    }
};


/* Def COB Object */
typedef struct _DefCob *PDEFCOB;

#define pDefCob(_i_) ((PDEFCOB)(_i_))
#define iDefCob(_p_) ((PIUNKNOWN)(_p_))

static SCODE DefCobDestroy(PIUNKNOWN iThis)
{
    PDEFCOB This = pDefCob(iThis);
    struct _CobDesc *cobd = This->cobd;
    struct _ClassDesc *classd;
    SCODE sc;
    UINT i;
    PIHEAP pHeap = CurrentHeap();

    /* If the COB has a private destructor call it first.
     * It is presumed not to deallocate.
     */
    //if (cobd->mainclass && cobd->mainclass->Destroy)

    //    cobd->mainclass->Destroy(iThis);


    /* Unregister registered classes before terminating */
    for (i = 0; i < cobd->Count; i++) {
        classd = cobd->Classes[i];
        if (classd->ClassName) {
            sc = BaseRegisterClass(NULL, 0, classd->ClassName, NULL);
            /* Ignore any error */
        }
    }

    /* And give CRT a chance to clean up */
    // CrtInit(DLL_PROCESS_DETACH);


    /* Do not free if marked static */
    if (This->super.RefCnt & REFCNT_STATIC)
        return S_OK;

    return pHeap->v->Free(pHeap, 0, This);
}

const CLASSDESC(DefCob,7);

static const struct CUnknownVtbl DefCobVtable = {
    (struct _ClassDesc *) &DefCob_ClassDesc,
    {
        __VTABLE_COMPATIBILITY_FILLER_INITIALIZER

        CUnknownQueryInterface,
        CUnknownAddRef,
        CUnknownRelease
    }
};

const struct DefCob_ClassDesc DefCob_ClassDesc = {
    _T("DefCob"), &IID_IUnknown,
    _T("CUnknown"), &IID_IUnknown,
    DefCobDestroy,
    sizeof(struct _DefCob),
    7, {
        {(struct IUnknownVtbl *) &DefCobVtable.v,       &IID_IUnknown, 3, 0},
        {(struct IUnknownVtbl *) &CUnknown_12_Vtable.v, &IID_IUnknown, 3, 12},
        {(struct IUnknownVtbl *) &CUnknown_16_Vtable.v, &IID_IUnknown, 3, 16},
        {(struct IUnknownVtbl *) &CUnknown_20_Vtable.v, &IID_IUnknown, 3, 20},
        {(struct IUnknownVtbl *) &CUnknown_24_Vtable.v, &IID_IUnknown, 3, 24},
        {(struct IUnknownVtbl *) &CUnknown_28_Vtable.v, &IID_IUnknown, 3, 28},
        {(struct IUnknownVtbl *) &CUnknown_32_Vtable.v, &IID_IUnknown, 3, 32}
    }
};

COBDESC(Builtin,2) = {
    _T("builtin"),
    2,
    {
        (struct _ClassDesc *) &CUnknown_ClassDesc,
        (struct _ClassDesc *) &DefCob_ClassDesc
    }
};

PIUNKNOWN GenericCobInit(PTR Descriptor,
                         /*optional*/ struct _DefCob *CobObjArg)
{
    METHOD_DECLARE_NOTHIS(classreg.c, GenericCobInit);
    struct _CobDesc *cobd = Descriptor;
    struct _ClassDesc *classd;
    struct _DefCob *CobObj = CobObjArg;
    UINT i, j, k;

    sc = S_OK;
    for (i = 0; i < cobd->Count; i++) {
        classd = cobd->Classes[i];

        if (classd->SuperClass) {
            /* Deal with v-table starting from QI so as to account for any pre-fillers */
            sc = BaseInheritClass((ADDRESS*) &classd->iface[0].v->QueryInterface,
                                  classd->SuperClass,
                                  classd->SuperId);
            CHECKSC("BaseInheritClass");

            /* Inherit other v-tables. XXX only CUnknown */
            for (j = 1; j < classd->Count; j++) {
                ADDRESS * VTable = (ADDRESS*) &classd->iface[j].v->QueryInterface;
                /* Lookup the right v-table in a loop */
                ADDRESS * StoredV = NULL;
                UINT off = classd->iface[j].Offset;
                for (k = 0; k < CUnknown_ClassDesc.Count; k++) {
                    if (CUnknown_ClassDesc.iface[k].Offset == off)
                        StoredV = (ADDRESS*) &CUnknown_ClassDesc.iface[k].v->QueryInterface;
                }
                CHECKSETSC(StoredV, E_INVALID_PARAMETER,
                           "CUnknow0n inheritance botched");
                for (k = 0; k < 3; k++)
                    if (VTable[k] == 0) {
                        VTable[k] = StoredV[k];

                    }
            }

            /* When we know the size of the COB, we'll use the correct size */
            if (!CobObj && !_tcscmp(classd->SuperClass, _T("DefCob"))) {
                PIHEAP pHeap = CurrentHeap();
                CobObj = (struct _DefCob *) pHeap->v->Alloc(pHeap,
                                                            HEAP_ZERO_MEMORY,
                                                            classd->Size, 0);
                CHECKSETSC(CobObj, E_NOT_ENOUGH_MEMORY, "Alloc cob object");
            }
        }

        if (classd->ClassName) {
            sc = BaseRegisterClass((ADDRESS*) &classd->iface[0].v->QueryInterface,
                                   classd->iface[0].MethodCount,
                                   classd->ClassName,
                                   classd->ClassId);
            CHECKSC("BaseRegisterClass");
        }
    }

    if (CobObj) {
        CUnknownInit(cobd->Classes[0], &CobObj->super);
        //CobObj->super.v = (void*) cobd->Classes[0]->iface[0].v;

        /* If an object was given, do not automatically Free it. */
        if (CobObjArg)
            CobObj->super.RefCnt |= REFCNT_STATIC;
        /* Save state so that we can undo the registrations */
        CobObj->cobd = cobd;
    }

METHOD_CLEANUP;
    if (FAILED(sc)) {
        RELEASE(iDefCob(CobObj));
        CobObj = NULL;
        /* XXX Unregister everything that was registered */
    }
    return iDefCob(CobObj);
}


/* Class Registry */

static PINAMERANGES ClassNR = NULL;

PUBLIC SCODE MCT BaseInitializeClassRegistry (void)
{
    SCODE sc;
    PINAMESPACE ns = CurrentNameSpace();
    PIUNKNOWN pUnk;
    /* Create subdir. */
    sc = ns->v->Bind(ns, _T("vtables"), NAME_SPACE_CREATE, &pUnk);
    if (FAILED(sc))
        return sc;
    sc = pUnk->v->QueryInterface(pUnk, &IID_INameRanges, (void *) &ClassNR);
    pUnk->v->Release(pUnk);
    if (FAILED(sc))
        return sc;

    pUnk = GenericCobInit((struct _CobDesc *) &Builtin_CobDesc, NULL);

    return sc;
}

void BaseDeleteClassRegistry (void)
{
    ClassNR->v->Release(ClassNR);
}


PUBLIC SCODE MCT BaseRegisterClass (
    const ADDRESS * VTable,
    UINT MethodCount,
    const _TCHAR * ClassName,
    REFIID Iid)
{
    /* Hack: register ClassNR as the object as it cannot be NULL
     * but we don't have a real object.
     */
    if (VTable) {
        return ClassNR->v->RegisterRange(ClassNR, ClassName,
                                         (PIUNKNOWN) ClassNR,
                                         NAME_SPACE_CREATE,
                                         (ADDRESS) VTable,
                                         MethodCount * sizeof(ADDRESS),
                                         Iid);
    } else {
        return ClassNR->v->Unregister(ClassNR, ClassName);
    }
}

PUBLIC SCODE MCT BaseInheritClass (
    ADDRESS * VTable,
    const _TCHAR * ClassName,
    REFIID Iid)
{
    SCODE sc;
    ADDRESS *StoredV;
    UINT Size, i;

    /* Search for class */
    sc = ClassNR->v->LookupRange(ClassNR, ClassName, Iid,
                                 (ADDRESS*) &StoredV, &Size);
    if (FAILED(sc))
        return sc;

    sc = S_FALSE;               /* no entries copied */

    /* Inherit methods */
    Size /= sizeof(ADDRESS);    /* scale to number of slots */
    for (i = 0; i < Size; i++)
        if (VTable[i] == 0) {
            VTable[i] = StoredV[i];
            sc = S_OK;          /* some entries copied */
        }

    return sc;
}