/**
 * Implementation of array assignment support routines.
 *
 * Copyright: Copyright Digital Mars 2004 - 2010.
 * License:   $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
 * Authors:   Walter Bright, Sean Kelly
 */

/*          Copyright Digital Mars 2004 - 2010.
 * Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE or copy at
 *          http://www.boost.org/LICENSE_1_0.txt)
 */
module rt.cast_;

extern (C):

/******************************************
 * Given a pointer:
 *      If it is an Object, return that Object.
 *      If it is an interface, return the Object implementing the interface.
 *      If it is null, return null.
 *      Else, undefined crash
 */

Object _d_toObject(void* p)
{
    if(!p)
        return null;

    Object o = cast(Object) p;
    ClassInfo oc = typeid(o);
    Interface* pi = **cast(Interface***) p;

    /* Interface.offset lines up with ClassInfo.name.ptr,
     * so we rely on pointers never being less than 64K,
     * and Objects never being greater.
     */
    if(pi.offset < 0x10000)
    {
        debug(cast_) printf("\tpi.offset = %d\n", pi.offset);
        return cast(Object)(p - pi.offset);
    }
    return o;
}


/*************************************
 * Attempts to cast Object o to class c.
 * Returns o if successful, null if not.
 */

void* _d_interface_cast(void* p, ClassInfo c)
{
    debug(cast_) printf("_d_interface_cast(p = %p, c = '%.*s')\n", p, c.name);
    if(!p)
        return null;

    Interface* pi = **cast(Interface***) p;

    debug(cast_) printf("\tpi.offset = %d\n", pi.offset);
    return _d_dynamic_cast(cast(Object)(p - pi.offset), c);
}

void* _d_dynamic_cast(Object o, ClassInfo c)
{
    debug(cast_) printf("_d_dynamic_cast(o = %p, c = '%.*s')\n", o, c.name);

    void* res = null;
    size_t offset = 0;
    if(o && _d_isbaseof2(typeid(o), c, offset))
    {
        debug(cast_) printf("\toffset = %d\n", offset);
        res = cast(void*) o + offset;
    }
    debug(cast_) printf("\tresult = %p\n", res);
    return res;
}

int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset)
{
    if(oc is c)
        return true;

    do
    {
        if(oc.base is c)
            return true;

        foreach(iface; oc.interfaces)
        {
            if(iface.classinfo is c)
            {
                offset = iface.offset;
                return true;
            }
        }

        foreach(iface; oc.interfaces)
        {
            if(_d_isbaseof2(iface.classinfo, c, offset))
            {
                offset = iface.offset;
                return true;
            }
        }

        oc = oc.base;
    }
    while(oc);

    return false;
}

int _d_isbaseof(ClassInfo oc, ClassInfo c)
{
    if(oc is c)
        return true;

    do
    {
        if(oc.base is c)
            return true;

        foreach(iface; oc.interfaces)
            if(iface.classinfo is c || _d_isbaseof(iface.classinfo, c))
                return true;

        oc = oc.base;
    }
    while(oc);

    return false;
}

/*********************************
 * Find the vtbl[] associated with Interface ic.
 */

void* _d_interface_vtbl(ClassInfo ic, Object o)
{
    debug(cast_) printf("__d_interface_vtbl(o = %p, ic = %p)\n", o, ic);

    assert(o);

    foreach(iface; typeid(o).interfaces)
        if(iface.classinfo is ic)
            return cast(void*) iface.vtbl;

    assert(0);
}
