#include "vclxtabcontrol.hxx"

#ifndef _TOOLKIT_HELPER_PROPERTY_HXX_
#include "toolkit/helper/property.hxx"
#endif
#ifndef _COM_SUN_STAR_AWT_SCROLLBARORIENTATION_HPP_
#include <com/sun/star/awt/ScrollBarOrientation.hpp>
#endif


#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _SV_TABCTRL_HXX
#include <vcl/tabctrl.hxx>
#endif

#ifndef _TOOLKIT_HELPER_VCLUNOHELPER_HXX_
#include <toolkit/helper/vclunohelper.hxx>
#endif

#include <vcl/tabpage.hxx>
#include <com/sun/star/awt/PosSize.hpp>
#include <sal/macros.h>

using namespace toolkit;
//........................................................................
namespace layoutimpl
{
//........................................................................

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star;

class TabChildProps : public PropHelper
{
public:
    TabChildProps( VCLXTabControl::ChildData *pData )
    {
        addProp( RTL_CONSTASCII_USTRINGPARAM( "Title" ),
                 ::getCppuType( static_cast< const rtl::OUString* >( NULL ) ),
                 &(pData->aTitle) );
    }
    PROPHELPER_SET_INFO
};

//====================================================================
//= VCLXTabControl
//====================================================================
DBG_NAME( VCLXTabControl )
//--------------------------------------------------------------------
VCLXTabControl::VCLXTabControl()
: VCLXWindow()
    , VCLXTabControl_Base()
    , Container()
{
    DBG_CTOR( VCLXTabControl, NULL );
    mnNextTabId = 1;
    mnChildrenNb = 0;
}

//--------------------------------------------------------------------
VCLXTabControl::~VCLXTabControl()
{
    DBG_DTOR( VCLXTabControl, NULL );
}

//--------------------------------------------------------------------
IMPLEMENT_2_FORWARD_XINTERFACE2( VCLXTabControl, VCLXWindow, Container, VCLXTabControl_Base )

//--------------------------------------------------------------------
IMPLEMENT_FORWARD_XTYPEPROVIDER2( VCLXTabControl, VCLXWindow, VCLXTabControl_Base )

//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::dispose( ) throw(RuntimeException)
{
    {
        ::vos::OGuard aGuard( GetMutex() );

        EventObject aDisposeEvent;
        aDisposeEvent.Source = *this;
//            maTabListeners.disposeAndClear( aDisposeEvent );
    }

    VCLXWindow::dispose();
}
/*
//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::addTabListener( const Reference< XTabListener >& listener ) throw (RuntimeException)
{
if ( listener.is() )
maTabListeners.addInterface( listener );
}

//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::removeTabListener( const Reference< XTabListener >& listener ) throw (RuntimeException)
{
if ( listener.is() )
maTabListeners.removeInterface( listener );
}
*/

//--------------------------------------------------------------------
TabControl *VCLXTabControl::getTabControl() const throw (RuntimeException)
{
    TabControl *pTabControl = static_cast< TabControl* >( GetWindow() );
    if ( pTabControl )
        return pTabControl;
    throw RuntimeException();
}

//--------------------------------------------------------------------
sal_Int32 SAL_CALL VCLXTabControl::insertTab() throw (RuntimeException)
{
    TabControl *pTabControl = getTabControl();
    int id = mnNextTabId++;
    rtl::OUString title (RTL_CONSTASCII_USTRINGPARAM( "" ) );
    pTabControl->InsertPage( id, title.getStr(), TAB_APPEND );
    pTabControl->SetTabPage( id, new TabPage( pTabControl ) );
    return id;
}

//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::removeTab( sal_Int32 ID ) throw (RuntimeException, IndexOutOfBoundsException)
{
    TabControl *pTabControl = getTabControl();
    if ( pTabControl->GetTabPage( ID ) == NULL )
        throw IndexOutOfBoundsException();
    pTabControl->RemovePage( ID );
}


//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::activateTab( sal_Int32 ID ) throw (RuntimeException, IndexOutOfBoundsException)
{
    TabControl *pTabControl = getTabControl();
    if ( pTabControl->GetTabPage( ID ) == NULL )
        throw IndexOutOfBoundsException();
    pTabControl->SelectTabPage( ID );
}

//--------------------------------------------------------------------
sal_Int32 SAL_CALL VCLXTabControl::getActiveTabID() throw (RuntimeException)
{
    return getTabControl()->GetCurPageId( );
}

//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::addTabListener( const ::com::sun::star::uno::Reference< ::com::sun::star::awt::XTabListener >& xListener ) throw (::com::sun::star::uno::RuntimeException)
{
    std::list< ::com::sun::star::uno::Reference
        < ::com::sun::star::awt::XTabListener > >::const_iterator it;
    for( it = mxTabListeners.begin(); it != mxTabListeners.end(); it++)
    {
        if ( *it == xListener )
            // already added
            return;
    }
    mxTabListeners.push_back( xListener );
}

//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::removeTabListener( const ::com::sun::star::uno::Reference< ::com::sun::star::awt::XTabListener >& xListener ) throw (::com::sun::star::uno::RuntimeException)
{
    std::list< ::com::sun::star::uno::Reference
        < ::com::sun::star::awt::XTabListener > >::iterator it;
    for( it = mxTabListeners.begin(); it != mxTabListeners.end(); it++)
    {
        if ( *it == xListener )
        {
            mxTabListeners.erase( it );
            break;
        }
    }
}

//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::setTabProps( sal_Int32 ID, const Sequence< NamedValue >& Properties ) throw (RuntimeException, IndexOutOfBoundsException)
{
    TabControl *pTabControl = getTabControl();
    if ( pTabControl->GetTabPage( ID ) == NULL )
        throw IndexOutOfBoundsException();

    for( int i = 0; i < Properties.getLength(); i++ )
    {
        const rtl::OUString &name = Properties[i].Name;
        const Any &value = Properties[i].Value;

        if ( name  == rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Title" ) ) )
        {
            rtl::OUString title = value.get<rtl::OUString>();
            pTabControl->SetPageText( ID, title.getStr() );
        }
    }
}

//--------------------------------------------------------------------
Sequence< NamedValue > SAL_CALL VCLXTabControl::getTabProps( sal_Int32 ID )
    throw (IndexOutOfBoundsException, RuntimeException)
{
    TabControl *pTabControl = getTabControl();
    if ( pTabControl->GetTabPage( ID ) == NULL )
        throw IndexOutOfBoundsException();

#define ADD_PROP( seq, i, name, val ) {                                \
        NamedValue value;                                                  \
        value.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( name ) ); \
        value.Value = makeAny( val );                                      \
        seq[i] = value;                                                    \
    }

    Sequence< NamedValue > props( 2 );
    ADD_PROP( props, 0, "Title", rtl::OUString( pTabControl->GetPageText( ID ) ) );
    ADD_PROP( props, 1, "Position", pTabControl->GetPagePos( ID ) );
#undef ADD_PROP
    return props;
}

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

// TODO: draw tab border here
void SAL_CALL VCLXTabControl::draw( sal_Int32 nX, sal_Int32 nY ) throw(::com::sun::star::uno::RuntimeException)
{
    ::vos::OGuard aGuard( GetMutex() );

    TabControl *pTabControl = getTabControl();
    TabPage *pTabPage = pTabControl->GetTabPage( getActiveTabID() );
    if ( pTabPage )
    {
        ::Point aPos( nX, nY );
        ::Size  aSize = pTabPage->GetSizePixel();

        OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( GetViewGraphics() );
        aPos  = pDev->PixelToLogic( aPos );
        aSize = pDev->PixelToLogic( aSize );

        pTabPage->Draw( pDev, aPos, aSize, 0 );
    }

    VCLXWindow::draw( nX, nY );
}

//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::addChild(
    const ::com::sun::star::uno::Reference< ::com::sun::star::awt::XLayoutConstrains > &xChild )
    throw (::com::sun::star::uno::RuntimeException, ::com::sun::star::awt::MaxChildrenException)
{
    unsigned int id = insertTab();
    if ( maChildren.size() < id )
        maChildren.resize( id, 0 );
    mnChildrenNb++;

    if ( xChild.is() )
    {
        ChildData *pData = new ChildData();
        pData->xChild = xChild;
        maChildren[ id-1 ] = pData;

        setChildParent( xChild );
        queueResize();
    }
}


//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::removeChild( const ::com::sun::star::uno::Reference< ::com::sun::star::awt::XLayoutConstrains > &xChild )
    throw (::com::sun::star::uno::RuntimeException)
{
    for( unsigned i = 0; i < maChildren.size(); i++)
    {
        if ( maChildren[ i ] && maChildren[ i ]->xChild == xChild )
        {
            removeTab( i );
            delete maChildren[ i ];
            maChildren[ i ] = NULL;
            mnChildrenNb--;

            unsetChildParent( xChild );
            queueResize();
            break;
        }
    }
}

//--------------------------------------------------------------------
::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference
                                 < ::com::sun::star::awt::XLayoutConstrains > > SAL_CALL VCLXTabControl::getChildren()
    throw (::com::sun::star::uno::RuntimeException)
{
    uno::Sequence< uno::Reference< awt::XLayoutConstrains > > childrenSeq( mnChildrenNb );
    for( unsigned si = 0, ci = 0; ci < maChildren.size(); ci++)
    {
        if ( maChildren[ ci ] && maChildren[ ci ]->xChild.is() )
            childrenSeq[si++] = maChildren[ ci ]->xChild;
    }
    return childrenSeq;
}

//--------------------------------------------------------------------
uno::Reference< beans::XPropertySet > SAL_CALL
VCLXTabControl::getChildProperties( const uno::Reference< awt::XLayoutConstrains >& xChild )
    throw (uno::RuntimeException)
{
    std::vector< ChildData * >::iterator iter;
    for( iter = maChildren.begin(); iter != maChildren.end(); iter++)
    {
        if ( (*iter)->xChild == xChild )
        {
            if ( !(*iter)->xProps.is() )
            {
                // FIXME: make me safe !
                PropHelper *pProps = new TabChildProps( *iter );
                pProps->setChangeListener( this );
                (*iter)->xProps = pProps;
            }
            return (*iter)->xProps;
        }
    }
    return uno::Reference< beans::XPropertySet >();
}

// TEMP:
static void setChildrenVisible( uno::Reference < awt::XLayoutConstrains > xChild, bool visible )
{
    uno::Reference< awt::XWindow > xWin( xChild, uno::UNO_QUERY);
    if ( xWin.is() )
    {
        xWin->setVisible( visible );
//xWin->setPosSize( 0, 0, 5, 5, PosSize::POSSIZE );
    }

    uno::Reference < awt::XLayoutContainer > xCont( xChild, uno::UNO_QUERY );
    if ( xCont.is())
    {
        uno::Sequence< uno::Reference < awt::XLayoutConstrains > > children = xCont->getChildren();
        for( int i = 0; i < children.getLength(); i++ )
        {
            setChildrenVisible( children[i], visible );
        }
    }
}

//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::allocateArea(
    const ::com::sun::star::awt::Rectangle &rArea )
    throw (::com::sun::star::uno::RuntimeException)
{
    maAllocation = rArea;

    TabControl *pTabControl = getTabControl();

// FIXME: this is wrong. We just want to set tab controls pos/size for the tabs menu,
// otherwise, it gets events that should go to children (I guess we could solve this
// by making the tabcontrol as the actual XWindow parent of its children, when importing...)
// Not sure about TabPage drawing... That doesn't work on gtk+; just ignoring that.
// LATER: Nah, the proper fix is to get the XWindow hierarchy straight.

    setPosSize( rArea.X, rArea.Y, rArea.Width, rArea.Height, PosSize::POSSIZE );

    // FIXME: we can save cycles by setting visibility more sensibly. Having
    // it here does makes it easier when changing tabs (just needs a recalc())
    for( unsigned int i = 0; i < maChildren.size(); i++ )
    {
        if ( !maChildren[ i ] )
            continue;
        ::com::sun::star::uno::Reference
              < ::com::sun::star::awt::XLayoutConstrains > xChild( maChildren[ i ]->xChild );
        if ( xChild.is() )
        {
            uno::Reference< awt::XWindow > xWin( xChild, uno::UNO_QUERY );
            bool active = (i+1 == (unsigned) getActiveTabID());

            // HACK: since our layout:: container don't implement XWindow, we have no easy
            // way to set them invisible; lets just set all their children as such :P
#if 0
            if ( xWin.is() )
                xWin->setVisible( active );
#else
            setChildrenVisible( xChild, active );
#endif

            if ( active )
            {
                ::Rectangle label_rect = pTabControl->GetTabBounds( i+1 );
                ::Rectangle page_rect = pTabControl->GetTabPageBounds( i+1 );

                awt::Rectangle childRect;
                childRect.X = page_rect.Left();
                childRect.Y = SAL_MAX( label_rect.Bottom(), page_rect.Top() );
                childRect.Width = page_rect.Right() - page_rect.Left();
                childRect.Height = page_rect.Bottom() - childRect.Y;

                allocateChildAt( xChild, childRect );
            }
        }
    }
}

//--------------------------------------------------------------------
::com::sun::star::awt::Size SAL_CALL VCLXTabControl::getMinimumSize()
    throw(::com::sun::star::uno::RuntimeException)
{
    awt::Size size = VCLXWindow::getMinimumSize();
    awt::Size childrenSize( 0, 0 );

    TabControl* pTabControl = static_cast< TabControl* >( GetWindow() );
    if ( !pTabControl )
        return size;

    // calculate size to accomodate all children
    for( unsigned int i = 0; i < maChildren.size(); i++ )
    {
        ChildData *pChild = maChildren[ i ];
        if ( pChild && pChild->xChild.is() )
        {
            // set the title prop here...
            pTabControl->SetPageText( i+1, pChild->aTitle.getStr() );

            awt::Size childSize( pChild->xChild->getMinimumSize() );
            childrenSize.Width = SAL_MAX( childSize.Width, childrenSize.Width );
            childrenSize.Height = SAL_MAX( childSize.Height, childrenSize.Height );
        }
    }

    size.Width += childrenSize.Width;
    size.Height += childrenSize.Height + 20;
    maRequisition = size;
    return size;
}

//--------------------------------------------------------------------
void VCLXTabControl::ProcessWindowEvent( const VclWindowEvent& _rVclWindowEvent )
{
    ::vos::OClearableGuard aGuard( GetMutex() );
    TabControl* pTabControl = static_cast< TabControl* >( GetWindow() );
    if ( !pTabControl )
        return;

    switch ( _rVclWindowEvent.GetId() )
    {
        case VCLEVENT_TABPAGE_ACTIVATE:
            forceRecalc();
        case VCLEVENT_TABPAGE_DEACTIVATE:
        case VCLEVENT_TABPAGE_INSERTED:
        case VCLEVENT_TABPAGE_REMOVED:
        case VCLEVENT_TABPAGE_REMOVEDALL:
        case VCLEVENT_TABPAGE_PAGETEXTCHANGED:
        {
            ULONG page = (ULONG) _rVclWindowEvent.GetData();
            std::list< ::com::sun::star::uno::Reference
                < ::com::sun::star::awt::XTabListener > >::iterator it;
            for( it = mxTabListeners.begin(); it != mxTabListeners.end(); it++)
            {
                ::com::sun::star::uno::Reference
                    < ::com::sun::star::awt::XTabListener > listener = *it;

                switch ( _rVclWindowEvent.GetId() )
                {

                    case VCLEVENT_TABPAGE_ACTIVATE:
                        listener->activated( page );
                        break;
                    case VCLEVENT_TABPAGE_DEACTIVATE:
                        listener->deactivated( page );
                        break;
                    case VCLEVENT_TABPAGE_INSERTED:
                        listener->inserted( page );
                        break;
                    case VCLEVENT_TABPAGE_REMOVED:
                        listener->removed( page );
                        break;
                    case VCLEVENT_TABPAGE_REMOVEDALL:
                        for( int i = 1; i < mnNextTabId; i++)
                        {
                            if ( pTabControl->GetTabPage( i ) )
                                listener->removed( i );
                        }
                        break;
                    case VCLEVENT_TABPAGE_PAGETEXTCHANGED:
                        listener->changed( page, getTabProps( page ) );
                        break;
                }
            }
            break;
        }
        default:
            aGuard.clear();
            VCLXWindow::ProcessWindowEvent( _rVclWindowEvent );
            break;
    }
}

//--------------------------------------------------------------------
void SAL_CALL VCLXTabControl::setProperty( const ::rtl::OUString& PropertyName, const Any &Value ) throw(RuntimeException)
{
    ::vos::OGuard aGuard( GetMutex() );

    if ( GetWindow() )
    {
        sal_uInt16 nPropertyId = GetPropertyId( PropertyName );
        switch ( nPropertyId )
        {
            default:
                VCLXWindow::setProperty( PropertyName, Value );
        }
    }
}

//--------------------------------------------------------------------
Any SAL_CALL VCLXTabControl::getProperty( const ::rtl::OUString& PropertyName ) throw(RuntimeException)
{
    ::vos::OGuard aGuard( GetMutex() );

    Any aReturn;
    if ( GetWindow() )
    {
        sal_uInt16 nPropertyId = GetPropertyId( PropertyName );
        switch ( nPropertyId )
        {
            default:
                aReturn = VCLXWindow::getProperty( PropertyName );
        }
    }
    return aReturn;
}

//........................................................................
} // namespace toolkit
//........................................................................