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

#include "ResourceFactoryManager.hxx"
#include <tools/wldcrd.hxx>
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <comphelper/processfactory.hxx>
#include <boost/bind.hpp>
#include <algorithm>

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

#undef VERBOSE
//#define VERBOSE 1

namespace sd { namespace framework {

ResourceFactoryManager::ResourceFactoryManager (const Reference<XControllerManager>& rxManager)
    : maMutex(),
      maFactoryMap(),
      maFactoryPatternList(),
      mxControllerManager(rxManager),
      mxURLTransformer()
{
    // Create the URL transformer.
    Reference<lang::XMultiServiceFactory> xServiceManager (
        ::comphelper::getProcessServiceFactory());
    mxURLTransformer = Reference<util::XURLTransformer>(
        xServiceManager->createInstance(
            OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer"))),
        UNO_QUERY);
}




ResourceFactoryManager::~ResourceFactoryManager (void)
{
    Reference<lang::XComponent> xComponent (mxURLTransformer, UNO_QUERY);
    if (xComponent.is())
        xComponent->dispose();
}




void ResourceFactoryManager::AddFactory (
    const OUString& rsURL,
    const Reference<XResourceFactory>& rxFactory)
        throw (RuntimeException)
{
    if ( ! rxFactory.is())
        throw lang::IllegalArgumentException();
    if (rsURL.getLength() == 0)
        throw lang::IllegalArgumentException();

    ::osl::MutexGuard aGuard (maMutex);

    if (rsURL.indexOf('*') >= 0 || rsURL.indexOf('?') >= 0)
    {
        // The URL is a URL pattern not an single URL.
        maFactoryPatternList.push_back(FactoryPatternList::value_type(rsURL, rxFactory));

#if defined VERBOSE && VERBOSE>=1
        OSL_TRACE("ResourceFactoryManager::AddFactory pattern %s %x\n",
            OUStringToOString(rsURL, RTL_TEXTENCODING_UTF8).getStr(),
            rxFactory.get());
#endif
    }
    else
    {
        maFactoryMap[rsURL] = rxFactory;

#if defined VERBOSE && VERBOSE>=1
        OSL_TRACE("ResourceFactoryManager::AddFactory fixed %s %x\n",
            OUStringToOString(rsURL, RTL_TEXTENCODING_UTF8).getStr(),
            rxFactory.get());
#endif
    }
}




void ResourceFactoryManager::RemoveFactoryForURL (
    const OUString& rsURL)
    throw (RuntimeException)
{
    if (rsURL.getLength() == 0)
        throw lang::IllegalArgumentException();

    ::osl::MutexGuard aGuard (maMutex);

    FactoryMap::iterator iFactory (maFactoryMap.find(rsURL));
    if (iFactory != maFactoryMap.end())
    {
        maFactoryMap.erase(iFactory);
    }
    else
    {
        // The URL may be a pattern.  Look that up.
        FactoryPatternList::iterator iPattern;
        for (iPattern=maFactoryPatternList.begin();
             iPattern!=maFactoryPatternList.end();
             ++iPattern)
        {
            if (iPattern->first == rsURL)
            {
                // Found the pattern.  Remove it.
                maFactoryPatternList.erase(iPattern);
                break;
            }
        }
    }
}





void ResourceFactoryManager::RemoveFactoryForReference(
    const Reference<XResourceFactory>& rxFactory)
    throw (RuntimeException)
{
    ::osl::MutexGuard aGuard (maMutex);

    // Collect a list with all keys that map to the given factory.
    ::std::vector<OUString> aKeys;
    FactoryMap::const_iterator iFactory;
    for (iFactory=maFactoryMap.begin(); iFactory!=maFactoryMap.end(); ++iFactory)
        if (iFactory->second == rxFactory)
            aKeys.push_back(iFactory->first);

    // Remove the entries whose keys we just have collected.
    ::std::vector<OUString>::const_iterator iKey;
    for (iKey=aKeys.begin(); iKey!=aKeys.end();  ++iKey)
        maFactoryMap.erase(maFactoryMap.find(*iKey));

    // Remove the pattern entries whose factories are identical to the given
    // factory.
    FactoryPatternList::iterator iNewEnd (
        std::remove_if(
            maFactoryPatternList.begin(),
            maFactoryPatternList.end(),
            ::boost::bind(
                std::equal_to<Reference<XResourceFactory> >(),
                ::boost::bind(&FactoryPatternList::value_type::second, _1),
                rxFactory)));
    if (iNewEnd != maFactoryPatternList.end())
        maFactoryPatternList.erase(iNewEnd, maFactoryPatternList.end());
}




Reference<XResourceFactory> ResourceFactoryManager::GetFactory (
    const OUString& rsCompleteURL)
    throw (RuntimeException)
{
    OUString sURLBase (rsCompleteURL);
    if (mxURLTransformer.is())
    {
        util::URL aURL;
        aURL.Complete = rsCompleteURL;
        if (mxURLTransformer->parseStrict(aURL))
            sURLBase = aURL.Main;
    }

    Reference<XResourceFactory> xFactory = FindFactory(sURLBase);

    if ( ! xFactory.is() && mxControllerManager.is())
    {
        Reference<XModuleController> xModuleController(mxControllerManager->getModuleController());
        if (xModuleController.is())
        {
            // Ask the module controller to provide a factory of the
            // requested view type.  Note that this can (and should) cause
            // intermediate calls to AddFactory().
            xModuleController->requestResource(sURLBase);
        
            xFactory = FindFactory(sURLBase);
        }
    }

    return xFactory;
}




Reference<XResourceFactory> ResourceFactoryManager::FindFactory (const OUString& rsURLBase)
    throw (RuntimeException)
{
    ::osl::MutexGuard aGuard (maMutex);
    FactoryMap::const_iterator iFactory (maFactoryMap.find(rsURLBase));
    if (iFactory != maFactoryMap.end())
        return iFactory->second;
    else
    {
        // Check the URL patterns.
        FactoryPatternList::const_iterator iPattern;
        for (iPattern=maFactoryPatternList.begin();
             iPattern!=maFactoryPatternList.end();
             ++iPattern)
        {
            WildCard aWildCard (iPattern->first);
            if (aWildCard.Matches(rsURLBase))
                return iPattern->second;
        }
    }
    return NULL;
}

} } // end of namespace sd::framework