/*! init client window
 *
 *  If manageClientViewports is set, then all viewports from the
 *  cluster window are duplcated to the client window.
 */
void MultiDisplayWindow::clientInit( void )
{
    bool             changed = false;
    ViewportPtr      vp,cvp;

    if(getManageClientViewports() == false ||
       getClientWindow() == NullFC)
        return;

    // check if something changed
    if(getMFPort()->size() == getClientWindow()->getMFPort()->size())
    {
        for(UInt32 v = 0 ; v < getMFPort()->size() && !changed ; v++)
        {
            vp  = getPort(v);
            cvp = getClientWindow()->getPort(v);
            if( vp->getRoot() != cvp->getRoot() ||
                vp->getLeft() != cvp->getLeft() ||
                vp->getRight() != cvp->getRight() ||
                vp->getBottom() != cvp->getBottom() ||
                vp->getTop() != cvp->getTop() ||
                vp->getBackground() != cvp->getBackground() ||
                vp->getMFForegrounds()->size() !=
                    cvp->getMFForegrounds()->size() )
                changed = true;
        }
    }
    else
    {
        changed = true;
    }

    if(changed)
    {
        beginEditCP(getClientWindow());
        // remove all viewports
        while(getClientWindow()->getMFPort()->size())
        {
            vp = getClientWindow()->getPort(0);
            getClientWindow()->subPort(0);
            subRefCP(vp);
        }
        // duplicate viewports
        for(UInt32 v=0 ; v<getMFPort()->size() ;v++)
        {
            getClientWindow()->addPort(ViewportPtr::dcast(getPort(v)->shallowCopy()));
        }
        endEditCP(getClientWindow());
    }
}
ViewportUnrecPtr WindowEventProducer::windowToViewport(const Pnt2f& WindowPoint, Pnt2f& ViewportPoint)
{
	ViewportUnrecPtr ThePort;
	for(UInt32 i(0) ; i<getMFPort()->size() ; ++i)
	{
		ThePort = getPort(i);
        if(ThePort->getEnabled())
        {
            ViewportPoint.setValues(WindowPoint.x() - ThePort->getPixelLeft(), WindowPoint.y() - ThePort->getPixelBottom());
    		
            return ThePort;
        }
		
	}
	return NULL;
}
void MultiDisplayWindow::clientInit(void)
{
    bool      changed = false;
    Viewport *vp, *cvp;

    if(getManageClientViewports() == false ||
            getClientWindow         () == NULL)
    {
        return;
    }

    // check if something changed
    if(getMFPort()->size() == getClientWindow()->getMFPort()->size())
    {
        for(UInt32 v = 0 ; v < getMFPort()->size() && !changed ; v++)
        {
            vp  = getPort(v);

            cvp = getClientWindow()->getPort(v);

            if( vp->getRoot() != cvp->getRoot() ||
                    vp->getLeft() != cvp->getLeft() ||
                    vp->getRight() != cvp->getRight() ||
                    vp->getBottom() != cvp->getBottom() ||
                    vp->getTop() != cvp->getTop() ||
                    vp->getBackground() != cvp->getBackground() ||
                    vp->getMFForegrounds()->size() !=
                    cvp->getMFForegrounds()->size() )
            {
                changed = true;
            }
        }
    }
    else
    {
        changed = true;
    }

    if(changed)
    {
        // remove all viewports
#if 0
        while(getClientWindow()->getMFPort()-size())
        {
            vp = getClientWindow()->getPort(0);

            getClientWindow()->subPort(0U);
        }
#endif

        getClientWindow()->clearPorts();

        // duplicate viewports
        for(UInt32 v=0 ; v<getMFPort()->size() ; v++)
        {
            ViewportUnrecPtr pTmpPort =
                dynamic_pointer_cast<Viewport>(getPort(v)->shallowCopy());

            getClientWindow()->addPort(pTmpPort);
        }
    }
}
void MultiDisplayWindow::serverRender(Window           *window,
                                      UInt32            id,
                                      RenderActionBase *action)
{
    TileCameraDecoratorUnrecPtr deco;
    ViewportUnrecPtr serverPort;
    Viewport * clientPort;
    StereoBufferViewport *clientStereoPort;
    UInt32 sv,cv;
    Int32 l,r,t,b;
    Int32 cleft,cright,ctop,cbottom;

    if(!getHServers())
    {
        setHServers(getMFServers()->size());
    }
    if(!getVServers())
    {
        setVServers(1);
    }

    UInt32 row   =id/getHServers();
    UInt32 column=id%getHServers();

    // calculate width and height from local width and height
    UInt32 width  = window->getWidth() ;
    UInt32 height = window->getHeight();

    if(getWidth()==0)
    {
        setWidth( width*getHServers() );
    }
    if(getHeight()==0)
    {
        setHeight( height*getVServers() );
    }

    Int32 left   = column * width  - column * getXOverlap();
    Int32 bottom = row    * height - row    * getYOverlap();
    Int32 right  = left   + width  - 1;
    Int32 top    = bottom + height - 1;
    Real64 scaleCWidth  =
        ((width - getXOverlap()) * (getHServers() - 1) + width) /
        float(getWidth());
    Real64 scaleCHeight =
        ((height - getYOverlap())* (getVServers() - 1) + height)/
        float(getHeight());

    // duplicate viewports
    for(cv=0,sv=0; cv<getMFPort()->size(); cv++)
    {
        clientPort = getPort(cv);

        clientStereoPort = dynamic_cast<StereoBufferViewport *>(clientPort);

        cleft   = Int32(clientPort->getPixelLeft()      * scaleCWidth)   ;
        cbottom = Int32(clientPort->getPixelBottom()    * scaleCHeight)  ;
        cright  = Int32((clientPort->getPixelRight()+1) * scaleCWidth) -1;
        ctop    = Int32((clientPort->getPixelTop()+1)   * scaleCHeight)-1;

        if(cright  < left   ||
                cleft   > right  ||
                ctop    < bottom ||
                cbottom > top      )
        {
            // invisible on this server screen
            continue;
        }

        // calculate overlapping viewport
        l = osgMax(cleft  ,left  ) - left;
        b = osgMax(cbottom,bottom) - bottom;
        r = osgMin(cright ,right ) - left;
        t = osgMin(ctop   ,top   ) - bottom;

        if(window->getMFPort()->size() <= sv)
        {
            serverPort = dynamic_pointer_cast<Viewport>(
                             clientPort->shallowCopy());

            deco = TileCameraDecorator::create();

            window->addPort(serverPort);

            serverPort->setCamera(deco);
        }
        else
        {
            serverPort = window->getPort(sv);

            deco = dynamic_cast<TileCameraDecorator *>(
                       serverPort->getCamera());

            if(window->getPort(sv)->getType() != clientPort->getType())
            {
                // there is a viewport with the wrong type
                serverPort =
                    dynamic_pointer_cast<Viewport>(clientPort->shallowCopy());

                window->replacePort(sv,
                                    serverPort);//[sv] = serverPort;
                serverPort->setCamera(deco);
            }
            else
            {
                deco = dynamic_cast<TileCameraDecorator *>(
                           serverPort->getCamera());
            }
        }

        // update changed viewport fields
        updateViewport(serverPort, clientPort);

        // set viewport size
        serverPort->setSize(Real32(l),Real32(b),Real32(r),Real32(t));

        // use pixel even if pixel = 1
        if(serverPort->getLeft() == 1.0)
            serverPort->setLeft(1.0001f);

        if(serverPort->getRight() == 1.0)
            serverPort->setRight(1.0001f);

        if(serverPort->getTop() == 1.0)
            serverPort->setTop(1.0001f);

        if(serverPort->getBottom() == 1.0)
            serverPort->setBottom(1.0001f);

        // calculate tile parameters
        deco->setFullWidth ( cright-cleft );
        deco->setFullHeight( ctop-cbottom );
        deco->setSize( ( l+left-cleft     ) / float( cright-cleft ),
                       ( b+bottom-cbottom ) / float( ctop-cbottom ),
                       ( r+left-cleft     ) / float( cright-cleft ),
                       ( t+bottom-cbottom ) / float( ctop-cbottom ) );
        deco->setDecoratee( clientPort->getCamera() );

        sv++;
    }

    // remove unused ports
    while(window->getMFPort()->size()>sv)
    {
        window->subPort(sv);
    }

    Inherited::serverRender(window,id,action);
}
/*! render server window
 *
 *  update all viewport parameters and render local viewports
 *  Width and height of the whole window are calculated by
 *  multiplieing the local window size by hServers and vServers.
 */
void MultiDisplayWindow::serverRender( WindowPtr serverWindow,
                                       UInt32 id,
                                       RenderActionBase *action )
{
    TileCameraDecoratorPtr deco;
    ViewportPtr serverPort;
    ViewportPtr clientPort;
    StereoBufferViewportPtr clientStereoPort;
    UInt32 sv,cv;
    Int32 l,r,t,b;
    Int32 cleft,cright,ctop,cbottom;

    // sync, otherwise viewports will be out of date

    if(!getHServers())
    {
        setHServers(getMFServers()->size());
    }
    if(!getVServers())
    {
        setVServers(1);
    }
    UInt32 row   =id/getHServers();
    UInt32 column=id%getHServers();
    // calculate width and height from local width and height
    UInt32 width  = serverWindow->getWidth() ;
    UInt32 height = serverWindow->getHeight();
    if(getWidth()==0)
    {
        setWidth( width*getHServers() );
    }
    if(getHeight()==0)
    {
        setHeight( height*getVServers() );
    }
    Int32 left   = column * width  - column * getXOverlap();
    Int32 bottom = row    * height - row    * getYOverlap();
    Int32 right  = left   + width  - 1;
    Int32 top    = bottom + height - 1;
    Real64 scaleCWidth   = ((width - getXOverlap()) * (getHServers() - 1) + width) / float(getWidth());
    Real64 scaleCHeight  = ((height - getYOverlap())* (getVServers() - 1) + height)/ float(getHeight());
    bool   isVirtualPort = false;

    // duplicate viewports
    for(cv = 0, sv = 0; cv < getMFPort()->size(); ++cv)
    {
        clientPort    = getPort(cv);
        isVirtualPort = clientPort->getType().isDerivedFrom(FBOViewport::getClassType());

        if(isVirtualPort)
        {
            // TODO -- seems wrong to render this on all servers, though rendering
            // then transmitting the texture doesn't seem like a good idea either.
            if(serverWindow->getMFPort()->size() <= sv)
            {
                serverPort = ViewportPtr::dcast(clientPort->shallowCopy());
                beginEditCP(serverWindow);
                serverWindow->addPort(serverPort);
                endEditCP(serverWindow);
            }
            else
            {
                serverPort = serverWindow->getPort(sv);

                if(serverPort->getType() != clientPort->getType())
                {
                    // there is a viewport with the wrong type
                    subRefCP(serverWindow->getPort(sv));
                    serverPort = ViewportPtr::dcast(clientPort->shallowCopy());
                    beginEditCP(serverWindow);
                    {
                        serverWindow->editPort(sv) = serverPort;
                    }
                    endEditCP(serverWindow);
                }
            }
            // update changed viewport fields
            updateViewport(serverPort,clientPort);
        }
        else
        {
            clientStereoPort = StereoBufferViewportPtr::dcast(clientPort);
            cleft   = Int32(clientPort->getPixelLeft()      * scaleCWidth)   ;
            cbottom = Int32(clientPort->getPixelBottom()    * scaleCHeight)  ;
            cright  = Int32((clientPort->getPixelRight()+1) * scaleCWidth) -1;
            ctop    = Int32((clientPort->getPixelTop()+1)   * scaleCHeight)-1;

            if( cright  < left   ||
                cleft   > right  ||
                ctop    < bottom ||
                cbottom > top       )
            {
                // invisible on this server screen
                continue;
            }
            // calculate overlapping viewport
            l = osgMax(cleft  ,left  ) - left;
            b = osgMax(cbottom,bottom) - bottom;
            r = osgMin(cright ,right ) - left;
            t = osgMin(ctop   ,top   ) - bottom;
            if(serverWindow->getMFPort()->size() <= sv)
            {
                serverPort = ViewportPtr::dcast(clientPort->shallowCopy());
                beginEditCP(serverPort);
                deco=TileCameraDecorator::create();
                beginEditCP(serverWindow);
                serverWindow->addPort(serverPort);
                serverPort->setCamera(deco);
                endEditCP(serverWindow);
                endEditCP(serverPort);
            }
            else
            {
                serverPort = serverWindow->getPort(sv);
                deco = TileCameraDecoratorPtr::dcast(serverPort->getCamera());

                if(serverPort->getType() != clientPort->getType())
                {
                    // there is a viewport with the wrong type
                    subRefCP(serverWindow->getPort(sv));
                    serverPort = ViewportPtr::dcast(clientPort->shallowCopy());

                    if(deco == NullFC)
                        deco = TileCameraDecorator::create();

                    beginEditCP(serverPort, Viewport::CameraFieldMask);
                    serverPort->setCamera(deco);
                    endEditCP(serverPort, Viewport::CameraFieldMask);

                    beginEditCP(serverWindow, Window::PortFieldMask);
                    serverWindow->editPort(sv) = serverPort;
                    endEditCP(serverWindow, Window::PortFieldMask);
                }
            }

            // update changed viewport fields
            updateViewport(serverPort,clientPort);

            // set viewport size
            beginEditCP(serverPort,
                        Viewport::LeftFieldMask|
                        Viewport::BottomFieldMask|
                        Viewport::RightFieldMask|
                        Viewport::TopFieldMask);
            serverPort->setSize(Real32(l),Real32(b),Real32(r),Real32(t));
            // use pixel even if pixel = 1
            if(serverPort->getLeft() == 1.0)
                serverPort->setLeft(1.0001);
            if(serverPort->getRight() == 1.0)
                serverPort->setRight(1.0001);
            if(serverPort->getTop() == 1.0)
                serverPort->setTop(1.0001);
            if(serverPort->getBottom() == 1.0)
                serverPort->setBottom(1.0001);
            endEditCP(serverPort,
                      Viewport::LeftFieldMask|
                      Viewport::BottomFieldMask|
                      Viewport::RightFieldMask|
                      Viewport::TopFieldMask);
            // calculate tile parameters
            beginEditCP(deco);
            deco->setFullWidth ( cright-cleft );
            deco->setFullHeight( ctop-cbottom );
            deco->setSize( ( l+left-cleft     ) / float( cright-cleft ),
                       ( b+bottom-cbottom ) / float( ctop-cbottom ),
                       ( r+left-cleft     ) / float( cright-cleft ),
                       ( t+bottom-cbottom ) / float( ctop-cbottom ) );
            deco->setDecoratee( clientPort->getCamera() );
            endEditCP(deco);
        }
        sv++;
    }
    // remove unused ports
    while(serverWindow->getMFPort()->size()>sv)
    {
        serverWindow->subPort(sv);
    }
    Inherited::serverRender(serverWindow,id,action);
}
void SortLastWindow::setupNodes(UInt32 groupId)
{
    UInt32  v             = 0;
    Node   *root          = NULL;
    UInt32  nI            = 0;
    UInt32  gnI           = 0;
    UInt32  gI            = 0;
    UInt32  group         = 0;
    UInt32  groupCount    = 0;
    UInt32  usableServers = getMFServers()->size32();

    if(!getGroupsChanged())
        return;

    // client and no client rendering 
    if(getMFServers()->size() == groupId &&
       (getComposer() == NULL ||
        !getComposer()->getClientRendering()))
    {
        for(nI = 0 ; nI < getMFGroupNodes()->size() ; ++nI)
        {
            if(getGroupNodes(nI)->getTravMask())
            {
                getGroupNodes(nI)->setTravMask(0);
                getGroupNodes(nI)->invalidateVolume();
            }
        }

        return;
    }

    if(getComposer() != NULL)
        usableServers = getComposer()->getUsableServers();

    // server but not usable, then invalidate all nodes
    if((getMFServers()->size() > groupId && usableServers <= groupId))
    {
        for(v = 0; v < getMFPort()->size(); ++v)
        {
            root = getPort(v)->getRoot();

            root->setTravMask(0);
            root->invalidateVolume();
        }

        setGroupsChanged(false);
        return;
    }        

    groupCount = usableServers;

    if(getComposer() != NULL) 
    {
        groupCount = getComposer()->getUsableServers();

        if(getComposer()->getClientRendering())
        {
            groupCount++;
        }
    }

    if(getMFServers()->size() == groupId)
        groupId = usableServers;

    // setup nodes
    for(nI = 0,gnI = 0,gI = 0,group = 0 ; nI < getMFGroupNodes()->size() ; ++nI)
    {
        while(nI >= gnI)
        {
            gnI += getGroupLengths(group); 
            gI++;
            group = gI % groupCount;
        }
        if(group == groupId)
        {
            if(getGroupNodes(nI)->getTravMask() != 
               TypeTraits<UInt32>::getMax())
            {
                getGroupNodes(nI)->setTravMask(TypeTraits<UInt32>::getMax());
                getGroupNodes(nI)->invalidateVolume();
            }
        }
        else
        {
            if(getGroupNodes(nI)->getTravMask())
            {
                getGroupNodes(nI)->setTravMask(0);
                getGroupNodes(nI)->invalidateVolume();
            }
        }

        getGroupNodes(nI)->updateVolume();
    }

    setGroupsChanged(false);
}
OSG_USING_NAMESPACE

/*! \class OSG::SortLastWindow
Cluster rendering configuration for sort first image composition
*/

/*----------------------------- static grouping functions -----------------*/

void SortLastWindow::buildGroups(void)
{
    UInt32            v          = 0;
    DrawableListT     drawables;
    UInt32            groupCount = 0;
    bool              rebuild    = false;

    // check for new nodes.
    FieldContainerFactoryBase *fcFactory = FieldContainerFactory::the();

    FieldContainer *fcPtr = NULL;

    ChangeList::ChangedStoreConstIt createdI;

    ChangeList *changeList = OSG::Thread::getCurrentChangeList();

    for(createdI  = changeList->beginCreated();
        createdI != changeList->endCreated(); 
        createdI++)
    {
        UInt32 uiId = (*createdI)->uiContainerId;

        fcPtr = fcFactory->getContainer(uiId);

        if(fcPtr != NULL && dynamic_cast<Node *>(fcPtr) != NULL)
            rebuild = true;
    }

    // is rebuild neccessary ?
    if(!rebuild && getMFGroupNodes()->size())
        return;

    groupCount = getMFServers()->size32();

    if(getComposer() != NULL)
    {
        groupCount = getComposer()->getUsableServers();
        if(getComposer()->getClientRendering())
            groupCount++;
    }

    // build groups for all viewports

    clearGroupNodes();
    editMFGroupLengths()->clear();

    for(v = 0; v < getMFPort()->size(); ++v)
    {
        Viewport *vp         = getPort(v);
        Node     *root       = vp->getRoot();

        drawables.clear();

        collectDrawables(root, drawables);

        if(drawables.size())
            splitDrawables(drawables, groupCount, false);
    }
}
void SortLastWindow::clientRender(RenderActionBase *action)
{
    UInt32          p;
    UInt32          groupId = getMFServers()->size32();

    if(getMFServers()->size())
    {
        if(getClientWindow() != NULL)
        {
            setupNodes(groupId);
/*
            getClientWindow()->activate();
            getClientWindow()->frameInit();
*/
            action->setWindow(getClientWindow());

            if(getComposer() != NULL)
                getComposer()->startFrame();

            DrawEnv oEnv;
            
            oEnv.setWindow(action->getWindow());

            // render all viewports
            for(p = 0; p < getMFPort()->size() ; ++p)
            {
                Viewport *vp=getPort(p);

                oEnv.setViewportDimension(vp->calcPixelLeft   (),
                                          vp->calcPixelBottom (),
                                          vp->calcPixelRight  (),
                                          vp->calcPixelTop    (),
                                          vp->calcIsFullWindow());
                if(getComposer() != NULL)
                {
                    getComposer()->startViewport(vp);

                    action->setCamera    (vp->getCamera    ());
                    action->setBackground(vp->getBackground());
                    action->setViewarea  (vp                 );
                    action->setTravMask  (vp->getTravMask  ());

                    action->apply(vp->getRoot());

                    for(UInt16 i=0; i < vp->getMFForegrounds()->size(); i++)
                    {
                        if(dynamic_cast<StatisticsForeground *>(
                               vp->getForegrounds(i)) == NULL)
                        {
                            vp->getForegrounds(i)->draw(&oEnv);
                        }
                    }

                    getComposer()->composeViewport(vp);

                    for(UInt16 i=0; i < vp->getMFForegrounds()->size(); i++)
                    {
                        if(dynamic_cast<StatisticsForeground *>(
                               vp->getForegrounds(i)) != NULL)
                        {
                            vp->getForegrounds(i)->draw(&oEnv);
                        }
                    }

                }
                else
                {
                    vp->render(action);
                }
            }

            // compose whole window
            if(getComposer() != NULL)
                getComposer()->composeWindow();
        }
    }
}
void SortLastWindow::serverRender(Window           *serverWindow,
                                  UInt32            id,
                                  RenderActionBase *action      )
{
    ViewportUnrecPtr  serverPort  = NULL;
    Viewport         *clientPort  = NULL;
    UInt32            sv          = 0;
    UInt32            cv          = 0;

    // duplicate viewports
    for(cv = 0, sv = 0; cv < getMFPort()->size(); ++cv)
    {
        clientPort = getPort(cv);

        if(serverWindow->getMFPort()->size() <= sv)
        {
            // create new port
            serverPort = Viewport::create();

            serverWindow->addPort(serverPort);
        }
        else
        {
            serverPort = serverWindow->getPort(sv);
        }

        // duplicate values

        if(getWidth() && getHeight())
        {
           serverPort->setSize(clientPort->calcPixelLeft  (),
                               clientPort->calcPixelBottom(),
                               clientPort->calcPixelRight (),
                               clientPort->calcPixelTop   ());
        }
        else
        {
            serverPort->setSize(0,0,0,0); 
        }
  
        serverPort->setCamera    (clientPort->getCamera    ());
        serverPort->setRoot      (clientPort->getRoot      ());
        serverPort->setBackground(clientPort->getBackground());

        // ignore statistics foreground
        serverPort->clearForegrounds();

        for(UInt32 f = 0 ; f < serverPort->getMFForegrounds()->size(); ++f)
        {
            Foreground *fg = clientPort->getForegrounds(f);

            StatisticsForeground *sfg = 
                dynamic_cast<StatisticsForeground *>(fg);

            if(sfg == NULL)
            {
                serverPort->addForeground(fg);
            }
        }

        serverPort->setTravMask(clientPort->getTravMask());

        sv++;
    }

    // remove unused ports
    while(serverWindow->getMFPort()->size() > sv)
    {
        serverWindow->subPort(sv);
    }

    // setup visible nodes
    setupNodes(id);

    // render the viewports
    serverWindow->activate();
    serverWindow->frameInit();

    action->setWindow(serverWindow);

    if(getComposer() != NULL)
        getComposer()->startFrame();

    for(sv = 0; sv < serverWindow->getMFPort()->size(); ++sv)
    {
        Viewport *vp = serverWindow->getPort(sv);

        if(getComposer() != NULL)
            getComposer()->startViewport(vp);

        // render
        vp->render(action);

        // compose single viewport
        if(getComposer() != NULL)
            getComposer()->composeViewport(vp);
    }

    // compose whole window
    if(getComposer() != NULL)
        getComposer()->composeWindow();
}