/*
Copyright (C) 2001-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/pmcproxy.pmc 34125 2008-12-19T01:57:06.072455Z chromatic  $

=head1 NAME

src/pmc/pmcproxy.pmc - proxy class object for a PMC enabling introspection

=head1 DESCRIPTION

This class is used to describe a PMC. It can sit in an inheritance hierarchy
of a PDD15 class as well as allowing introspection of the PMC.

PMCProxy is not derived from any other PMC.

=head2 Structure

This class stores its state in the Parrot_Class structure, using the
following fields.

=over 4

=item C<id>

The type number of the PMC that is being described.

=item C<name>

The name of the PMC -- a STRING.

=item C<namespace>

The namespace the PMC is associated with, if any.

=item C<parents>

An array of immediate parent classes.
An empty ResizablePMCArray PMC is allocated during initialization.

=item C<all_parents>

A cached array of ourself and all parent classes, in MRO order.
A ResizablePMCArray PMC is allocated during initialization,
and is populated with the current class.

=back

NOTE: No doubt we will need other things later; this is a start.

=cut

*/

#include "parrot/parrot.h"
#include "parrot/oo_private.h"

/*

=head2 Functions

=over 4

=cut

*/


pmclass PMCProxy extends Class need_ext {

/*

=item C<void init()>

Initializes a PMCProxy PMC.

=cut

*/

    VTABLE void init() {
        Parrot_Class_attributes * const _pmc = mem_allocate_zeroed_typed(Parrot_Class_attributes);
        PMC          * const new_attribute   = pmc_new(interp, enum_class_Hash);
        STRING       * const name            = CONST_STRING(interp, "proxy");
        PMC_data(SELF)                       = _pmc;

        /* Set flags for custom DOD mark and destroy. */
        PObj_custom_mark_SET(SELF);
        PObj_active_destroy_SET(SELF);

        /* Set up the object. */
        _pmc->id               = 0;
        _pmc->name             = CONST_STRING(interp, "");
        _pmc->_namespace       = PMCNULL;
        _pmc->parents          = pmc_new(interp, enum_class_ResizablePMCArray);
        _pmc->all_parents      = pmc_new(interp, enum_class_ResizablePMCArray);
        _pmc->roles            = pmc_new(interp, enum_class_ResizablePMCArray);
        _pmc->methods          = pmc_new(interp, enum_class_Hash);
        _pmc->vtable_overrides = constant_pmc_new(interp, enum_class_Hash);
        _pmc->parent_overrides = pmc_new(interp, enum_class_Hash);
        _pmc->attrib_metadata  = pmc_new(interp, enum_class_Hash);
        _pmc->attrib_index     = PMCNULL;
        _pmc->attrib_cache     = PMCNULL;
        _pmc->resolve_method   = pmc_new(interp, enum_class_ResizablePMCArray);

        /* Set up the attribute storage for the proxy instance */
        VTABLE_set_string_keyed_str(interp, new_attribute, CONST_STRING(interp, "name"), name);
        VTABLE_set_pmc_keyed_str(interp, _pmc->attrib_metadata, name, new_attribute);

        /* We put ourself on the all parents list. */
        VTABLE_push_pmc(interp, _pmc->all_parents, SELF);

        /* We are a class. */
        PObj_is_class_SET(SELF);
    }

    VTABLE void init_pmc(PMC *init_data) {
        const INTVAL type_num = VTABLE_get_integer(interp, init_data);
        Parrot_Class_attributes *proxy_info;
        INTVAL mro_length, i;

        /* Ensure that it's a valid type number. */
        if (type_num > interp->n_vtable_max || type_num <= 0)
            Parrot_ex_throw_from_c_args(interp, NULL, 1,
                "Attempt to create PMC Proxy for invalid type number!");

        /* Set up the object. */
        SELF.init();

        /* Set up number, name and namespace. */
        proxy_info             = PARROT_CLASS(SELF);
        proxy_info->id         = type_num;
        proxy_info->name       = interp->vtables[type_num]->whoami;
        proxy_info->_namespace = interp->vtables[type_num]->_namespace;

        /* Build MRO (skip ourself). */
        mro_length = VTABLE_elements(interp, interp->vtables[type_num]->mro);
        for (i = 1; i < mro_length; i++) {
            PMC    *pclass = VTABLE_get_pmc_keyed_int(interp, interp->vtables[type_num]->mro, i);
            STRING *cname  = pclass->vtable->whoami;
            if (string_equal(interp, cname, CONST_STRING(interp, "scalar")) != 0) {
                PMC *pproxy = Parrot_oo_get_class_str(interp, cname);
                VTABLE_push_pmc(interp, proxy_info->all_parents, pproxy);
            }
        }

        /* PMCs just do single inheritance, so we'll assume that if we have a
         * second entry in our MRO, it goes in the parents list. */
        if (VTABLE_elements(interp, proxy_info->all_parents) >= 2)
            VTABLE_push_pmc(interp, proxy_info->parents,
                    VTABLE_get_pmc_keyed_int(interp, proxy_info->all_parents, 1));

        if (!PMC_IS_NULL(proxy_info->_namespace) &&
            PMC_IS_NULL(VTABLE_get_class(interp, proxy_info->_namespace))) {

            /* Link the proxy and the namespace, caching the proxy object for
             * later retrieval on class lookup. */
             Parrot_PCCINVOKE(interp, proxy_info->_namespace, CONST_STRING(interp, "set_class"), "P->", SELF);

            /* Extract any methods from the namespace */
            Parrot_oo_extract_methods_from_namespace(interp, SELF,
                    proxy_info->_namespace);
        }
    }

/*

=item C<PMC *instantiate(PMC *init)>

Creates an instance of the PMC.

=cut

*/
    VTABLE PMC *instantiate(PMC *init) {
        Parrot_Class_attributes * const _pmc = PARROT_CLASS(SELF);

        if (!PMC_IS_NULL(init))
            return pmc_new_init(interp, _pmc->id, init);

        return pmc_new(interp, _pmc->id);
    }

/*

=item C<void destroy()>

Free the memory associated with the object's underlying struct.

=cut

*/

    VTABLE void destroy() {
        mem_sys_free(PMC_data(SELF));
    }

/*

=item C<void mark()>

Mark any referenced strings and PMCs in the structure as live.

=cut

*/

    VTABLE void mark() {
        Parrot_Class_attributes * const _pmc = PARROT_CLASS(SELF);

        if (_pmc->name)
            pobject_lives(interp, (PObj *)_pmc->name);
        if (_pmc->_namespace)
            pobject_lives(interp, (PObj *)_pmc->_namespace);
        if (_pmc->parents)
            pobject_lives(interp, (PObj *)_pmc->parents);
        if (_pmc->all_parents)
            pobject_lives(interp, (PObj *)_pmc->all_parents);
        if (_pmc->roles)
            pobject_lives(interp, (PObj *)_pmc->roles);
        if (_pmc->methods)
            pobject_lives(interp, (PObj *)_pmc->methods);
        if (_pmc->vtable_overrides)
            pobject_lives(interp, (PObj *)_pmc->vtable_overrides);
        if (_pmc->parent_overrides)
            pobject_lives(interp, (PObj *)_pmc->parent_overrides);
        if (_pmc->attrib_metadata)
            pobject_lives(interp, (PObj *)_pmc->attrib_metadata);
        if (_pmc->attrib_index)
            pobject_lives(interp, (PObj *)_pmc->attrib_index);
        if (_pmc->attrib_cache)
            pobject_lives(interp, (PObj *)_pmc->attrib_cache);
        if (_pmc->resolve_method)
            pobject_lives(interp, (PObj *)_pmc->resolve_method);
    }

/*

=item C<INTVAL isa_pmc(PMC *classname)>

Returns whether the class is or inherits from C<*classname>.

=cut

*/

    VTABLE INTVAL isa_pmc(PMC *lookup) {
        Parrot_Class_attributes * const _proxy = PARROT_CLASS(SELF);
        INTVAL        i, num_classes;
        STRING       *classname;
        PMC          *classobj;

        if (PMC_IS_NULL(lookup))
            return 0;

        if (SUPER(lookup))
            return 1;

        classobj = Parrot_oo_get_class(interp, lookup);

        if (PMC_IS_NULL(classobj))
            return 0;

        classname = VTABLE_get_string(interp, classobj);

        /* Check if the passed name is the same as the stored short name. */
        if (string_equal(interp, classname, _proxy->name) == 0)
            return 1;

        /* Check if the class object is the same as self's class object */
        if (VTABLE_is_same(interp, SELF, classobj))
            return 1;

        /* Check if the passed name is the same as the fully qualified name. */
        if (string_equal(interp, classname, VTABLE_get_string(interp, SELF)) == 0)
            return 1;

        /* Look in the isa hash. */
        if (parrot_hash_exists(interp, interp->vtables[_proxy->id]->isa_hash, classname))
            return 1;

        /* Iterate over all the parents and check if they respond true
         * for 'isa' on the original comparison. */
        num_classes = VTABLE_elements(interp, _proxy->parents);

        for (i = 0; i < num_classes; i++) {
            PMC * const cur_class = VTABLE_get_pmc_keyed_int(interp,
                _proxy->parents, i);

            if (VTABLE_isa_pmc(interp, cur_class, lookup))
                return 1;
        }

        return 0;
    }
/*

=item C<INTVAL isa(STRING *classname)>

Returns whether the class is or inherits from C<*classname>.

=cut

*/

    VTABLE INTVAL isa(STRING *classname) {
        Parrot_Class_attributes * const _proxy = PARROT_CLASS(SELF);
        const STRING * const pmc_proxy = CONST_STRING(interp, "PMCProxy");

        if (string_equal(INTERP, classname, pmc_proxy) == 0)
            return 1;

        if (SUPER(classname))
            return 1;

        /* Look in the isa hash. */
        if (parrot_hash_exists(interp, interp->vtables[_proxy->id]->isa_hash, classname))
            return 1;

        return 0;
    }

/*

=item C<INTVAL does(STRING *role_name)>

Returns whether the class does the role with the given C<*rolename>.

=cut

*/

    VTABLE INTVAL does(STRING *role_name) {
        Parrot_Class_attributes *_class  = PARROT_CLASS(SELF);
        INTVAL                   id      = _class->id;
        PMC                     *proxied = pmc_new(interp, id);

        if (VTABLE_does(interp, proxied, role_name))
            return 1;

        return VTABLE_isa(interp, proxied, role_name);

    }

/*

=item C<INTVAL type()>

Returns the integer type of the class.

=cut

*/


    VTABLE INTVAL type() {
        const Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
        return _class->id;
    }

/*

=item C<PMC *inspect()>

Returns a Hash describing the PMC, with key/value pairs as described in
inspect_str.

=cut

*/
    PMC *inspect()
    {
        /* Create a hash, then use inspect_str to get all of the data to
         * fill it up with. */
        STRING * const name_str    = CONST_STRING(interp, "name");
        STRING * const ns_str      = CONST_STRING(interp, "namespace");
        STRING * const meth_str    = CONST_STRING(interp, "methods");
        STRING * const parents_str = CONST_STRING(interp, "parents");

        PMC * const metadata = pmc_new(interp, enum_class_Hash);

        VTABLE_set_pmc_keyed_str(interp, metadata, name_str,
            VTABLE_inspect_str(interp, SELF, name_str));
        VTABLE_set_pmc_keyed_str(interp, metadata, ns_str,
            VTABLE_inspect_str(interp, SELF, ns_str));
        VTABLE_set_pmc_keyed_str(interp, metadata, meth_str,
            VTABLE_inspect_str(interp, SELF, meth_str));
        VTABLE_set_pmc_keyed_str(interp, metadata, parents_str,
            VTABLE_inspect_str(interp, SELF, parents_str));

        return metadata;
    }

/*

=item C<STRING *name()>

Gets the name of the PMC.

=cut

*/
    METHOD name() {
        Parrot_Class_attributes * const _pmc     = PARROT_CLASS(SELF);
        STRING       * const ret_name = _pmc->name;
        RETURN(STRING *ret_name);
    }

/*

=item C<void get_namespace()>

Gets the namespace that this class is attached to.

=cut

*/
    METHOD get_namespace() {
        Parrot_Class_attributes * const _pmc          = PARROT_CLASS(SELF);
        PMC          * const ret_namespace = _pmc->_namespace;
        RETURN(PMC *ret_namespace);
    }

/*

=item C<void new(PMC *args)>

Creates an instance of the PMC.

=cut

*/
    METHOD new(PMC *args :optional, int got_args :opt_flag) {
        PMC * const obj = VTABLE_instantiate(interp, SELF, args);
        UNUSED(got_args);
        RETURN(PMC *obj);
    }

/*

=item C<void methods()>

Return a hash where the keys are method names and the values are methods.

=cut

*/
    METHOD methods() {
        PMC * const ret_methods = VTABLE_inspect_str(interp, SELF, CONST_STRING(interp, "methods"));
        RETURN(PMC *ret_methods);
    }

/*

=item C<void parents()>

Return the parents array PMC.

=cut

*/
    METHOD parents() {
        PMC * const ret_parents = VTABLE_inspect_str(interp, SELF, CONST_STRING(interp, "parents"));
        RETURN(PMC *ret_parents);
    }

/*

=item C<void inspect(STRING *what :optional)>

Gets all introspection data for the PMC or, if the optional string
parameter is supplied, a particular item of introspection data.

=cut

*/
    METHOD inspect(STRING *what :optional, int got_what :opt_flag) {
        /* Just delegate to the appropriate vtable method. */
        PMC * const found =
            got_what
                ? VTABLE_inspect_str(interp, SELF, what)
                : VTABLE_inspect(interp, SELF);

        RETURN(PMC *found);
    }

/*

=item C<STRING *get_string()>

Return the name of the low-level class (without the HLL namespace).

=cut

*/

    VTABLE STRING *get_string() {
        Parrot_Class_attributes * const proxy_info = PARROT_CLASS(SELF);

        /* Copy the stored string name of the proxy. */
        return string_copy(interp, proxy_info->name);
    }


} /* END pmclass */

/*

=back

=head1 SEE ALSO

F<docs/pdds/pdd17_pmc.pod>.

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
