/**************************************************************
 * 
 * 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_ucb.hxx"

/**************************************************************************
                                TODO
 **************************************************************************

 *************************************************************************/

#include <hash_map>
#include <osl/diagnose.h>
#include <cppuhelper/weak.hxx>
#include <ucbhelper/contentidentifier.hxx>
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
#include "pkgprovider.hxx"
#include "pkgcontent.hxx"
#include "pkguri.hxx"

using namespace com::sun::star;

namespace package_ucp
{

//=========================================================================
//
// class Package.
//
//=========================================================================

class Package : public cppu::OWeakObject,
                public container::XHierarchicalNameAccess
{
    friend class ContentProvider;

    rtl::OUString                                        m_aName;
    uno::Reference< container::XHierarchicalNameAccess > m_xNA;
    ContentProvider*                                     m_pOwner;

public:
    Package( const rtl::OUString& rName,
             const uno::Reference< container::XHierarchicalNameAccess > & xNA,
             ContentProvider* pOwner )
    : m_aName( rName ), m_xNA( xNA ), m_pOwner( pOwner ) {}
    virtual ~Package() { m_pOwner->removePackage( m_aName ); }

    // XInterface
    virtual uno::Any SAL_CALL
    queryInterface( const uno::Type& aType )
        throw( uno::RuntimeException )
    { return m_xNA->queryInterface( aType ); }
    virtual void SAL_CALL
    acquire() throw()
    { OWeakObject::acquire(); }
    virtual void SAL_CALL
    release() throw()
    { OWeakObject::release(); }

    // XHierarchicalNameAccess
    virtual uno::Any SAL_CALL
    getByHierarchicalName( const rtl::OUString& aName )
        throw( container::NoSuchElementException, uno::RuntimeException )
    { return m_xNA->getByHierarchicalName( aName ); }
    virtual sal_Bool SAL_CALL
    hasByHierarchicalName( const rtl::OUString& aName )
        throw( uno::RuntimeException )
    { return m_xNA->hasByHierarchicalName( aName ); }
};

//=========================================================================
//
// Packages.
//
//=========================================================================

struct equalString
{
    bool operator()(
        const rtl::OUString& rKey1, const rtl::OUString& rKey2 ) const
    {
        return !!( rKey1 == rKey2 );
    }
};

struct hashString
{
    size_t operator()( const rtl::OUString & rName ) const
    {
        return rName.hashCode();
    }
};

typedef std::hash_map
<
    rtl::OUString,
    Package*,
    hashString,
    equalString
>
PackageMap;

class Packages : public PackageMap {};

}

using namespace package_ucp;

//=========================================================================
//=========================================================================
//
// ContentProvider Implementation.
//
//=========================================================================
//=========================================================================

ContentProvider::ContentProvider(
            const uno::Reference< lang::XMultiServiceFactory >& rSMgr )
: ::ucbhelper::ContentProviderImplHelper( rSMgr ),
  m_pPackages( 0 )
{
}

//=========================================================================
// virtual
ContentProvider::~ContentProvider()
{
    delete m_pPackages;
}

//=========================================================================
//
// XInterface methods.
//
//=========================================================================

XINTERFACE_IMPL_3( ContentProvider,
                   lang::XTypeProvider,
                   lang::XServiceInfo,
                   ucb::XContentProvider );

//=========================================================================
//
// XTypeProvider methods.
//
//=========================================================================

XTYPEPROVIDER_IMPL_3( ContentProvider,
                      lang::XTypeProvider,
                      lang::XServiceInfo,
                      ucb::XContentProvider );

//=========================================================================
//
// XServiceInfo methods.
//
//=========================================================================

XSERVICEINFO_IMPL_1( ContentProvider,
                     rtl::OUString::createFromAscii(
                        "com.sun.star.comp.ucb.PackageContentProvider" ),
                     rtl::OUString::createFromAscii(
                        PACKAGE_CONTENT_PROVIDER_SERVICE_NAME ) );

//=========================================================================
//
// Service factory implementation.
//
//=========================================================================

ONE_INSTANCE_SERVICE_FACTORY_IMPL( ContentProvider );

//=========================================================================
//
// XContentProvider methods.
//
//=========================================================================

// virtual
uno::Reference< ucb::XContent > SAL_CALL ContentProvider::queryContent(
            const uno::Reference< ucb::XContentIdentifier >& Identifier )
    throw( ucb::IllegalIdentifierException, uno::RuntimeException )
{
    if ( !Identifier.is() )
        return uno::Reference< ucb::XContent >();

    PackageUri aUri( Identifier->getContentIdentifier() );
    if ( !aUri.isValid() )
        throw ucb::IllegalIdentifierException();

    // Create a new identifier for the mormalized URL returned by
    // PackageUri::getUri().
    uno::Reference< ucb::XContentIdentifier > xId
                = new ::ucbhelper::ContentIdentifier( m_xSMgr, aUri.getUri() );

    osl::MutexGuard aGuard( m_aMutex );

    // Check, if a content with given id already exists...
    uno::Reference< ucb::XContent > xContent
        = queryExistingContent( xId ).get();
    if ( xContent.is() )
        return xContent;

    // Create a new content.

    xContent = Content::create( m_xSMgr, this, Identifier ); // not xId!!!
    registerNewContent( xContent );

    if ( xContent.is() && !xContent->getIdentifier().is() )
        throw ucb::IllegalIdentifierException();

    return xContent;
}

//=========================================================================
//
// Other methods.
//
//=========================================================================

uno::Reference< container::XHierarchicalNameAccess >
ContentProvider::createPackage( const rtl::OUString & rName, const rtl::OUString & rParam )
{
    osl::MutexGuard aGuard( m_aMutex );

    if ( !rName.getLength() )
    {
        OSL_ENSURE( sal_False,
                    "ContentProvider::createPackage - Invalid URL!" );
        return uno::Reference< container::XHierarchicalNameAccess >();
    }

	rtl::OUString rURL = rName + rParam;

    if ( m_pPackages )
    {
        Packages::const_iterator it = m_pPackages->find( rURL );
        if ( it != m_pPackages->end() )
        {
            // Already instanciated. Return package.
            return (*it).second->m_xNA;
        }
    }
    else
        m_pPackages = new Packages;

    // Create new package...
    try
    {
        uno::Sequence< uno::Any > aArguments( 1 );
        aArguments[ 0 ] <<= rURL;

        uno::Reference< uno::XInterface > xIfc
            = m_xSMgr->createInstanceWithArguments(
                rtl::OUString::createFromAscii(
                                "com.sun.star.packages.comp.ZipPackage" ),
                aArguments );

        if ( xIfc.is() )
        {
            uno::Reference<
                container::XHierarchicalNameAccess > xNameAccess(
                                                        xIfc, uno::UNO_QUERY );

            OSL_ENSURE( xNameAccess.is(),
                        "ContentProvider::createPackage - "
                        "Got no hierarchical name access!" );

            rtl::Reference< Package> xPackage
                = new Package( rURL, xNameAccess, this );

            (*m_pPackages)[ rURL ] = xPackage.get();

            return xPackage.get();
        }
    }
    catch ( uno::RuntimeException const & )
    {
        // createInstanceWithArguemts
    }
    catch ( uno::Exception const & )
    {
        // createInstanceWithArguemts
    }

    return uno::Reference< container::XHierarchicalNameAccess >();
}

//=========================================================================
sal_Bool ContentProvider::removePackage( const rtl::OUString & rName )
{
    osl::MutexGuard aGuard( m_aMutex );

    if ( m_pPackages )
    {
        Packages::iterator it = m_pPackages->find( rName );
        if ( it != m_pPackages->end() )
        {
            m_pPackages->erase( it );
            return sal_True;
        }
    }
    return sal_False;
}

