/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



#include "root.hxx"

#include <cassert>

#include <com/sun/star/awt/WindowAttribute.hpp>
#include <com/sun/star/awt/XMessageBox.hpp>
#include <com/sun/star/awt/MessageBoxButtons.hpp>
#include <com/sun/star/frame/XDesktop.hpp>
#include <com/sun/star/awt/XMessageBoxFactory.hpp>
#include <com/sun/star/xml/sax/SAXParseException.hpp>
#include <com/sun/star/xml/sax/XParser.hpp>

#include "helper.hxx"
#include "import.hxx"
#include "timer.hxx"
#include "translate.hxx"

namespace layoutimpl
{

using namespace css;
using ::rtl::OUString;

LayoutRoot::LayoutRoot( const uno::Reference< lang::XMultiServiceFactory >& xFactory )
    : mbDisposed( sal_False )
    , mxFactory( xFactory )
    , mpListeners( NULL )
    , mpToplevel( NULL )
{
    if ( !xFactory.is() )
        throw uno::RuntimeException();
    mxLayoutUnit = uno::Reference< awt::XLayoutUnit >( new LayoutUnit() );
}

LayoutRoot::~LayoutRoot()
{
// TODO: we want to delete the top level LayoutWidget...
    ::osl::MutexGuard aGuard( maMutex );
    if ( !mbDisposed )
    {
        try
        {
            m_refCount++; // inhibit multiple destruction
            dispose();
        }
        catch( uno::Exception& )
        {
        }
    }
}

void ShowMessageBox( uno::Reference< lang::XMultiServiceFactory > const& xFactory, uno::Reference< awt::XToolkit > xToolkit, OUString const& aTitle, OUString const& aMessage )
{
    uno::Reference< uno::XInterface > iDesktop = xFactory->createInstance
        ( OUString::createFromAscii( "com.sun.star.frame.Desktop" ) );
    uno::Reference< frame::XDesktop > xDesktop ( iDesktop, uno::UNO_QUERY );
    uno::Reference< frame::XFrame > xFrame ( xDesktop->getCurrentFrame() );
    uno::Reference< awt::XWindow > xContainerWindow( xFrame->getContainerWindow() );
    uno::Reference< awt::XWindowPeer > xWindowPeer( xContainerWindow, uno::UNO_QUERY_THROW );
    uno::Reference< awt::XMessageBoxFactory > xMessageBoxFactory( xToolkit, uno::UNO_QUERY );

    awt::Rectangle aRectangle;
    uno::Reference< awt::XMessageBox > xMessageBox
        = xMessageBoxFactory->createMessageBox
        ( xWindowPeer, aRectangle, OUString::createFromAscii( "errorbox" ),
          awt::MessageBoxButtons::BUTTONS_OK, aTitle, aMessage );

    if ( xMessageBox.is() )
        xMessageBox->execute();
    //FIXME: exceptions not caught and printed at top level??
    //else
    //printf( "%s\n", OUSTRING_CSTR( aMessage ) );
}

void LayoutRoot::error( OUString const& message )
{
    OSL_TRACE( "%s\n", OUSTRING_CSTR( message ) );
    ShowMessageBox( mxFactory, mxToolkit,
                    OUString::createFromAscii( "Fatal error" ),
                    message );
    throw uno::RuntimeException( message, uno::Reference< uno::XInterface >() );
}

// XInitialization
void SAL_CALL LayoutRoot::initialize( const uno::Sequence< uno::Any >& aArguments )
    throw ( uno::Exception,
            uno::RuntimeException )
{
    ::osl::MutexGuard aGuard( maMutex );

    if ( mbDisposed )
        throw lang::DisposedException();

    if ( mxContainer.is() ) // only 1 init ...
        throw uno::Exception();

    if ( !aArguments.getLength() )
        throw lang::IllegalArgumentException();

    OSL_ENSURE( aArguments.getLength() == 1, "Wrong arg count\n" );

    OUString aXMLName;
    if ( !( aArguments[0] >>= aXMLName ) )
        throw lang::IllegalArgumentException();

    uno::Reference< xml::sax::XParser > xParser
        ( mxFactory->createInstance(
            OUString::createFromAscii( "com.sun.star.xml.sax.Parser" ) ),
          uno::UNO_QUERY );
    OSL_ASSERT( xParser.is() );
    if (! xParser.is())
    {
        throw uno::RuntimeException(
            OUString::createFromAscii( "cannot create sax-parser component" ),
            uno::Reference< uno::XInterface >() );
    }

    // FIXME: quite possibly we want to pass this in ...
    uno::Reference< awt::XToolkit > xToolkit;

    mxToolkit = uno::Reference< awt::XToolkit >(
        mxFactory->createInstance(
            OUString::createFromAscii( "com.sun.star.awt.Toolkit" ) ),
        uno::UNO_QUERY );

    if ( !mxToolkit.is() )
        throw uno::RuntimeException(
            OUString::createFromAscii( "failed to create toolkit!" ),
            uno::Reference< uno::XInterface >() );

    OUString aXMLFile = readRightTranslation( aXMLName );
    uno::Reference< io::XInputStream > xStream = getFileAsStream( aXMLFile );
    if (! xStream.is() )
        error( OUString::createFromAscii( "Installation problem: cannot find XML file:" ) + aXMLName );

    // error handler, entity resolver omitted

    ImportContext *pCtx = new ImportContext( *this );

    uno::Reference< xml::input::XRoot > xRoot( pCtx );
    uno::Sequence < uno::Any > aArgs( 1 );
    aArgs[0] <<= xRoot;
    uno::Reference< xml::sax::XDocumentHandler > xDocHandler
        (mxFactory->createInstanceWithArguments
         ( OUString::createFromAscii( "com.sun.star.xml.input.SaxDocumentHandler" ),
          aArgs ), uno::UNO_QUERY );

    if (! xDocHandler.is() )
        error( OUString::createFromAscii( "cannot find SAx handler for document type of:") + aXMLName );
        
    xParser->setDocumentHandler( xDocHandler );

    xml::sax::InputSource source;
    source.aInputStream = xStream;
    source.sSystemId = OUString::createFromAscii( "virtual file" );

    try
    {
        xParser->parseStream( source );
    }
    catch ( xml::sax::SAXParseException& e )
    {
        OUString c = OUString::createFromAscii( ":" );
        error( aXMLName
               + c + OUString::valueOf( e.LineNumber )
               + c + OUString::valueOf( e.ColumnNumber )
               + c + OUString::createFromAscii( "Sax parse error" ) );
    }
}

// XLayoutContainer
uno::Reference< awt::XLayoutContainer > LayoutRoot::getLayoutContainer() throw (uno::RuntimeException)
{
    return uno::Reference< awt::XLayoutContainer >();
}

// local helper ...
void LayoutRoot::addItem( const OUString &rName,
                          const uno::Reference< awt::XLayoutConstrains > &xRef )
{
    maItems[ rName ] = xRef;
}

// XNameAccess
uno::Any SAL_CALL LayoutRoot::getByName( const OUString &rName )
    throw ( container::NoSuchElementException,
            lang::WrappedTargetException,
            uno::RuntimeException )
{
    ::osl::MutexGuard aGuard( maMutex );
    if ( mbDisposed )
        throw lang::DisposedException();

    uno::Reference< awt::XLayoutConstrains > xItem;
    ItemHash::iterator i = maItems.find( rName );
    if ( i != maItems.end() )
        xItem = i->second;
    return uno::makeAny( xItem );
}

sal_Bool SAL_CALL LayoutRoot::hasByName( const OUString &rName )
    throw (uno::RuntimeException)
{
    ::osl::MutexGuard aGuard( maMutex );
    if ( mbDisposed ) throw lang::DisposedException();

    ItemHash::iterator i = maItems.find( rName );
    return i != maItems.end();
}

uno::Sequence< OUString > SAL_CALL LayoutRoot::getElementNames()
    throw ( uno::RuntimeException )
{
    ::osl::MutexGuard aGuard( maMutex );
    if ( mbDisposed ) throw lang::DisposedException();

    uno::Sequence< OUString > aNames( maItems.size() );
    sal_Int32 nPos = 0;

    for ( ItemHash::const_iterator it = maItems.begin();
          it != maItems.end(); it++ )
        aNames[ nPos++ ] = it->first;

    return aNames;
}

uno::Type SAL_CALL LayoutRoot::getElementType()
    throw ( uno::RuntimeException )
{
    return getCppuType( ( const uno::Reference< awt::XLayoutConstrains >* )NULL );
}

sal_Bool SAL_CALL LayoutRoot::hasElements()
    throw ( uno::RuntimeException )
{
    ::osl::MutexGuard aGuard( maMutex );

    if ( mbDisposed ) throw lang::DisposedException();

    return maItems.size() > 0;
}

// XComponent
void SAL_CALL LayoutRoot::dispose()
    throw ( uno::RuntimeException )
{
    ::osl::MutexGuard aGuard( maMutex );

    if ( mbDisposed ) throw lang::DisposedException();

    if ( mpListeners )
    {

        lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) );
        mpListeners->disposeAndClear( aSource );
        delete mpListeners;
        mpListeners = NULL;
    }

    maItems.clear();
    mbDisposed = sal_True;
}

void SAL_CALL LayoutRoot::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
    throw ( uno::RuntimeException )
{
    ::osl::MutexGuard aGuard( maMutex );

    if ( mbDisposed ) throw lang::DisposedException();

    if ( !mpListeners )
        mpListeners = new ::cppu::OInterfaceContainerHelper( maMutex );
    mpListeners->addInterface( xListener );
}

void SAL_CALL LayoutRoot::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
    throw ( uno::RuntimeException )
{
    ::osl::MutexGuard aGuard( maMutex );

    if ( mbDisposed ) throw lang::DisposedException();

    if ( mpListeners )
        mpListeners->removeInterface( xListener );
}

// builder

LayoutWidget *LayoutRoot::create( OUString id, const OUString unoName, long attrbs,uno::Reference< awt::XLayoutContainer > xParent )
{
    LayoutWidget *pWidget = new LayoutWidget( mxToolkit, xParent, unoName, attrbs );
    if ( !mpToplevel )
    {
        mpToplevel = pWidget;
        mxWindow = uno::Reference< awt::XWindow >( pWidget->getPeer(), uno::UNO_QUERY );
        mxContainer = pWidget->mxContainer;
    }
    if ( pWidget->mxContainer.is() )
        pWidget->mxContainer->setLayoutUnit( mxLayoutUnit );
    if ( id.getLength() )
        maItems[ id ] = pWidget->getPeer();
    return pWidget;
}

#if 0
uno::Reference< awt::XLayoutConstrains > LayoutRoot::getToplevel()
{
    if ( mpToplevel )
        return mpToplevel->getPeer();
    return uno::Reference< awt::XLayoutConstrains > ();
}

uno::Reference< awt::XLayoutConstrains > LayoutRoot::getById( OUString id )
{
    uno::Reference< awt::XLayoutConstrains > rRef = 0;
    ItemHash::iterator it = maItems.find( id );
    if ( it != maItems.end() )
        rRef = it->second;
    return rRef;
}
#endif

LayoutWidget::LayoutWidget( uno::Reference< awt::XToolkit > xToolkit,
                            uno::Reference< awt::XLayoutContainer > xParent,
                            OUString unoName, long attrbs )
{
    while ( xParent.is() && !uno::Reference< awt::XWindow >( xParent, uno::UNO_QUERY ).is() )
    {
        uno::Reference< awt::XLayoutContainer > xContainer( xParent, uno::UNO_QUERY );
        assert( xContainer.is() );
        xParent = uno::Reference< awt::XLayoutContainer >( xContainer->getParent(), uno::UNO_QUERY );
    }

    mxWidget = WidgetFactory::createWidget( xToolkit, xParent, unoName, attrbs );
    assert( mxWidget.is() );
    mxContainer = uno::Reference< awt::XLayoutContainer >( mxWidget, uno::UNO_QUERY );
}

LayoutWidget::~LayoutWidget()
{
    /* should we dispose of the references...? */
    // at least of its children... Or should root?
}

bool LayoutWidget::addChild( LayoutWidget *pChild )
{
    if ( !mxContainer.is() )
        return false;

    try
    {
        mxContainer->addChild( pChild->mxWidget );
    }
    catch( awt::MaxChildrenException ex )
    {
        return false;
    }
    return true;
}

void LayoutWidget::setProperties( PropList const& rProps )
{
    ::layoutimpl::setProperties( mxWidget, rProps );
}

void LayoutWidget::setProperty( OUString const& attr, OUString const& value )
{
    ::layoutimpl::setProperty( mxWidget, attr, value );
}

void LayoutWidget::setChildProperties( LayoutWidget *pChild,
                                       PropList const& rProps )
{
    uno::Reference< beans::XPropertySet > xChildPeer;
    xChildPeer = mxContainer->getChildProperties( pChild->mxWidget );

    if ( xChildPeer.is() )
        ::layoutimpl::setProperties( xChildPeer, rProps );
}

} // namespace layoutimpl

