/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * 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 XML::Sablotron::DOM module.
 * 
 * The Initial Developer of the Original Code is Ginfer Alliance Ltd.
 * Portions created by Ginger Alliance are 
 * Copyright (C) 1999-2000 Ginger Alliance Ltd..  
 * All Rights Reserved.
 * 
 * Contributor(s):
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL"), in which case the provisions of the GPL are applicable 
 * instead of those above.  If you wish to allow use of your 
 * version of this file only under the terms of the GPL and not to
 * allow others to use your version of this file under the MPL,
 * indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by
 * the GPL.  If you do not delete the provisions above, a recipient
 * may use your version of this file under either the MPL or the
 * GPL.
 */

/*
 *
 *  ../Sablotron.xs includes this file
 *
 */ 

/************************************************************/
/* globals */
/************************************************************/

/* classes moved to common.h */
char *__classNames[] = CLASSNAMES;

/************************************************************/
/* error handling */
/************************************************************/

/* errorNames moved to common.h */
char* __errorNames[] = ERRORNAMES;

/* check function return value */
#define DE(sit, status) if (status) \
                      croak("XML::Sablotron::DOM(Code=%d, Name=%s, Msg=%s)", \
                            status, __errorNames[status], \
                            SDOM_getExceptionMessage(sit))

/* check the validity of the node */
#define CN(node) if (! node) croak("XML::Sablotron::DOM(Code=-1, Name='INVALID_NODE_ERR')")

/************************************************************/
/* globals for document */
/************************************************************/
#define DOC_HANDLE(doc) (SDOM_Document)SvIV(*hv_fetch((HV*)SvRV(doc), "_handle", 7, 0))

#define SIT_HANDLE(sit) (SablotSituation)SvIV(*hv_fetch((HV*)SvRV(sit), "_handle", 7, 0))

#define SIT_PARAM(cnt) ((items >= cnt) ? ST(cnt - 1) : &PL_sv_undef)

#define SIT_SMART(sit) (SvOK(sit) ? SIT_HANDLE(sit) : __sit)


bool __useUniqueDOMWrappers( void ) 
{
    SV* flag = get_sv( "XML::Sablotron::DOM::useUniqueWrappers", FALSE );
    return flag && SvTRUE(flag);
}

static SV* __createNodeOld(SablotSituation situa, SDOM_Node handle)
{
    HV* hash;
    SV* retval;
    SDOM_NodeType type;
    /* check and/or create inner SV* - used for validity checks*/
    SV* inner = (SV*)SDOM_getNodeInstanceData(handle);
    if (!inner) {
        /* printf("+++> creating new inner\n"); */
        inner = newSViv((IV)handle);
        /* store inner SV to node */
        SDOM_setNodeInstanceData(handle, inner);
    } else {
        /* printf("---> reusing the inner %d\n", SvIV(inner)); */
    }
    
    /* create new hash and store the handle into it */
    hash = newHV();
    hv_store(hash, "_handle", 7, SvREFCNT_inc(inner), 0);
    /* create blessed reference */
    retval = newRV_noinc((SV*)hash);
    DE( situa, SDOM_getNodeType(situa, handle, &type) );
    sv_bless(retval, gv_stashpv(__classNames[type], 0));

    return retval;
}


void __checkNodeInstanceData( SDOM_Node handle, HV * inner )
{
    if ( ! inner ) {
        croak("XML::Sablotron::DOM(Code=-1, Name='INVALID_NODE_ERR', Msg='InstanceData corrupted: NULL')");
    } else if ( SvTYPE( (SV*)inner ) != SVt_PVHV ) {
        croak("XML::Sablotron::DOM(Code=-1, Name='INVALID_NODE_ERR', Msg='InstanceData corrupted: not a HV')");
    } else {
        SV * ref = newRV_inc((SV*) inner);
        if ( ! ( sv_isobject( ref ) && sv_derived_from( ref , "XML::Sablotron::DOM::Node" ) ) ) {
            SvREFCNT_dec(ref);
            croak("XML::Sablotron::DOM(Code=-1, Name='INVALID_NODE_ERR', Msg='InstanceData corrupted: not a XML::Sablotron::DOM::Node");
        }  else if ( NODE_HANDLE( ref ) != handle ) {
            SvREFCNT_dec(ref);
            croak("XML::Sablotron::DOM(Code=-1, Name='INVALID_NODE_ERR', Msg='InstanceData corrupted: points to wrong node");
        }
        SvREFCNT_dec(ref);    
    }
}


static SV* __createNodeNew(SablotSituation situa, SDOM_Node handle)
{
    SV* retval;
    HV * hash;
    SDOM_NodeType type;
    /* we create only one wrapper object and one ref for each handle */
 
    /* is there already an wrapper ? */
    hash = (HV*)SDOM_getNodeInstanceData(handle);
    if ( hash ) {
        /* validity test */ 
        __checkNodeInstanceData( handle, hash );
        /* hash handle is ok. Return a new reference to it.  */
        retval = newRV_inc((SV*)hash);
#if 0
        fprintf(stderr,"DOM.h::__createNodeNew(%p): new rv %p reusing hv %p (%d)\n", 
                handle, retval, hash,(int)SvREFCNT((SV*)hash)); 
#endif
        return retval;
    }
    
    
    /* create new hash and store the handle into it */
    hash = newHV();
    hv_store(hash, "_handle", 7, newSViv( (IV) handle ), 0);
    SDOM_setNodeInstanceData(handle, hash);

    /* create blessed reference */
    retval = newRV_inc((SV*)hash);
    DE( situa, SDOM_getNodeType(situa, handle, &type) );
    retval = sv_bless(retval, gv_stashpv(__classNames[type], 0));
#if 0
    fprintf(stderr,"DOM.h::__createNodeNew(%p): new rv %p hash %p (%d)\n", 
            handle, retval, hash,(int)SvREFCNT((SV*)hash) );
#endif
    /* sv_dump( retval );
       sv_dump( (SV*) hash ); */
    return retval;
}

static SV* __createNode(SablotSituation situa, SDOM_Node handle)
{
    if ( __useUniqueDOMWrappers() ) {
        return __createNodeNew(situa, handle);
    }
    return __createNodeOld(situa, handle);
}


/************************************************************/
/* dispose calback */
/************************************************************/

static void __nodeDisposeCallbackOld(SDOM_Node node) 
{
    SV* pnode = (SV*)SDOM_getNodeInstanceData(node);
    if ( pnode ) sv_setiv(pnode, 0);
}

static void __nodeDisposeCallbackNew(SDOM_Node node) 
{
    HV * inner = (HV*)SDOM_getNodeInstanceData( node );
    if ( inner ) {
        __checkNodeInstanceData( node , inner );
        sv_setiv( *hv_fetch( inner, "_handle", 7, 0), 0 );
        SvREFCNT_dec( inner );
    }
}

void __nodeDisposeCallback(SDOM_Node node) 
{
    if ( __useUniqueDOMWrappers() )
        __nodeDisposeCallbackNew(node);
    else
        __nodeDisposeCallbackOld(node);
}

/*************************************************************/
/*  get implicit situation */
/*************************************************************/
SablotSituation __sit;