#include "wrapper.hxx"

#include <com/sun/star/awt/XFixedText.hpp>
#include <com/sun/star/awt/XDialog2.hpp>
#include <com/sun/star/awt/WindowAttribute.hpp>
#include <com/sun/star/awt/VclWindowPeerAttribute.hpp>
#include <comphelper/processfactory.hxx>
#include <vcl/window.hxx>
#include <toolkit/awt/vclxwindow.hxx>

#include "layoutcore.hxx"
#include "../layout/factory.hxx"
#include "../layout/root.hxx"

using namespace ::com::sun::star;

namespace layout
{

// Context bits ...
class ContextImpl
{
    uno::Reference< awt::XLayoutRoot > mxRoot;
    uno::Reference< container::XNameAccess > mxNameAccess;
    PeerHandle mxTopLevel;

public:
    ContextImpl( char const *pPath )
    {
        uno::Sequence< uno::Any > aParams( 1 );
        aParams[0] <<= rtl::OUString( pPath, strlen( pPath ), RTL_TEXTENCODING_UTF8 );

        uno::Reference< lang::XSingleServiceFactory > xFactory(
            comphelper::createProcessComponent(
                rtl::OUString::createFromAscii( "com.sun.star.awt.Layout" ) ),
            uno::UNO_QUERY );
        if ( !xFactory.is() )
        {
            throw uno::RuntimeException(
                rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Layout engine not installed" ) ),
                uno::Reference< uno::XInterface >() );
        }
        mxRoot = uno::Reference< awt::XLayoutRoot >(
            xFactory->createInstanceWithArguments( aParams ),
            uno::UNO_QUERY );

        mxNameAccess = uno::Reference< container::XNameAccess >( mxRoot, uno::UNO_QUERY );
    }

    ~ContextImpl()
    {
    }

    PeerHandle getByName( const rtl::OUString &rName )
    {
        uno::Any val = mxNameAccess->getByName( rName );
        PeerHandle xRet;
        val >>= xRet;
        return xRet;
    }
    PeerHandle getTopLevel()                 { return mxTopLevel; }
    void setTopLevel( PeerHandle xToplevel ) { mxTopLevel = xToplevel; }
    PeerHandle getRoot()                     { return mxRoot; }
};

Context::Context( const char *pPath )
    : pImpl( new ContextImpl( pPath ) )
{
}
Context::~Context()
{
    delete pImpl;
    pImpl = NULL;
}

void Context::setToplevel( PeerHandle xToplevel )
{
    pImpl->setTopLevel( xToplevel );
}

PeerHandle Context::getToplevel()
{
    return pImpl->getTopLevel();
}
PeerHandle Context::getRoot()
{
     return pImpl->getRoot();
}

PeerHandle Context::GetPeerHandle( const char *pId, sal_uInt32 nId ) const
{
    PeerHandle xHandle;
    xHandle = pImpl->getByName( rtl::OUString( pId, strlen( pId ), RTL_TEXTENCODING_UTF8 ) );
    if ( !xHandle.is() )
    {
        DBG_ERROR1( "Failed to fetch widget '%s'", pId );
    }

    if ( nId != 0 )
    {
        rtl::OString aStr = rtl::OString::valueOf( (sal_Int32) nId );
        xHandle = GetPeerHandle( aStr, 0 );
    }
    return xHandle;
}

// Window/Dialog
class DialogImpl : public WindowImpl
{
public:
    uno::Reference< awt::XDialog2 > mxDialog;
    DialogImpl( Context *pCtx, const PeerHandle &xPeer, Window *pWindow )
        : WindowImpl( pCtx, xPeer, pWindow )
        , mxDialog( xPeer, uno::UNO_QUERY )
    {
    }
};

// Accessors
DECL_GET_IMPL_IMPL( Control )
DECL_GET_IMPL_IMPL( Dialog )

Window::Window( WindowImpl *pImpl )
: mpImpl( pImpl )
{
}

Window::~Window()
{
    /* likely to be an UNO object - with floating references */
    mpImpl->wrapperGone();
    mpImpl = NULL;
}

Context *Window::getContext()
{
    return this && mpImpl ? mpImpl->mpCtx : NULL;
}

PeerHandle Window::GetPeer()
{
    if (!mpImpl)
        return PeerHandle();
    return mpImpl->mxWindow;
}

struct ToolkitVclPropsMap
{
    WinBits vclStyle;
    long initAttr;
    const char *propName;

    // the value to give the prop to enable/disable it -- not the most brilliant
    // type declaration and storage, but does the work... properties are
    // either a boolean or a short since they are either a directly wrappers for
    // a WinBit, or aggregates related (like Align for WB_LEFT, _RIGHT and _CENTER).
    bool isBoolean;
    short enableProp, disableProp;
};

#define TYPE_BOOL  true
#define TYPE_SHORT false
#define NOTYPE     0
static const ToolkitVclPropsMap toolkitVclPropsMap[] =
{
    { WB_BORDER,    awt::WindowAttribute::BORDER,    "Border", TYPE_SHORT, 1, 0 },
    { WB_NOBORDER,    awt::VclWindowPeerAttribute::NOBORDER,    "Border", TYPE_SHORT, 0, 1 },
    { WB_SIZEABLE,    awt::WindowAttribute::SIZEABLE,    NULL, NOTYPE, 0, 0 },
    { WB_MOVEABLE,    awt::WindowAttribute::MOVEABLE,    NULL, NOTYPE, 0, 0 },
    { WB_CLOSEABLE,    awt::WindowAttribute::CLOSEABLE,    NULL, NOTYPE, 0, 0 },

    { WB_HSCROLL,    awt::VclWindowPeerAttribute::HSCROLL,    NULL, NOTYPE, 0, 0 },
    { WB_VSCROLL,    awt::VclWindowPeerAttribute::VSCROLL,    NULL, NOTYPE, 0, 0 },
    { WB_LEFT,    awt::VclWindowPeerAttribute::LEFT,    "Align", TYPE_SHORT, 0, 0 },
    { WB_CENTER,    awt::VclWindowPeerAttribute::CENTER,    "Align", TYPE_SHORT, 1, 0 },
    { WB_RIGHT,    awt::VclWindowPeerAttribute::RIGHT,    "Align", TYPE_SHORT, 2, 0 },
    { WB_SPIN,    awt::VclWindowPeerAttribute::SPIN,    NULL, NOTYPE, 0, 0 },
    { WB_SORT,    awt::VclWindowPeerAttribute::SORT,    NULL, NOTYPE, 0, 0 },
    { WB_DROPDOWN,    awt::VclWindowPeerAttribute::DROPDOWN,    "Dropdown",    TYPE_BOOL, 1, 0 },
    { WB_DEFBUTTON,    awt::VclWindowPeerAttribute::DEFBUTTON,    "DefaultButton", TYPE_BOOL, 1, 0 },
    { WB_READONLY,    awt::VclWindowPeerAttribute::READONLY,    NULL, NOTYPE, 0, 0 },
    { WB_CLIPCHILDREN,    awt::VclWindowPeerAttribute::CLIPCHILDREN,    NULL, NOTYPE, 0, 0 },
    { WB_GROUP,    awt::VclWindowPeerAttribute::GROUP,    NULL, NOTYPE, 0, 0 },

    { WB_OK,    awt::VclWindowPeerAttribute::OK,    NULL, NOTYPE, 0, 0 },
    { WB_OK_CANCEL,    awt::VclWindowPeerAttribute::OK_CANCEL,    NULL, NOTYPE, 0, 0 },
    { WB_YES_NO,    awt::VclWindowPeerAttribute::YES_NO,    NULL, NOTYPE, 0, 0 },
    { WB_YES_NO_CANCEL,    awt::VclWindowPeerAttribute::YES_NO_CANCEL,    NULL, NOTYPE, 1, 0 },
    { WB_RETRY_CANCEL,    awt::VclWindowPeerAttribute::RETRY_CANCEL,    NULL, NOTYPE, 1, 0 },
    { WB_DEF_OK,    awt::VclWindowPeerAttribute::DEF_OK,    NULL, NOTYPE, 0, 0 },
    { WB_DEF_CANCEL,    awt::VclWindowPeerAttribute::DEF_CANCEL,    NULL, NOTYPE, 1, 0 },
    { WB_DEF_RETRY,    awt::VclWindowPeerAttribute::DEF_RETRY,    NULL, NOTYPE, 0, 0 },
    { WB_DEF_YES,    awt::VclWindowPeerAttribute::DEF_YES,    NULL, NOTYPE, 0, 0 },
    { WB_DEF_NO,    awt::VclWindowPeerAttribute::DEF_NO,    NULL, NOTYPE, 0, 0 },

    { WB_AUTOHSCROLL, awt::VclWindowPeerAttribute::AUTOHSCROLL, "AutoHScroll", TYPE_BOOL, 1, 0 },
    { WB_AUTOVSCROLL, awt::VclWindowPeerAttribute::AUTOVSCROLL, "AutoVScroll",    TYPE_BOOL, 1, 0 },

    { WB_WORDBREAK,    0,    "MultiLine", TYPE_BOOL, 1, 0 },
    { WB_NOPOINTERFOCUS,    0,    "FocusOnClick", TYPE_BOOL, 1, 0 },
    { WB_TOGGLE,    0,    "Toggle", TYPE_BOOL, 1, 0 },
    { WB_REPEAT,    0,    "Repeat", TYPE_BOOL, 1, 0 },
    { WB_NOHIDESELECTION,    0,    "HideInactiveSelection", TYPE_BOOL, 1, 0 },
};
#undef TYPE_BOOL
#undef TYPE_SHORT
#undef NOTYPE

static const int toolkitVclPropsMapLen =
    sizeof( toolkitVclPropsMap ) / sizeof( ToolkitVclPropsMap );

void Window::SetStyle( WinBits nStyle )
{
    uno::Reference< awt::XVclWindowPeer > xPeer = mpImpl->mxVclPeer;
    for( int i = 0; i < toolkitVclPropsMapLen; i++ )
    {
        if ( toolkitVclPropsMap[ i ].propName )
        {
            short nValue;
            if ( nStyle & toolkitVclPropsMap[ i ].vclStyle )
                nValue = toolkitVclPropsMap[ i ].enableProp;
            else
                nValue = toolkitVclPropsMap[ i ].disableProp;
            uno::Any aValue;
            if ( toolkitVclPropsMap[ i ].isBoolean )
                aValue = uno::makeAny( (bool) nValue );
            else
                aValue = uno::makeAny( (short) nValue );
            mpImpl->setProperty( toolkitVclPropsMap[ i ].propName, aValue );
        }
    }
}

WinBits Window::GetStyle()
{
    uno::Reference< awt::XVclWindowPeer > xPeer = mpImpl->mxVclPeer;
    WinBits ret = 0;
    for( int i = 0; i < toolkitVclPropsMapLen; i++ )
    {
        if ( toolkitVclPropsMap[ i ].propName )
        {
            short nValue;
            if ( toolkitVclPropsMap[ i ].isBoolean )
            {
                bool bValue;
                mpImpl->getProperty( toolkitVclPropsMap[ i ].propName ) >>= bValue;
                nValue = bValue ? 1 : 0;
            }
            else
                mpImpl->getProperty( toolkitVclPropsMap[ i ].propName ) >>= nValue;
            if ( nValue == toolkitVclPropsMap[ i ].enableProp )
                ret |= toolkitVclPropsMap[i].vclStyle;
        }
    }
    return ret;
}

/* Unpleasant way to get an xToolkit pointer ... */
uno::Reference< awt::XToolkit > getToolkit()
{
    static uno::Reference< awt::XToolkit > xToolkit;
    if (!xToolkit.is())
    {
        // Urgh ...
        xToolkit = uno::Reference< awt::XToolkit >(
            ::comphelper::getProcessServiceFactory()->createInstance(
                rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.Toolkit" ) ) ),
            uno::UNO_QUERY );
        if ( !xToolkit.is() )
            throw uno::RuntimeException(
                rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "failed to create toolkit!") ),
                uno::Reference< uno::XInterface >() );
    }
    return xToolkit;
}

PeerHandle Window::CreatePeer( Window *pParent, WinBits nStyle, const char *pName)
{

    long nWinAttrbs = 0;
    for( int i = 0; i < toolkitVclPropsMapLen; i++ )
        if ( nStyle & toolkitVclPropsMap[ i ].vclStyle )
            nWinAttrbs |= toolkitVclPropsMap[ i ].initAttr;

    return layoutimpl::createWidget( getToolkit(), pParent->GetPeer(),
                                     rtl::OUString::createFromAscii( pName ), nWinAttrbs );


#if 0
    awt::WindowDescriptor desc;

    // debugging help ...
    desc.Bounds.X = 0;
    desc.Bounds.Y = 0;
    desc.Bounds.Width = 300;
    desc.Bounds.Height = 200;

    desc.ParentIndex = 0;
    // FIXME: this code should be shared with
    // toolkit/source/awt/vclxtoolkit.cxx (ImplGetWinBits)
    desc.WindowAttributes = 0;
    for( int i = 0; i < toolkitVclPropsMapLen; i++ )
        if ( nStyle & toolkitVclPropsMap[ i ].vclStyle )
            desc.WindowAttributes |= toolkitVclPropsMap[ i ].initAttr;
    desc.WindowServiceName = rtl::OUString( pName, strlen( pName ),
                                            RTL_TEXTENCODING_ASCII_US );

    // FIXME: we really want to walk up the peer hierarchy
    // and get the top-level that this requires.
    if ( pParent != NULL )
    {
        PeerHandle hdl = pParent->GetPeer();
        desc.Parent = uno::Reference< awt::XWindowPeer >( hdl, uno::UNO_QUERY );
    }

    PeerHandle xPeer = getToolkit()->createWindow( desc );

    // not all WinBits have an equivalent attribute, some are available through
    // properties though
    for( int i = 0; i < toolkitVclPropsMapLen; i++ )
        if ( nStyle & toolkitVclPropsMap[ i ].vclStyle &&
            !toolkitVclPropsMap[ i ].initAttr &&
            toolkitVclPropsMap[ i ].propName )
        {
            uno::Any aValue;
            if ( toolkitVclPropsMap[ i ].isBoolean )
                aValue = uno::makeAny( (bool) toolkitVclPropsMap[ i ].enableProp );
            else
                aValue = uno::makeAny( (short) toolkitVclPropsMap[ i ].enableProp );
            layoutimpl::prophlp::setProperty( xPeer,
                                              rtl::OUString::createFromAscii( toolkitVclPropsMap[ i ].propName ), aValue );
        }

    return xPeer;
#endif
}

void Window::Enable( bool bEnable )
{
    if ( !getImpl().mxWindow.is() )
        return;
    getImpl().mxWindow->setEnable( bEnable );
}

void Window::Show( BOOL bVisible )
{
    if ( !getImpl().mxWindow.is() )
        return;
    getImpl().mxWindow->setVisible( bVisible );
}

void Window::GrabFocus()
{
    if ( !getImpl().mxWindow.is() )
        return;
    getImpl().mxWindow->setFocus();
}

Dialog::Dialog( Window *pParent, const char *pXMLPath, const char *pId, sal_uInt32 nId )
    : Context( pXMLPath )
    , Window( new DialogImpl( this, Context::GetPeerHandle( pId, nId ), this ) )
{
    if ( pParent )
        SetParent( pParent );
}

Dialog::Dialog( ::Window *pParent, const char *pXMLPath, const char *pId, sal_uInt32 nId )
    : Context( pXMLPath )
    , Window( new DialogImpl( this, Context::GetPeerHandle( pId, nId ), this ) )
{
    if ( pParent )
        SetParent( pParent );
}

void Dialog::SetParent( ::Window *pParent )
{
    uno::Reference <awt::XWindow> ref( GetPeer(), uno::UNO_QUERY );
    ::Window *window = VCLXWindow::GetImplementation( ref )->GetWindow();
    window->SetParent( pParent );
}

void Dialog::SetParent( Window *pParent )
{
    uno::Reference <awt::XWindow> parentRef( pParent->GetPeer(), uno::UNO_QUERY );
    ::Window *parentWindow = VCLXWindow::GetImplementation( parentRef )->GetWindow();
    SetParent( parentWindow );
}

short Dialog::Execute()
{
    if ( !getImpl().mxDialog.is() )
        return -1;
    return getImpl().mxDialog->execute();
}

void Dialog::EndDialog( long nResult )
{
    if ( !getImpl().mxDialog.is() )
        return;
    getImpl().mxDialog->endDialog( nResult );
}

void Dialog::SetText( const String& rStr )
{
    if ( !getImpl().mxDialog.is() )
        return;
    getImpl().mxDialog->setTitle( rStr );
}

class FixedLineImpl : public ControlImpl
{
public:
    FixedLineImpl( Context *pCtx, const PeerHandle &xPeer, Window *pWindow )
        : ControlImpl( pCtx, xPeer, pWindow )
    {
    }
};

DECL_CONSTRUCTOR_IMPLS( FixedLine, Control, "hfixedline" );
DECL_GET_IMPL_IMPL( FixedLine )

class FixedTextImpl : public ControlImpl
{
public:
    uno::Reference< awt::XFixedText > mxFixedText;
    FixedTextImpl( Context *pCtx, const PeerHandle &xPeer, Window *pWindow )
        : ControlImpl( pCtx, xPeer, pWindow )
        , mxFixedText( xPeer, uno::UNO_QUERY )
    {
    }

    virtual void SAL_CALL disposing( const css::lang::EventObject& /* Source */ )
        throw (css::uno::RuntimeException)
    {
        mxFixedText.clear();
    }
};

DECL_CONSTRUCTOR_IMPLS( FixedText, Control, "fixedtext" );
DECL_GET_IMPL_IMPL( FixedText )

void FixedText::SetText( const String& rStr )
{
    if ( !getImpl().mxFixedText.is() )
        return;
    getImpl().mxFixedText->setText( rStr );
}

class FixedInfoImpl : public FixedTextImpl
{
public:
    FixedInfoImpl( Context *pCtx, const PeerHandle &xPeer, Window *pWindow )
        : FixedTextImpl( pCtx, xPeer, pWindow )
    {
    }
};

DECL_CONSTRUCTOR_IMPLS( FixedInfo, FixedText, "fixedinfo" );
DECL_GET_IMPL_IMPL( FixedInfo )

}; // end namespace layout