/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sdext.hxx"

#include "PresenterPaneBase.hxx"
#include "PresenterCanvasHelper.hxx"
#include "PresenterController.hxx"
#include "PresenterGeometryHelper.hxx"
#include "PresenterPaintManager.hxx"
#include "PresenterTextView.hxx"
#include <com/sun/star/awt/PosSize.hpp>
#include <com/sun/star/awt/XWindow2.hpp>
#include <com/sun/star/awt/XWindowPeer.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/drawing/CanvasFeature.hpp>
#include <com/sun/star/rendering/CompositeOperation.hpp>
#include <com/sun/star/rendering/TexturingMode.hpp>
#include <osl/mutex.hxx>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::drawing::framework;
using ::rtl::OUString;

namespace sdext { namespace presenter {

//===== PresenterPaneBase =====================================================

PresenterPaneBase::PresenterPaneBase (
    const Reference<XComponentContext>& rxContext,
    const ::rtl::Reference<PresenterController>& rpPresenterController)
    : PresenterPaneBaseInterfaceBase(m_aMutex),
      mpPresenterController(rpPresenterController),
      mxParentWindow(),
      mxBorderWindow(),
      mxBorderCanvas(),
      mxContentWindow(),
      mxContentCanvas(),
      mxPaneId(),
      mxBorderPainter(),
      mxPresenterHelper(),
      msTitle(),
      mxComponentContext(rxContext),
      mpViewBackground(),
      mbHasCallout(false),
      maCalloutAnchor()
{
    if (mpPresenterController.get() != NULL)
        mxPresenterHelper = mpPresenterController->GetPresenterHelper();
}




PresenterPaneBase::~PresenterPaneBase (void)
{
}




void PresenterPaneBase::disposing (void)
{
    if (mxBorderWindow.is())
    {
        mxBorderWindow->removeWindowListener(this);
        mxBorderWindow->removePaintListener(this);
    }

    {
        Reference<XComponent> xComponent (mxContentCanvas, UNO_QUERY);
        mxContentCanvas = NULL;
        if (xComponent.is())
            xComponent->dispose();
    }

    {
        Reference<XComponent> xComponent (mxContentWindow, UNO_QUERY);
        mxContentWindow = NULL;
        if (xComponent.is())
            xComponent->dispose();
    }

    {
        Reference<XComponent> xComponent (mxBorderCanvas, UNO_QUERY);
        mxBorderCanvas = NULL;
        if (xComponent.is())
            xComponent->dispose();
    }

    {
        Reference<XComponent> xComponent (mxBorderWindow, UNO_QUERY);
        mxBorderWindow = NULL;
        if (xComponent.is())
            xComponent->dispose();
    }

    mxComponentContext = NULL;
}




void PresenterPaneBase::SetTitle (const OUString& rsTitle)
{
    msTitle = rsTitle;
    
    OSL_ASSERT(mpPresenterController.get()!=NULL);
    OSL_ASSERT(mpPresenterController->GetPaintManager().get()!=NULL);
    
    mpPresenterController->GetPaintManager()->Invalidate(mxBorderWindow);
}




::rtl::OUString PresenterPaneBase::GetTitle (void) const
{
    return msTitle;
}




Reference<drawing::framework::XPaneBorderPainter>
    PresenterPaneBase::GetPaneBorderPainter (void) const
{
    return mxBorderPainter;
}




void PresenterPaneBase::SetCalloutAnchor (const css::awt::Point& rCalloutAnchor)
{
    mbHasCallout = true;
    // Anchor is given in the coorindate system of the parent window.
    // Transform it into the local coordinate system.
    maCalloutAnchor = rCalloutAnchor;
    const awt::Rectangle aBorderBox (mxBorderWindow->getPosSize());
    maCalloutAnchor.X -= aBorderBox.X;
    maCalloutAnchor.Y -= aBorderBox.Y;

    // Move the bottom of the border window so that it goes through the
    // callout anchor (special case for bottom callout).
    sal_Int32 nHeight (rCalloutAnchor.Y - aBorderBox.Y);
    if (mxBorderPainter.is() && mxPaneId.is())
        nHeight += mxBorderPainter->getCalloutOffset(mxPaneId->getResourceURL()).Y;

    if (nHeight != aBorderBox.Height)
    {
        mxBorderWindow->setPosSize(
            aBorderBox.X,
            aBorderBox.Y,
            aBorderBox.Width,
            nHeight,
            awt::PosSize::HEIGHT);
    }

    mpPresenterController->GetPaintManager()->Invalidate(mxBorderWindow);
}




awt::Point PresenterPaneBase::GetCalloutAnchor (void) const
{
    return maCalloutAnchor;
}




::boost::shared_ptr<PresenterTextView> PresenterPaneBase::GetTextViewForTitle (void)
{
    ::boost::shared_ptr<PresenterTextView> pTextView(
        new PresenterTextView(
            mxComponentContext,
            mxBorderCanvas));
    pTextView->SetText(msTitle);
    return pTextView;
}




//----- XInitialization -------------------------------------------------------

void SAL_CALL PresenterPaneBase::initialize (const Sequence<Any>& rArguments)
    throw (Exception, RuntimeException)
{
    ThrowIfDisposed();

    if ( ! mxComponentContext.is())
    {
        throw RuntimeException(
            OUString::createFromAscii("PresenterSpritePane: missing component context"),
            static_cast<XWeak*>(this));
    }

    if (rArguments.getLength() == 5 || rArguments.getLength() == 6)
    {
        try
        {
            // Get the resource id from the first argument.
            if ( ! (rArguments[0] >>= mxPaneId))
            {
                throw lang::IllegalArgumentException(
                    OUString::createFromAscii("PresenterPane: invalid pane id"),
                    static_cast<XWeak*>(this),
                    0);
            }

            if ( ! (rArguments[1] >>= mxParentWindow))
            {
                throw lang::IllegalArgumentException(
                    OUString::createFromAscii("PresenterPane: invalid parent window"),
                    static_cast<XWeak*>(this),
                    1);
            }

            Reference<rendering::XSpriteCanvas> xParentCanvas;
            if ( ! (rArguments[2] >>= xParentCanvas))
            {
                throw lang::IllegalArgumentException(
                    OUString::createFromAscii("PresenterPane: invalid parent canvas"),
                    static_cast<XWeak*>(this),
                    2);
            }
            
            if ( ! (rArguments[3] >>= msTitle))
            {
                throw lang::IllegalArgumentException(
                    OUString::createFromAscii("PresenterPane: invalid title"),
                    static_cast<XWeak*>(this),
                    3);
            }

            if ( ! (rArguments[4] >>= mxBorderPainter))
            {
                throw lang::IllegalArgumentException(
                    OUString::createFromAscii("PresenterPane: invalid border painter"),
                    static_cast<XWeak*>(this),
                    4);
            }

            bool bIsWindowVisibleOnCreation (true);
            if (rArguments.getLength()>5 && ! (rArguments[5] >>= bIsWindowVisibleOnCreation))
            {
                throw lang::IllegalArgumentException(
                    OUString::createFromAscii("PresenterPane: invalid window visibility flag"),
                    static_cast<XWeak*>(this),
                    5);
            }

            CreateWindows(mxParentWindow, bIsWindowVisibleOnCreation);
    
            if (mxBorderWindow.is())
            {
                mxBorderWindow->addWindowListener(this);
                mxBorderWindow->addPaintListener(this);
            }
            
            CreateCanvases(mxParentWindow, xParentCanvas);

            // Raise new windows.
            ToTop();
        }
        catch (Exception&)
        {
            mxContentWindow = NULL;
            mxComponentContext = NULL;
            throw;
        }
    }
    else
    {
        throw RuntimeException(
            OUString::createFromAscii("PresenterSpritePane: invalid number of arguments"),
                static_cast<XWeak*>(this));
    }
}




//----- XResourceId -----------------------------------------------------------

Reference<XResourceId> SAL_CALL PresenterPaneBase::getResourceId (void)
    throw (RuntimeException)
{
    ThrowIfDisposed();
    return mxPaneId;
}




sal_Bool SAL_CALL PresenterPaneBase::isAnchorOnly (void)
    throw (RuntimeException)
{
    return true;
}




//----- XWindowListener -------------------------------------------------------

void SAL_CALL PresenterPaneBase::windowResized (const awt::WindowEvent& rEvent)
    throw (RuntimeException)
{
    (void)rEvent;
    ThrowIfDisposed();
}





void SAL_CALL PresenterPaneBase::windowMoved (const awt::WindowEvent& rEvent)
    throw (RuntimeException)
{
    (void)rEvent;
    ThrowIfDisposed();
}




void SAL_CALL PresenterPaneBase::windowShown (const lang::EventObject& rEvent)
    throw (RuntimeException)
{
    (void)rEvent;
    ThrowIfDisposed();
}




void SAL_CALL PresenterPaneBase::windowHidden (const lang::EventObject& rEvent)
    throw (RuntimeException)
{
    (void)rEvent;
    ThrowIfDisposed();
}




//----- lang::XEventListener --------------------------------------------------

void SAL_CALL PresenterPaneBase::disposing (const lang::EventObject& rEvent)
    throw (RuntimeException)
{
    if (rEvent.Source == mxBorderWindow)
    {
        mxBorderWindow = NULL;
    }
}




//-----------------------------------------------------------------------------


void PresenterPaneBase::CreateWindows (
    const Reference<awt::XWindow>& rxParentWindow,
    const bool bIsWindowVisibleOnCreation)
{
    if (mxPresenterHelper.is() && rxParentWindow.is())
    {
        
        mxBorderWindow = mxPresenterHelper->createWindow(
            rxParentWindow,
            sal_False,
            bIsWindowVisibleOnCreation,
            sal_False,
            sal_False);
        mxContentWindow = mxPresenterHelper->createWindow(
            mxBorderWindow,
            sal_False,
            bIsWindowVisibleOnCreation,
            sal_False,
            sal_False);
    }
}




Reference<awt::XWindow> PresenterPaneBase::GetBorderWindow (void) const
{
    return mxBorderWindow;
}




void PresenterPaneBase::ToTop (void)
{
    if (mxPresenterHelper.is())
        mxPresenterHelper->toTop(mxContentWindow);
}




void PresenterPaneBase::SetBackground (const SharedBitmapDescriptor& rpBackground)
{
    mpViewBackground = rpBackground;
}




void PresenterPaneBase::PaintBorderBackground (
    const awt::Rectangle& rBorderBox,
    const awt::Rectangle& rUpdateBox)
{
    (void)rBorderBox;
    (void)rUpdateBox;
    /*
    // The outer box of the border is given.  We need the center and inner
    // box as well.
    awt::Rectangle aCenterBox (
        mxBorderPainter->removeBorder(
            mxPaneId->getResourceURL(),
            rBorderBox,
            drawing::framework::BorderType_OUTER_BORDER));
    awt::Rectangle aInnerBox (
        mxBorderPainter->removeBorder(
            mxPaneId->getResourceURL(),
            rBorderBox,
            drawing::framework::BorderType_TOTAL_BORDER));
    mpPresenterController->GetCanvasHelper()->Paint(
        mpViewBackground,
        mxBorderCanvas,
        rUpdateBox,
        aCenterBox,
        aInnerBox);
    */
}




void PresenterPaneBase::PaintBorder (const awt::Rectangle& rUpdateBox)
{
    OSL_ASSERT(mxPaneId.is());

    if (mxBorderPainter.is() && mxBorderWindow.is() && mxBorderCanvas.is())
    {
        awt::Rectangle aBorderBox (mxBorderWindow->getPosSize());
        awt::Rectangle aLocalBorderBox (0,0, aBorderBox.Width, aBorderBox.Height);

        PaintBorderBackground(aLocalBorderBox, rUpdateBox);
        
        if (mbHasCallout)
            mxBorderPainter->paintBorderWithCallout(
                mxPaneId->getResourceURL(),
                mxBorderCanvas,
                aLocalBorderBox,
                rUpdateBox,
                msTitle,
                maCalloutAnchor);
        else
            mxBorderPainter->paintBorder(
                mxPaneId->getResourceURL(),
                mxBorderCanvas,
                aLocalBorderBox,
                rUpdateBox,
                msTitle);
    }
}




void PresenterPaneBase::LayoutContextWindow (void)
{
    OSL_ASSERT(mxPaneId.is());
    OSL_ASSERT(mxBorderWindow.is());
    OSL_ASSERT(mxContentWindow.is());
    if (mxBorderPainter.is() && mxPaneId.is() && mxBorderWindow.is() && mxContentWindow.is())
    {
        const awt::Rectangle aBorderBox (mxBorderWindow->getPosSize());
        const awt::Rectangle aInnerBox (mxBorderPainter->removeBorder(
            mxPaneId->getResourceURL(),
            aBorderBox,
            drawing::framework::BorderType_TOTAL_BORDER));
        mxContentWindow->setPosSize(
            aInnerBox.X - aBorderBox.X,
            aInnerBox.Y - aBorderBox.Y,
            aInnerBox.Width,
            aInnerBox.Height,
            awt::PosSize::POSSIZE);
    }
}




bool PresenterPaneBase::IsVisible (void) const
{
    Reference<awt::XWindow2> xWindow2 (mxBorderPainter, UNO_QUERY);
    if (xWindow2.is())
        return xWindow2->isVisible();

    return false;
}




void PresenterPaneBase::ThrowIfDisposed (void)
    throw (::com::sun::star::lang::DisposedException)
{
	if (rBHelper.bDisposed || rBHelper.bInDispose)
	{
        throw lang::DisposedException (
            ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
                "PresenterPane object has already been disposed")),
            static_cast<uno::XWeak*>(this));
    }
}




} } // end of namespace ::sd::presenter
