/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the IDispatch implementation for XPConnect.
 *
 * The Initial Developer of the Original Code is
 * David Bradley.
 * Portions created by the Initial Developer are Copyright (C) 2002
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/**
 * \file XPCDispInlines.h inline implementations
 * Implementations for inline members of classes found in DispPrivate.h
 */


inline
PRBool nsXPConnect::IsIDispatchEnabled() 
{
    return XPCIDispatchExtension::IsEnabled();
}

//=============================================================================
// XPCDispInterface::Member:ParamInfo inlines

inline
XPCDispInterface::Member::ParamInfo::ParamInfo(
    const ELEMDESC * paramInfo) : mParamInfo(paramInfo) 
{
}

inline
JSBool XPCDispInterface::Member::ParamInfo::InitializeOutputParam(
    void * varBuffer, VARIANT & var) const
{
    var.vt = GetType() | VT_BYREF;
    var.byref = varBuffer;
    return JS_TRUE;
}

inline
PRBool XPCDispInterface::Member::ParamInfo::IsFlagSet(
    unsigned short flag) const 
{
    return mParamInfo->paramdesc.wParamFlags & flag ? PR_TRUE : PR_FALSE; 
}

inline
PRBool XPCDispInterface::Member::ParamInfo::IsIn() const 
{
    return IsFlagSet(PARAMFLAG_FIN) || mParamInfo->paramdesc.wParamFlags == 0;
}

inline
PRBool XPCDispInterface::Member::ParamInfo::IsOut() const 
{
    return IsFlagSet(PARAMFLAG_FOUT);
}

inline
PRBool XPCDispInterface::Member::ParamInfo::IsOptional() const 
{
    return IsFlagSet(PARAMFLAG_FOPT);
}

inline
PRBool XPCDispInterface::Member::ParamInfo::IsRetVal() const 
{
    return IsFlagSet(PARAMFLAG_FRETVAL);
}

// TODO: Handle VT_ARRAY as well
inline
VARTYPE XPCDispInterface::Member::ParamInfo::GetType() const 
{
    return mParamInfo->tdesc.vt == VT_PTR ? mParamInfo->tdesc.lptdesc->vt : mParamInfo->tdesc.vt;
}

//=============================================================================
// XPCDispInterface::Member inlines

inline
XPCDispInterface::Member::Member() : 
    mType(UNINITIALIZED), mFuncDesc(nsnull), mGetterFuncDesc(nsnull),
    mTypeInfo(nsnull)
{
}

inline
XPCDispInterface::Member::~Member() 
{
    if(mTypeInfo)
    {
        // Test to see if we have a separate getter. If we only have a getter they can
        // be the same
        PRBool releaseGetter = mGetterFuncDesc != nsnull && mFuncDesc != mGetterFuncDesc;
        if(mFuncDesc) 
            mTypeInfo->ReleaseFuncDesc(mFuncDesc);
        if(releaseGetter)
            mTypeInfo->ReleaseFuncDesc(mGetterFuncDesc);
    }
}

inline
void* XPCDispInterface::Member::operator new(size_t, Member* p)
{
    return p;
}

inline
void XPCDispInterface::Member::MakeGetter() 
{
    NS_ASSERTION(!IsFunction(), "Can't be function and property"); 
    mType |= GET_PROPERTY; 
}

inline
void XPCDispInterface::Member::MakeSetter() 
{ 
    NS_ASSERTION(!IsFunction(), "Can't be function and property"); 
    mType |= SET_PROPERTY; 
}

inline
void XPCDispInterface::Member::ResetType() 
{
    mType = UNINITIALIZED;
}

inline
void XPCDispInterface::Member::SetFunction() 
{ 
    NS_ASSERTION(!IsProperty(), "Can't be function and property"); 
    mType = FUNCTION; 
}

inline
PRBool XPCDispInterface::Member::IsFlagSet(unsigned short flag) const 
{
    return mType & flag ? PR_TRUE : PR_FALSE; 
}

inline
PRBool XPCDispInterface::Member::IsSetter() const
{
    return IsFlagSet(SET_PROPERTY);
}

inline
PRBool XPCDispInterface::Member::IsGetter() const
{
    return IsFlagSet(GET_PROPERTY);
}

inline
PRBool XPCDispInterface::Member::IsProperty() const
{
    return IsSetter() || IsGetter(); 
}

inline
PRBool XPCDispInterface::Member::IsFunction() const
{
    return IsFlagSet(FUNCTION);
}

inline
PRBool XPCDispInterface::Member::IsParameterizedSetter() const
{
    return IsSetter() && GetParamCount() > 1;
}

inline
PRBool XPCDispInterface::Member::IsParameterizedGetter() const
{
    return IsGetter() && (GetParamCount(PR_TRUE) > 1 ||
        (GetParamCount(PR_TRUE) == 1 && !GetParamInfo(0, PR_TRUE).IsRetVal()));
}

inline
PRBool XPCDispInterface::Member::IsParameterizedProperty() const
{
    return IsParameterizedSetter() || IsParameterizedGetter();
}

inline
jsval XPCDispInterface::Member::GetName() const
{
    return mName;
}

inline
void XPCDispInterface::Member::SetName(jsval name)
{
    mName = name;
}

inline
PRUint32 XPCDispInterface::Member::GetDispID() const
{
    return mFuncDesc->memid;
}

inline
PRUint32 XPCDispInterface::Member::GetParamCount(PRBool getter) const
{
    return (getter && mGetterFuncDesc) ? mGetterFuncDesc->cParams : mFuncDesc->cParams;
}

inline
XPCDispInterface::Member::ParamInfo XPCDispInterface::Member::GetParamInfo(PRUint32 index, PRBool getter) const
{
    NS_ASSERTION(index < GetParamCount(getter), "Array bounds error");
    return ParamInfo(((getter && mGetterFuncDesc) ? mGetterFuncDesc->lprgelemdescParam : mFuncDesc->lprgelemdescParam) + index);
}

inline
void XPCDispInterface::Member::SetTypeInfo(DISPID dispID, 
                                                ITypeInfo* pTypeInfo, 
                                                FUNCDESC* funcdesc)
{
    mTypeInfo = pTypeInfo; 
    mFuncDesc = funcdesc;
}

inline
void XPCDispInterface::Member::SetGetterFuncDesc(FUNCDESC* funcdesc)
{
    mGetterFuncDesc = funcdesc;
}

inline
PRUint16 XPCDispInterface::Member::GetParamType(PRUint32 index) const 
{
    return mFuncDesc->lprgelemdescParam[index].paramdesc.wParamFlags; 
}

inline
void XPCDispInterface::Member::SetMemID(DISPID memID)
{
    mMemID = memID;
}

inline
DISPID XPCDispInterface::Member::GetMemID() const
{
    return mMemID;
}

//=============================================================================
// XPCDispInterface::Allocator

inline
XPCDispInterface* XPCDispInterface::Allocator::Allocate()
{
    return Valid() ? new (Count()) XPCDispInterface(mCX, mTypeInfo, mIDispatchMembers) : nsnull;
}

inline
XPCDispInterface::Allocator::~Allocator()
{
    delete [] mMemIDs;
}

PRBool XPCDispInterface::Allocator::Valid() const
{
    return mMemIDs ? PR_TRUE : PR_FALSE;
}

//=============================================================================
// XPCDispInterface inlines

inline
JSObject* XPCDispInterface::GetJSObject() const
{
    return mJSObject;
}

inline
void XPCDispInterface::SetJSObject(JSObject* jsobj) 
{
    mJSObject = jsobj;
}

inline
const XPCDispInterface::Member* XPCDispInterface::FindMember(jsval name) const
{
    // Iterate backwards to save time
    const Member* member = mMembers + mMemberCount;
    while(member > mMembers)
    {
        --member;
        if(name == member->GetName())
        {
            return member;
        }
    }
    return nsnull;
}


inline
const XPCDispInterface::Member& XPCDispInterface::GetMember(PRUint32 index)
{ 
    NS_ASSERTION(index < mMemberCount, "invalid index");
    return mMembers[index]; 
}

inline
PRUint32 XPCDispInterface::GetMemberCount() const 
{
    return mMemberCount;
}

inline
void XPCDispInterface::operator delete(void * p) 
{
    PR_Free(p);
}

inline
XPCDispInterface::~XPCDispInterface()
{
    // Cleanup our members, the first gets cleaned up by the destructor
    // We have to cleanup the rest manually. These members are allocated
    // as part of the XPCIDispInterface object at the end
    for(PRUint32 index = 1; index < GetMemberCount(); ++index)
    {
        mMembers[index].~Member();
    }
}

inline
XPCDispInterface::XPCDispInterface(JSContext* cx, ITypeInfo * pTypeInfo,
                                   PRUint32 members) : mJSObject(nsnull)
{
    InspectIDispatch(cx, pTypeInfo, members);
}

inline
void * XPCDispInterface::operator new (size_t, PRUint32 members) 
{
    // Must allow for the member in XPCDispInterface
    if(!members)
        members = 1;
    // Calculate the size needed for the base XPCDispInterface and its members
    return PR_Malloc(sizeof(XPCDispInterface) + sizeof(Member) * (members - 1));
}

//=============================================================================
// XPCDispNameArray inlines

inline
XPCDispNameArray::XPCDispNameArray() : mCount(0), mNames(0) 
{
}

inline
XPCDispNameArray::~XPCDispNameArray() 
{ 
    delete [] mNames;
}

inline
void XPCDispNameArray::SetSize(PRUint32 size) 
{
    NS_ASSERTION(mCount == 0, "SetSize called more than once");
    mCount = size;
    mNames = (size ? new nsString[size] : 0);
}

inline
PRUint32 XPCDispNameArray::GetSize() const 
{
    return mCount;
}

inline
void XPCDispNameArray::SetName(DISPID dispid, nsAString const & name) 
{
    NS_ASSERTION(dispid <= mCount, "Array bounds error in XPCDispNameArray::SetName");
    mNames[dispid - 1] = name;
}

inline
const nsAString & XPCDispNameArray::GetName(DISPID dispid) const 
{
    NS_ASSERTION(dispid <= mCount, "Array bounds error in XPCDispNameArray::Get");
    if(dispid > 0)
        return mNames[dispid - 1];
    return sEmpty;
}

inline
DISPID XPCDispNameArray::Find(const nsAString &target) const
{
    for(PRUint32 index = 0; index < mCount; ++index) 
    {
        if(mNames[index].Equals(target)) 
            return NS_STATIC_CAST(DISPID, index + 1);
    }
    return 0; 
}

//=============================================================================
// XPCDispIDArray inlines

inline
PRUint32 XPCDispIDArray::Length() const
{
    return mIDArray.Count();
}

inline
jsval XPCDispIDArray::Item(JSContext* cx, PRUint32 index) const
{
    jsval val;
    if(!JS_IdToValue(cx, 
                     NS_REINTERPRET_CAST(jsid, 
                                         mIDArray.ElementAt(index)), &val))
        return JSVAL_NULL;
    return val;
}

inline
void XPCDispIDArray::Unmark()
{
    mMarked = JS_FALSE;
}

inline
JSBool XPCDispIDArray::IsMarked() const
{
    return mMarked;
}

    // NOP. This is just here to make the AutoMarkingPtr code compile.
inline
void XPCDispIDArray::MarkBeforeJSFinalize(JSContext*) 
{
}

//=============================================================================
// XPCDispTypeInfo inlines

inline
FUNCDESC* XPCDispTypeInfo::FuncDescArray::Get(PRUint32 index) 
{
    return NS_REINTERPRET_CAST(FUNCDESC*,mArray[index]);
}

inline
void XPCDispTypeInfo::FuncDescArray::Release(FUNCDESC *) 
{
}

inline
PRUint32 XPCDispTypeInfo::FuncDescArray::Length() const 
{
    return mArray.Count();
}

inline
const nsAString & XPCDispTypeInfo::GetNameForDispID(DISPID dispID)
{
    return mNameArray.GetName(dispID);
}

//=============================================================================
// XPCDispJSPropertyInfo inlines

inline
PRBool XPCDispJSPropertyInfo::Valid() const 
{
    return mPropertyType != INVALID;
}

inline
PRUint32 XPCDispJSPropertyInfo::GetParamCount() const
{
    return IsSetter() ? 1 : mParamCount;
}

inline
PRUint32 XPCDispJSPropertyInfo::GetMemID() const
{
    return mMemID;
}

inline
INVOKEKIND XPCDispJSPropertyInfo::GetInvokeKind() const
{
    return IsSetter() ? INVOKE_PROPERTYPUT : 
        (IsProperty() ? INVOKE_PROPERTYGET : INVOKE_FUNC); 
}

inline
PRBool XPCDispJSPropertyInfo::IsProperty() const
{
    return PropertyType() == PROPERTY || PropertyType() == READONLY_PROPERTY;
}

inline
PRBool XPCDispJSPropertyInfo::IsReadOnly() const
{
    return PropertyType()== READONLY_PROPERTY;
}

inline
PRBool XPCDispJSPropertyInfo::IsSetter() const
{
    return (mPropertyType & SETTER_MODE) != 0;
}
inline
void XPCDispJSPropertyInfo::SetSetter()
{
    mPropertyType |= SETTER_MODE;
}

inline
const nsAString & XPCDispJSPropertyInfo::GetName() const
{
    return mName; 
}

inline
XPCDispJSPropertyInfo::property_type XPCDispJSPropertyInfo::PropertyType() const
{
    return NS_STATIC_CAST(property_type,mPropertyType & ~SETTER_MODE);
}

//=============================================================================
// GUID/nsIID/nsCID conversion functions

inline
const nsIID & XPCDispIID2nsIID(const IID & iid)
{
    NS_ASSERTION(sizeof(IID) == sizeof(nsIID), "IID is not the same size as nsIID");
    return NS_REINTERPRET_CAST(const nsIID &,iid);
}

inline
const IID & XPCDispIID2IID(const nsIID & iid)
{
    NS_ASSERTION(sizeof(IID) == sizeof(nsIID), "IID is not the same size as nsIID");
    return NS_REINTERPRET_CAST(const IID &, iid);
}

inline
const nsCID & XPCDispCLSID2nsCID(const CLSID & clsid)
{
    NS_ASSERTION(sizeof(CLSID) == sizeof(nsCID), "CLSID is not the same size as nsCID");
    return NS_REINTERPRET_CAST(const nsCID &,clsid);
}

inline
const CLSID & XPCDispnsCID2CLSID(const nsCID & clsid)
{
    NS_ASSERTION(sizeof(CLSID) == sizeof(nsCID), "CLSID is not the same size as nsCID");
    return NS_REINTERPRET_CAST(const CLSID &, clsid);
}

//=============================================================================
// XPCDispParams inlines

inline
void XPCDispParams::SetNamedPropID()
{
    mDispParams.rgdispidNamedArgs = &mPropID; 
    mDispParams.cNamedArgs = 1; 
}

inline
VARIANT & XPCDispParams::GetParamRef(PRUint32 index)
{
    NS_ASSERTION(index < mDispParams.cArgs, "XPCDispParams::GetParam bounds error");
    return mDispParams.rgvarg[mDispParams.cArgs - index - 1];
}

inline
_variant_t XPCDispParams::GetParam(PRUint32 index) const
{
    return NS_CONST_CAST(XPCDispParams*,this)->GetParamRef(index);
}

inline
void * XPCDispParams::GetOutputBuffer(PRUint32 index)
{
    NS_ASSERTION(index < mDispParams.cArgs, "XPCDispParams::GetParam bounds error");
    return mRefBuffer + sizeof(VARIANT) * index;
}

//=============================================================================
// XPCDispParamPropJSClass inlines
inline
JSBool XPCDispParamPropJSClass::Invoke(XPCCallContext& ccx, 
                                       XPCDispObject::CallMode mode,
                                       jsval* retval)
{
    return XPCDispObject::Dispatch(ccx, mDispObj, mDispID, mode, mDispParams,
                                   retval);
}

//=============================================================================
// Other helper functions

/**
 * Converts a jsval that is a string to a char const *
 * @param cx a JS context
 * @param val The JS value to be converted
 * @return a C string (Does not need to be freed)
 */
inline
jschar * xpc_JSString2String(JSContext * cx, jsval val, PRUint32 * len = 0)
{
    JSString* str = JSVAL_IS_STRING(val) ? JSVAL_TO_STRING(val) : 
                                           JS_ValueToString(cx, val);
    if(str)
    {
        if(len)
            *len = JS_GetStringLength(str);
        return JS_GetStringChars(str);
    }
    if(len)
        *len = 0;
    return nsnull;
}

/**
 * Converts a jsval that is a string to a PRUnichar *
 * @param cx a JS context
 * @param val the JS value to vbe converted
 * @param length optional pointer to a variable to hold the length
 * @return a PRUnichar buffer (Does not need to be freed)
 */
inline
PRUnichar* xpc_JSString2PRUnichar(XPCCallContext& ccx, jsval val,
                                  size_t* length = nsnull)
{
    JSString* str = JS_ValueToString(ccx, val);
    if(!str)
        return nsnull;
    if(length)
        *length = JS_GetStringLength(str);
    return NS_REINTERPRET_CAST(PRUnichar*,JS_GetStringChars(str));
}

