/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: minisite.cpp,v 1.16.2.3 2004/07/09 01:59:28 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * 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 terms of either the RPSL
 * or RCSL, 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 the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "minisite.h"
#include "hxvsurf.h"
#include "hxtick.h"
#include "minisurf.h"

CMiniBaseSite::CMiniBaseSite(IUnknown* pContext,
                             IUnknown* pUnkOuter,
                             INT32 lZorder)
    :  m_pContext(pContext),
       m_pCCF(NULL),
       m_pUser(NULL),
       m_pWatcher(NULL),
       m_pValues(NULL),
       m_pVideoSurface(NULL),
       m_pWindow(NULL),
       m_lRefCount(0),
       m_lZOrder(lZorder),
#if defined (HELIX_FEATURE_SMIL_SITE)
       m_pTopLevelSite(NULL),
       m_pParentSite(NULL),
       m_Region(NULL),
       m_RegionWithoutChildren(NULL),
       m_bIsVisible(TRUE),
       m_CallbackHandle(0),
       m_pScheduler(NULL),
       m_bfCallBacks(0),
       m_ulCallbackTime(0),
#endif
       m_bWindowCreatedByCreate(FALSE)
{

    memset(&m_size,      0, sizeof(m_size));
    memset(&m_position , 0, sizeof(m_position ));
#if defined (HELIX_FEATURE_SMIL_SITE)
    m_pTopLevelSite = this;
    memset(&m_topleft ,  0, sizeof(m_topleft ));
    memset(&m_TopLevelWindow , 0, sizeof(m_TopLevelWindow ));
    if( m_pContext )
    {
        m_pContext->QueryInterface(IID_IHXScheduler, (void**)&m_pScheduler);
    }
    HX_ASSERT(m_pScheduler);
#endif
    if(m_pContext)
    {
        m_pContext->AddRef();
        
        // get the CCF 
        m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                   (void**)&m_pCCF);

        if( m_pCCF )
        {
            m_pCCF->CreateInstance(CLSID_IHXValues, (void**)&m_pValues);
        }
    }


    m_pVideoSurface = CMiniBaseSurface::Create(m_pContext, this);
    m_pVideoSurface->AddRef();
}

CMiniBaseSite::~CMiniBaseSite()
{
    // release intefaces
    HX_RELEASE(m_pValues);
    HX_RELEASE(m_pCCF);
    HX_RELEASE(m_pContext);
    HX_RELEASE(m_pVideoSurface);

#if defined(HELIX_FEATURE_SMIL_SITE)
    if( m_CallbackHandle )
    {
        if( m_pScheduler)
            m_pScheduler->Remove(m_CallbackHandle);
        m_CallbackHandle = 0;
    }

    HX_RELEASE(m_pScheduler);
    
    m_ChildrenInZOrder.RemoveAll();

    //clean up passive site watchers
    while(m_PassiveSiteWatchers.GetCount())
    {
        IHXPassiveSiteWatcher* pWatcher =
            (IHXPassiveSiteWatcher*)m_PassiveSiteWatchers.GetHead();
        HX_RELEASE(pWatcher);
        m_PassiveSiteWatchers.RemoveHead();
    }
    
#endif    
}


/************************************************************************
 *  Method:
 *    IUnknown::QueryInterface
 */
STDMETHODIMP CMiniBaseSite::QueryInterface(REFIID riid, void** ppvObj)
{
    if(IsEqualIID(riid, IID_IUnknown))
    {
        *ppvObj = (IUnknown*)(IHXSite*)this;
    }
    else if (IsEqualIID(riid, IID_IHXSite))
    {
        *ppvObj = (IHXSite*)this;
    }

#if defined (HELIX_FEATURE_SMIL_SITE)    
    else if (IsEqualIID(riid, IID_IHXSite2))
    {
        *ppvObj = (IHXSite2*)this;
    }
    else if (IsEqualIID(riid, IID_IHXSiteControl))
    {
        *ppvObj = (IHXSiteControl*)this;
    }
#endif //HELIX_FEATURE_SMIL_SITE
    
    else if (IsEqualIID(riid, IID_IHXSiteWindowed))
    {
        *ppvObj = (IHXSiteWindowed*)this;
    }
    else if (m_pValues &&
             m_pValues->QueryInterface(riid, ppvObj) == HXR_OK)
    {
        return HXR_OK;
    }
    else
    {
        *ppvObj = NULL;
        return HXR_NOINTERFACE;
    }

    AddRef();
    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IUnknown::AddRef
 */
STDMETHODIMP_(ULONG32) CMiniBaseSite::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}


/************************************************************************
 *  Method:
 *    IUnknown::Release
 */
STDMETHODIMP_(ULONG32) CMiniBaseSite::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

/************************************************************************
 *  Method:
 *    CMiniBaseSite::AttachWindow
 */
STDMETHODIMP CMiniBaseSite::AttachWindow(HXxWindow* pWindow)
{
    if (m_pWindow && m_pWindow->window)
        return HXR_UNEXPECTED;

    m_pWindow = pWindow;

#if defined(HELIX_FEATURE_SMIL_SITE)
    if (!m_pParentSite)
    {
        memcpy(&m_TopLevelWindow,
               pWindow,
               sizeof(HXxWindow)); /* Flawfinder: ignore */
    }
#endif
    
    _AttachWindow();

    if (m_pUser)
    {
        // send HX_ATTACH_WINDOW msg to the renderers
        void *  lpParam1 = NULL;
        void *  lpParam2 = NULL;

        HXxEvent event = {HX_ATTACH_WINDOW,
                          m_pWindow ? m_pWindow->window : NULL,
                          lpParam1, lpParam2,
                          0, 0 };

        m_pUser->HandleEvent(&event);     
    }
   
    return HXR_OK;
}


/************************************************************************
 *  Method:
 *    IHXSiteWindowed::DetachWindow
 */
STDMETHODIMP CMiniBaseSite::DetachWindow()
{
    if (!m_pWindow || !m_pWindow->window)
        return HXR_UNEXPECTED;
    
    if (m_pUser)
    {
        // send HX_DETACH_WINDOW msg to the renderers
        void *  lpParam1 = NULL;
        void *  lpParam2 = NULL;
        HXxEvent event = {HX_DETACH_WINDOW, m_pWindow ? m_pWindow->window : NULL, lpParam1, lpParam2, 0, 0};
 
        m_pUser->HandleEvent(&event);
    }

    // let the OS specific site do its cleanup.
    _DetachWindow();
    m_pWindow = NULL;

#if defined(HELIX_FEATURE_SMIL_SITE)
    if (m_pTopLevelSite == this)
    {
        if (m_CallbackHandle)
        {
            if( m_pScheduler )
                m_pScheduler->Remove(m_CallbackHandle);
            m_CallbackHandle = 0;
        }
    }
#endif

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXSiteWindowed::Create
 */
STDMETHODIMP CMiniBaseSite::Create(void* ParentWindow, UINT32 style)
{
    HRESULT retVal = HXR_OK;
    
    if( m_pWindow && m_pWindow->window )
    {
        //We already have one.
        return HXR_FAIL;
    }
   
    void* hWnd = _Create(ParentWindow, style);

    if (!hWnd)
    {
        retVal = HXR_FAIL;
    }
    else
    {
        HXxWindow* pWindow = new HXxWindow;
        memset(pWindow,0,sizeof(HXxWindow));
        pWindow->window = hWnd;

        //This flags tells us if Create was used to make the window
        //or not. We use this in SetSize and SetPosition so that we
        //never resize or move the TLCs window. But, in TLC that call
        //create, like testplay, we need to do the move and resize
        //for them.
        m_bWindowCreatedByCreate = TRUE;
      
        //This way we always pass through attach window.
        AttachWindow(pWindow);
    }
    
    return retVal;
}

/************************************************************************
 *  Method:
 *    IHXSiteWindowed::Destroy
 */
STDMETHODIMP CMiniBaseSite::Destroy() 
{
    //This could be the site that owns the overlay even though
    //it doesn't have a window. Give it a chance to clean up
    //here...
    if( m_pVideoSurface )
    {
        m_pVideoSurface->EndOptimizedBlt();
    }
    
    if(!m_pWindow || !m_pWindow->window)
    {
        return HXR_UNEXPECTED;
    }

    HXxWindow tempWindow;
    memcpy( &tempWindow, m_pWindow, sizeof( HXxWindow ) ); /* Flawfinder: ignore */
    DetachWindow();
    _Destroy(&tempWindow);
    m_pWindow = NULL;

    return HXR_OK;
}

STDMETHODIMP_(HXxWindow*) CMiniBaseSite::GetWindow() 
{
    HXxWindow* pRet = m_pWindow;

#if defined(HELIX_FEATURE_SMIL_SITE)    
    if( m_pTopLevelSite != this && m_pParentSite )
        pRet = m_pParentSite->GetWindow();
#endif    
    return pRet;
}

/************************************************************************
 *  Method:
 *    IHXSite::AttachUser
 */
STDMETHODIMP CMiniBaseSite::AttachUser(IHXSiteUser* /*IN*/ pUser)
{
    if (m_pUser)
        return HXR_UNEXPECTED;

    m_pUser = pUser;
    m_pUser->AddRef();

    m_pUser->AttachSite(this);

    /*if (m_bAttachWindowPending)
      {
      m_bAttachWindowPending = FALSE;
      m_bDetachWndMsgPending = TRUE;
      // send HX_ATTACH_WINDOW msg to the renderers
      void *  lpParam1 = NULL;
      void *  lpParam2 = NULL;

      HXxEvent event = {HX_ATTACH_WINDOW, m_pWindow ? m_pWindow->window : NULL, lpParam1, lpParam2, 0, 0};
      m_pUser->HandleEvent(&event);     
      }*/

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXSite::DetachUser
 */
STDMETHODIMP CMiniBaseSite::DetachUser()
{
    if (!m_pUser)
        return HXR_UNEXPECTED;

#if defined(HELIX_FEATURE_SMIL_SITE)
    if (m_CallbackHandle)
    {
        if( m_pScheduler)
            m_pScheduler->Remove(m_CallbackHandle);
        m_CallbackHandle = 0;
    }
#endif

    AddRef();

    if (HXR_OK == m_pUser->DetachSite())
    {
        HX_RELEASE(m_pUser);
    }
    Release();

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXSite::GetUser
 */
STDMETHODIMP CMiniBaseSite::GetUser(REF(IHXSiteUser*) /*OUT*/ pUser)
{
    if (!m_pUser)
        return HXR_UNEXPECTED;

    pUser = m_pUser;
    pUser->AddRef();

    return HXR_OK;
}


/************************************************************************
 *  Method:
 *    IHXSite::CreateChild
 */
STDMETHODIMP CMiniBaseSite::CreateChild(REF(IHXSite*) /*OUT*/ pChildSite)
{
    HRESULT result = HXR_NOTIMPL;
#if defined (HELIX_FEATURE_SMIL_SITE)

    // Create an instance of CMiniBaseSite, let it know it's in child
    // window mode.
    CMiniBaseSite* pChildSiteWindowless =
        CMiniBaseSite::CreateSite(m_pContext, NULL, m_ChildrenInZOrder.GetCount());
    pChildSiteWindowless->SetParentSite(this);
    pChildSiteWindowless->SetTopLevelSite(m_pTopLevelSite);
    pChildSiteWindowless->SetParentWindow(&m_TopLevelWindow);

    // Get the IHXSite interface from the child to return to the
    // outside world.
    pChildSiteWindowless->QueryInterface(IID_IHXSite, (void**)&pChildSite);

    //Add it to our children list
    m_ChildrenInZOrder.AddTail(pChildSite);

    //Make sure it has the lowest z-order.
    pChildSiteWindowless->SetInternalZOrder(m_ChildrenInZOrder.GetCount());
    
    //Set the child's origin
    pChildSiteWindowless->SetOrigin(&m_topleft);

    //Let subclasses do any platform specific work.
    _ChildCreated();
    
    result = HXR_OK;
#endif
    return result;
}

/************************************************************************
 *  Method:
 *    IHXSite::DestroyChild
 */
STDMETHODIMP CMiniBaseSite::DestroyChild(IHXSite* /*IN*/ pChildSite)
{
    HX_RESULT res = HXR_FAIL;
#ifdef HELIX_FEATURE_SMIL_SITE
    //Find this child and remove it from our list
    LISTPOSITION pos = m_ChildrenInZOrder.Find((void*)pChildSite);
    if(pos)
    {
        res = HXR_OK;
        CMiniBaseSite* pSite = (CMiniBaseSite*)m_ChildrenInZOrder.GetAt(pos);
        m_ChildrenInZOrder.RemoveAt(pos);
    }
#endif
    return res;
}

/************************************************************************
 *  Method:
 *    IHXSite::AttachWatcher
 */
STDMETHODIMP CMiniBaseSite::AttachWatcher(IHXSiteWatcher* /*IN*/ pWatcher)
{
    if (m_pWatcher)
        return HXR_UNEXPECTED;

    m_pWatcher = pWatcher;

    if (m_pWatcher)
    {
        m_pWatcher->AddRef();
        m_pWatcher->AttachSite(this);
    }

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXSite::DetachWatcher
 */
STDMETHODIMP CMiniBaseSite::DetachWatcher()
{
    if (!m_pWatcher)
        return HXR_UNEXPECTED;

    m_pWatcher->DetachSite();
    HX_RELEASE(m_pWatcher);

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXSite::SetSize
 */

STDMETHODIMP CMiniBaseSite::SetSize(HXxSize size)
{
    HRESULT hres = HXR_OK;
#if defined (HELIX_FEATURE_SMIL_SITE)
    CHXSimpleList::Iterator i;
    
    size.cx = size.cx<0?0:size.cx;
    size.cy = size.cy<0?0:size.cy;

    //before we do anything, we give the SiteWatcher a chance to
    //influence this operation.
    if (m_pWatcher)
    {
        hres = m_pWatcher->ChangingSize(m_size, size);
    }
    
    if (HXR_OK == hres )
    {
        m_size = size;
        
        // iterate child site list 
        for(i=m_PassiveSiteWatchers.Begin(); i!= m_PassiveSiteWatchers.End(); ++i)
        {
            ((IHXPassiveSiteWatcher*) *i)->SizeChanged(&m_size);
        }

        if( m_pWindow && m_pWindow->window &&
            (m_pTopLevelSite!=this || m_bWindowCreatedByCreate ) )
        {
            _SetSize(size);
        }
    }

    if (m_pTopLevelSite==this)  
    {
        m_pTopLevelSite->RecomputeClip();
    } 
    else
    {
        if(m_pTopLevelSite)
            m_pTopLevelSite->ScheduleCallback(CLIP, 0);
    }
#else
    m_size = size;
    _SetSize(m_size);
#endif   
    return hres;
}

/************************************************************************
 *  Method:
 *    IHXSite::SetPosition
 */

STDMETHODIMP CMiniBaseSite::SetPosition(HXxPoint position)
{
    HRESULT hres = HXR_OK;
#if defined (HELIX_FEATURE_SMIL_SITE)
    CHXSimpleList::Iterator i;
    CHXMapPtrToPtr::Iterator j;

    //Before we do anything, we give the SiteWatcher a chance to
    //influence this operation.
    if (m_pWatcher)
    {
        hres = m_pWatcher->ChangingPosition(m_position, position);
    }

    if (HXR_OK == hres)
    {
        if(!m_bWindowCreatedByCreate )
        {
            // if we've created the window we don't want to move the site
            // just the window
            m_position = position;
        }
        
        //fixup our top left
        ResetOrigin();

        //iterate passive site watcher list 
        for(i=m_PassiveSiteWatchers.Begin(); i!= m_PassiveSiteWatchers.End(); ++i)
        {
            ((IHXPassiveSiteWatcher*) *i)->PositionChanged(&m_position);
        }

        if( m_pWindow &&
            m_pWindow->window &&
            (m_pTopLevelSite!=this || m_bWindowCreatedByCreate )
            )
        {
            _SetPosition(position);
        }
    }

    if (m_pTopLevelSite==this)
    {
        m_pTopLevelSite->RecomputeClip();
    }
    else
    {
        if (m_pTopLevelSite)
        {
            m_pTopLevelSite->ScheduleCallback(CLIP, 0);
        }
    }
#else
    m_position = position;
    _SetPosition(m_position);
#endif    
    return hres;
}

/************************************************************************
 *  Method:
 *    IHXSite::GetSize
 */
STDMETHODIMP CMiniBaseSite::GetSize(REF(HXxSize) size)
{
    size = m_size;
    return HXR_OK;
}


/************************************************************************
 *  Method:
 *    IHXSite::GetPosition
 */
STDMETHODIMP CMiniBaseSite::GetPosition(REF(HXxPoint) position)
{
    position = m_position;
    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXSite::DamageRect
 */
STDMETHODIMP CMiniBaseSite::DamageRect(HXxRect rect)
{
    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXSite::DamageRegion
 */
STDMETHODIMP CMiniBaseSite::DamageRegion(HXxRegion region)
{
    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXSite::ForceRedraw
 */
STDMETHODIMP CMiniBaseSite::ForceRedraw()
{
    
    if(m_pUser)
    {
        HXxEvent event;
        HXxWindow* hxxWin = GetWindow();

        event.event   = HX_SURFACE_UPDATE;
        event.window  = m_pWindow ? m_pWindow->window : NULL;
        event.param1  = m_pVideoSurface;
        event.param2  = NULL;
        event.result  = 0;
        event.handled = 0;

        m_pUser->HandleEvent(&event);
    }

    return HXR_OK;
}


#if defined (HELIX_FEATURE_SMIL_SITE)

STDMETHODIMP CMiniBaseSite::ForceRedrawAll()
{
    HX_RESULT retVal = HXR_OK;
    _ForceRedrawAll();
    return retVal;
}


/************************************************************************
 *  Method:
 *    IHXSite2::UpdateSiteWindow
 *      
 *      Not used on Windows platform
 */
STDMETHODIMP CMiniBaseSite::UpdateSiteWindow
(
    HXxWindow* /*IN*/ pWindow
    )
{
    return HXR_NOTIMPL;
}


/************************************************************************
 *  Method:
 *    IHXSite2::ShowSite
 */
STDMETHODIMP CMiniBaseSite::ShowSite(BOOL bShow)
{
    HX_RESULT res = HXR_OK;

#ifdef HELIX_FEATURE_SMIL_SITE    
    if(m_bIsVisible != bShow)
    {
        m_bIsVisible = bShow;

        if( this == m_pTopLevelSite )
        {
            RecomputeClip();
        }
        else
        {
            if(m_pTopLevelSite)
                m_pTopLevelSite->ScheduleCallback(CLIP, 0);
        }
    }

    res = HXR_OK;
#endif
    return res;
}

/************************************************************************
 *  Method:
 *    IHXSite2::MoveSiteToTop
 */
STDMETHODIMP CMiniBaseSite::MoveSiteToTop()
{
    SetZOrder(-1); //-1 means the top.
    return HXR_OK; 
}

/************************************************************************
 *  Method:
 *    IHXSite2::GetVideoSurface
 */
STDMETHODIMP CMiniBaseSite::GetVideoSurface(REF(IHXVideoSurface*) pSurface)
{
    HX_RESULT res = HXR_FAIL;

    if (m_pVideoSurface)
    {
        res = m_pVideoSurface->QueryInterface(IID_IHXVideoSurface, 
                                              (void**)&pSurface);
    }

    return res;
}


/************************************************************************
 *  Method:
 *    IHXSite2::AddPassiveSiteWatcher
 */
STDMETHODIMP CMiniBaseSite::AddPassiveSiteWatcher(IHXPassiveSiteWatcher* pWatcher)
{
    pWatcher->AddRef();
    m_PassiveSiteWatchers.AddTail(pWatcher);
    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXSite2::RemovePassiveSiteWatcher
 */
STDMETHODIMP CMiniBaseSite::RemovePassiveSiteWatcher(IHXPassiveSiteWatcher* pWatcher)
{
    // iterate child site list 
    IHXPassiveSiteWatcher* pThisWatcher = NULL;
    LISTPOSITION pPos = m_PassiveSiteWatchers.Find(pWatcher);
    HX_RESULT retVal = HXR_FAIL;

    if (pPos)
    {
        m_PassiveSiteWatchers.RemoveAt(pPos);
        HX_RELEASE(pWatcher);
        retVal = HXR_OK;
    }

    return retVal;
}

/************************************************************************
 *  Method:
 *    IHXSite2::SetCursor
 */
STDMETHODIMP CMiniBaseSite::SetCursor(HXxCursor cursor, REF(HXxCursor) oldCursor)
{
    return HXR_NOTIMPL;
}


STDMETHODIMP CMiniBaseSite::SetZOrder(INT32 lZOrder)
{
    if(!m_pParentSite) 
    {
        return HXR_UNEXPECTED;
    }

    if (lZOrder == -1)
    {
        lZOrder = m_pParentSite->GetNumberOfChildSites() - 1; 
    }
    
    if (lZOrder >= (INT32) m_pParentSite->GetNumberOfChildSites())
    {
        lZOrder = m_pParentSite->GetNumberOfChildSites() - 1;
    }

    if (m_lZOrder != lZOrder)
    {
        m_pParentSite->UpdateZOrder(this, lZOrder);
    }

    if( this == m_pTopLevelSite)
    {
        RecomputeClip();
    }
    else
    {
        if (m_pTopLevelSite)
            m_pTopLevelSite->ScheduleCallback(CLIP, 0);
    }
    return HXR_OK;
}

void CMiniBaseSite::UpdateZOrder( CMiniBaseSite* pUpdatedChildSite,INT32 lNewZOrder)
{
    HX_ASSERT(pUpdatedChildSite);
    LISTPOSITION pos = m_ChildrenInZOrder.Find((void*)pUpdatedChildSite);
    if (!pos)
    {
        return;
    }
    
    m_ChildrenInZOrder.RemoveAt(pos);
    
    BOOL bHasReinsertedChild = FALSE;
    INT32 zOrder = 0;
    INT32 newZOrder = 0;
    LISTPOSITION posNext = m_ChildrenInZOrder.GetHeadPosition();
    while (posNext)
    {
        pos = posNext;
        CMiniBaseSite* pSite = (CMiniBaseSite*)m_ChildrenInZOrder.GetNext(posNext);
        if (!bHasReinsertedChild)
        {
            pSite->GetZOrder(zOrder);
            
            if (zOrder > lNewZOrder)
            {
                m_ChildrenInZOrder.InsertBefore(pos, (void*)pUpdatedChildSite);
                bHasReinsertedChild = TRUE;
                pUpdatedChildSite->SetInternalZOrder(newZOrder++);
            }
        }
        pSite->SetInternalZOrder(newZOrder++);
    }
    
    if (!bHasReinsertedChild)
    {
        m_ChildrenInZOrder.AddTail((void*)pUpdatedChildSite);
        pUpdatedChildSite->SetInternalZOrder(newZOrder++);
    }
}
void CMiniBaseSite::RecomputeClip()
{
    // this recomputes our clipping rect.
    if( ComputeSubRects() )
    {
        //We need to do this in Z-Order.
        LISTPOSITION pos = m_ChildrenInZOrder.GetHeadPosition();
        while(pos)
        {
            CMiniBaseSite* pSite = (CMiniBaseSite*)m_ChildrenInZOrder.GetNext(pos);
            pSite->RecomputeClip();
        }
    }
}

BOOL CMiniBaseSite::IsSiteVisible()
{
    BOOL bIsVisible = m_bIsVisible;
    if(m_pParentSite)
    {
        bIsVisible &= m_pParentSite->IsSiteVisible();
    }
    if( bIsVisible )
    {
        //If we are a m_bSiteNeverBlts then it doesn't matter if we are
        //visible or not, we must have at least one child that is
        //visible and not m_bSiteNeverBlts. Otherwise we really aren't
        //visible at all. This distinction is needed because of our
        //favorite logic in computesubrecs not that we have transparent
        //regions.
        if(!_CheckForVisibleChild())
        {
            bIsVisible = FALSE;
        }
    }

    return bIsVisible;
}

BOOL CMiniBaseSite::_CheckForVisibleChild()
{
    BOOL       retVal = FALSE;
    BOOL       bSiteNeverBlts = FALSE;
    IHXBuffer* pBuf=NULL;

    m_pValues->GetPropertyCString( "SiteNeverBlts", pBuf );
    if( pBuf )
    {
        bSiteNeverBlts = atoi( (const char*)pBuf->GetBuffer() )==1;
        HX_RELEASE(pBuf);
    }
    
    if( m_bIsVisible && !bSiteNeverBlts )
    {
        retVal = TRUE;
    }
    else
    {
        LISTPOSITION pos = m_ChildrenInZOrder.GetHeadPosition();
        while(pos)
        {
            CMiniBaseSite* pSite = (CMiniBaseSite*)m_ChildrenInZOrder.GetNext(pos);
            if( pSite->_CheckForVisibleChild() )
            {
                retVal = TRUE;
                break;
            }
        }
    }
    return retVal;
}

BOOL CMiniBaseSite::ComputeSubRects()
{

    BOOL         retVal          = TRUE;        
    HXREGION*    hTemp           = NULL;
    HXxSize      size;
    HXxPoint*    pPosition       = NULL;
    LISTPOSITION pos             = NULL;

    if (m_Region)
    {
        HXDestroyRegion(m_Region);
        m_Region = NULL;
    }

    if (m_RegionWithoutChildren)
    {
        HXDestroyRegion(m_RegionWithoutChildren);
        m_RegionWithoutChildren = NULL;
    }

    if( IsSiteVisible() )
    {
        m_RegionWithoutChildren = HXCreateRectRegion(m_topleft.x,
                                                     m_topleft.y,
                                                     m_size.cx, 
                                                     m_size.cy
                                                     );


        if (m_pParentSite)
            m_pParentSite->BuildParnetClipping(m_RegionWithoutChildren,this);

        // subtract all of my children from my clipping region
        m_Region = HXCreateRectRegion(0,0,0,0);

        HXUnionRegion(m_Region, m_RegionWithoutChildren, m_Region);
        if (m_Region->numRects == 0)
        {
            retVal = FALSE;
        }
        else
        {
            pos = m_ChildrenInZOrder.GetHeadPosition();
            while(pos)
            {
                CMiniBaseSite* pSite = (CMiniBaseSite*) m_ChildrenInZOrder.GetNext(pos);
                if(pSite->IsSiteVisible())
                {    
                    pPosition = pSite->GetOrigin();
                    pSite->GetSize(size);
                    hTemp = HXCreateRectRegion( pPosition->x,
                                                pPosition->y,
                                                size.cx, 
                                                size.cy);
                    
                    HXSubtractRegion(m_Region, hTemp, m_Region);
                    HXDestroyRegion(hTemp);
                    hTemp = NULL;
                }
            }
        }
    }
    else
    {
        m_RegionWithoutChildren = HXCreateRectRegion(0,0,0,0);
        m_Region                = HXCreateRectRegion(0,0,0,0);
    }

    return TRUE;
}

void CMiniBaseSite::BuildParnetClipping(HXREGION* hClip, CMiniBaseSite* pChild)
{
    HXREGION* hTemp     = NULL;
    HXxPoint* pPosition = NULL;
    HXxSize   size;
    BOOL      bFound    = FALSE;

    // subtract all of my children from my clipping region
    LISTPOSITION pos = m_ChildrenInZOrder.GetHeadPosition();
    while(pos)
    {
        CMiniBaseSite* pSite = (CMiniBaseSite*)m_ChildrenInZOrder.GetNext(pos);
        //Keep in mind that all sites before pChild have zorder less than
        //pChild and all sites after have a higher zorder.
        if( pChild == pSite )
        {
            bFound = TRUE;
        }
        if(bFound && pChild!=pSite && pSite->IsSiteVisible())
        {
            pSite->GetSize(size);
            pPosition = pSite->GetOrigin();
            hTemp = HXCreateRectRegion( pPosition->x,
                                        pPosition->y,
                                        size.cx, 
                                        size.cy);

            HXSubtractRegion(hClip, hTemp, hClip);
            HXDestroyRegion(hTemp);
            hTemp=NULL;
        }
    }

    // now handle my clipping region
    hTemp = HXCreateRectRegion(m_topleft.x,
                               m_topleft.y,
                               m_size.cx, 
                               m_size.cy);
    
    HXIntersectRegion(hClip, hTemp, hClip);
    HXDestroyRegion(hTemp);
    hTemp=NULL;
    
    if (m_pParentSite)
        m_pParentSite->BuildParnetClipping(hClip,this);
}

void CMiniBaseSite::ScheduleCallback(CALLBACK_TYPE nWhichCallback,
                                   INT32 nMilliseconds)
{
    HX_ASSERT(m_pTopLevelSite == this);


    if( !(m_bfCallBacks & nWhichCallback))
    {
        //It is not already set.
        m_bfCallBacks |= nWhichCallback;
        
        if (m_pScheduler)
        {
            ULONG32 now = HX_GET_TICKCOUNT();
            if(m_CallbackHandle && m_ulCallbackTime>(now+nMilliseconds) )
            {
                m_pScheduler->Remove(m_CallbackHandle);
                m_CallbackHandle = 0;
            }

            if( !m_CallbackHandle )
            {
                //Don't schedule a new one if we are already waiting.
                m_CallbackHandle = m_pScheduler->RelativeEnter( this,
                                                                nMilliseconds);
                m_ulCallbackTime = now+nMilliseconds;
            }
        }
    }
}

STDMETHODIMP CMiniBaseSite::Func(void)
{
    m_CallbackHandle = 0;

    AddRef();

    if( m_bfCallBacks & CLIP )
    {
        RecomputeClip();
        _ForceRedrawAll();
        m_bfCallBacks &= ~CLIP;
        m_bfCallBacks &= ~REDRAW_ALL;
    }

    if (m_bfCallBacks & REPAINT)
    {
        _ForceRedrawAll();
        m_bfCallBacks &= ~REPAINT;
    }

    if( m_bfCallBacks & REDRAW_ALL )
    {
        _ForceRedrawAll();
        m_bfCallBacks &= ~REDRAW_ALL;
    }

    Release();

    return HXR_OK;
}

void CMiniBaseSite::_ForceRedrawAll()
{
    if( (IsSiteVisible() && m_Region && !HXEmptyRegion(m_Region)))
    {
        ForceRedraw();
    }
   
    //Now do all the children in z-order
    LISTPOSITION pos = m_ChildrenInZOrder.GetHeadPosition();
    while(pos)
    {
        CMiniBaseSite* pSite = (CMiniBaseSite*)m_ChildrenInZOrder.GetNext(pos);
        pSite->_ForceRedrawAll();
    }
}

void CMiniBaseSite::ResetOrigin()
{
    m_topleft.x =0;
    m_topleft.y =0;
    GetAbsoluteCords(m_topleft);
   
    LISTPOSITION pos = m_ChildrenInZOrder.GetHeadPosition();
    while(pos)
    {
        CMiniBaseSite* pChildSite = (CMiniBaseSite*)m_ChildrenInZOrder.GetNext(pos);
        pChildSite->ResetOrigin();
    }
}

void CMiniBaseSite::GetAbsoluteCords(HXxPoint& point)
{
    point.x += m_position.x;
    point.y += m_position.y;

    if (m_pParentSite)
    {
        m_pParentSite->GetAbsoluteCords(point);
    }
}

#endif //HELIX_FEATURE_SMIL_SITE

