/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

#include <stdio.h>

#include <cppuhelper/factory.hxx>

#include <cppuhelper/implbase1.hxx>
#include <cppuhelper/implbase2.hxx>


#include <com/sun/star/bridge/XUnoUrlResolver.hpp>

#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/loader/XImplementationLoader.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>


using namespace ::com::sun::star::bridge;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::loader;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::registry;


using namespace ::cppu;
using namespace ::rtl;

namespace dynamic_loader {

    class SingleServiceFactory : public WeakImplHelper2<XServiceInfo, XSingleServiceFactory> {
        Reference<XMultiServiceFactory> _xServiceManager;
        OUString _serviceName;
        OUString _link;
        OUString _resolver;

        Reference<XSingleServiceFactory> getRemoteFactory() throw(Exception, RuntimeException);

    public:

        SingleServiceFactory(const Reference<XMultiServiceFactory > & xServiceManager,
                             const OUString & serviceName,
                             const OUString & link,
                             const OUString & resolver)
            : _xServiceManager(xServiceManager),
              _serviceName(serviceName),
              _link(link),
              _resolver(resolver)
        {}

        // XSingleServiceFactory
        Reference<XInterface> SAL_CALL createInstance() throw(Exception, RuntimeException);
        Reference<XInterface> SAL_CALL createInstanceWithArguments(const Sequence<Any>& Arguments)
            throw(::com::sun::star::uno::Exception,
                  ::com::sun::star::uno::RuntimeException);

        // XServiceInfo
        OUString           SAL_CALL getImplementationName()                      throw(RuntimeException);
        sal_Bool           SAL_CALL supportsService(const OUString& ServiceName) throw(RuntimeException);
        Sequence<OUString> SAL_CALL getSupportedServiceNames(void)               throw(RuntimeException);
    };


    Reference<XSingleServiceFactory> SingleServiceFactory::getRemoteFactory() throw(Exception, RuntimeException) {
        Reference<XUnoUrlResolver> xResolver(_xServiceManager->createInstance(_resolver), UNO_QUERY);
        if(!xResolver.is()) {
            OUString message(RTL_CONSTASCII_USTRINGPARAM("dynamic_loader::singleServiceFactory.createInstance - couldn't create resolver: "));
            message += _resolver;

            throw Exception(message, Reference<XInterface>());
        }

        Reference<XInterface> remoteObject = xResolver->resolve(_link);
        if(!remoteObject.is()) {
            OUString message(RTL_CONSTASCII_USTRINGPARAM("dynamic_loader::singleServiceFactory.createInstance - couldn't resolve link: "));
            message += _link;

            throw Exception(message, Reference<XInterface>());
        }

        Reference<XSingleServiceFactory> remoteFactory(remoteObject, UNO_QUERY);
        if(!remoteFactory.is()) {
            OUString message(RTL_CONSTASCII_USTRINGPARAM("dynamic_loader::singleServiceFactory.createInstance - couldn't get XSingleServiceFactory from: "));
            message += _link;

            throw Exception(message, Reference<XInterface>());
        }

        return remoteFactory;
    }

    // XSingleServiceFactory
    Reference<XInterface> SAL_CALL SingleServiceFactory::createInstance() throw(Exception, RuntimeException) {
        OSL_TRACE("dynamic_loader::singleServiceFactory::createInstance");

        return getRemoteFactory()->createInstance();
    }

    Reference<XInterface> SAL_CALL SingleServiceFactory::createInstanceWithArguments(const Sequence<Any>& Arguments)
        throw(Exception, RuntimeException)
    {
        OSL_TRACE("dynamic_loader::singleServiceFactory::createInstanceWithArguments");

        return getRemoteFactory()->createInstanceWithArguments(Arguments);
    }

    // XServiceInfo
    OUString SAL_CALL SingleServiceFactory::getImplementationName() throw(RuntimeException) {
        return _link;
    }

    sal_Bool SAL_CALL SingleServiceFactory::supportsService(const OUString & ServiceName) throw(RuntimeException) {
        return _serviceName.equals(ServiceName);
    }

    Sequence<OUString> SAL_CALL SingleServiceFactory::getSupportedServiceNames(void) throw(RuntimeException) {
        return Sequence<OUString>(&_serviceName, 1);
    }



    class DynamicLoader : public WeakImplHelper2<XImplementationLoader, XServiceInfo> {
        Reference<XMultiServiceFactory>  _xSMgr;

    protected:
        DynamicLoader(const Reference<XMultiServiceFactory> & rXSMgr) throw(RuntimeException);
        ~DynamicLoader() throw();

    public:
        static const OUString implname;
        static const OUString servname;

        static Reference<XInterface> SAL_CALL createInstance(const Reference<XMultiServiceFactory> & rSMgr) throw(Exception);
        static Sequence<OUString>    SAL_CALL getSupportedServiceNames_Static() throw();

        static void parseUrl(const OUString & url, OUString * serviceName, OUString * link, OUString * resolver) throw(RuntimeException);

        // XServiceInfo
        virtual OUString           SAL_CALL getImplementationName()                      throw(RuntimeException);
        virtual sal_Bool           SAL_CALL supportsService(const OUString& ServiceName) throw(RuntimeException);
        virtual Sequence<OUString> SAL_CALL getSupportedServiceNames()                   throw(RuntimeException);

        // XImplementationLoader
        virtual Reference<XInterface> SAL_CALL activate(const OUString & implementationName,
                                                        const OUString & implementationLoaderUrl,
                                                        const OUString& locationUrl,
                                                        const Reference<XRegistryKey>& xKey)       throw(CannotActivateFactoryException, RuntimeException);
        virtual sal_Bool              SAL_CALL writeRegistryInfo(const Reference<XRegistryKey>& xKey,
                                                                 const OUString& implementationLoaderUrl,
                                                                 const OUString& locationUrl)      throw(CannotRegisterImplementationException, RuntimeException);
    };

    const OUString DynamicLoader::implname (RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.stoc.DynamicLoader"));
    const OUString DynamicLoader::servname (RTL_CONSTASCII_USTRINGPARAM("com.sun.star.loader.Dynamic"));

    Sequence<OUString> SAL_CALL DynamicLoader::getSupportedServiceNames_Static() throw() {
        return Sequence<OUString>(&servname, 1);
    }

    Reference<XInterface> SAL_CALL DynamicLoader::createInstance(const Reference<XMultiServiceFactory> & rSMgr) throw(Exception) {
        Reference<XInterface> xRet;

        try {
            XImplementationLoader *pXLoader = (XImplementationLoader *)new DynamicLoader(rSMgr);

            xRet = Reference<XInterface>::query(pXLoader);
        }
        catch(RuntimeException & runtimeException) {
            OString message = OUStringToOString(runtimeException.Message, RTL_TEXTENCODING_ASCII_US);
            osl_trace("dynamic loader - could not init cause of %s", message.getStr());
        }

        OSL_TRACE("DynamicLoader - createInstance: %d", xRet.is());


        return xRet;
    }

    DynamicLoader::DynamicLoader(const Reference<XMultiServiceFactory> & xSMgr) throw(RuntimeException)
        : _xSMgr(xSMgr)
    {
    }


    DynamicLoader::~DynamicLoader() throw() {
    }

    // XServiceInfo
    OUString SAL_CALL DynamicLoader::getImplementationName() throw(RuntimeException) {
        return implname;
    }

    sal_Bool SAL_CALL DynamicLoader::supportsService(const OUString & ServiceName) throw(RuntimeException)  {
        sal_Bool bSupport = sal_False;

        Sequence<OUString> aSNL = getSupportedServiceNames();
        const OUString * pArray = aSNL.getArray();
        for(sal_Int32 i = 0; i < aSNL.getLength() && !bSupport; ++ i)
            bSupport = pArray[i] == ServiceName;

        return bSupport;
    }

    Sequence<OUString> SAL_CALL DynamicLoader::getSupportedServiceNames() throw(RuntimeException) {
        return getSupportedServiceNames_Static();
    }


    void DynamicLoader::parseUrl(const OUString & locationUrl, OUString * serviceName, OUString * link, OUString * resolver) throw(RuntimeException) {
#if OSL_DEBUG_LEVEL > 1
        OString tmp = OUStringToOString(locationUrl, RTL_TEXTENCODING_ASCII_US);
        OSL_TRACE("DynamicLoader - locationUrl %s", tmp.getStr());
#endif

        // This is the default resolver
        *resolver = OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.bridge.UnoUrlResolver"));

        const OUString bSlash(OUString(RTL_CONSTASCII_USTRINGPARAM("\\")));
        const OUString tuedle(OUString(RTL_CONSTASCII_USTRINGPARAM("\"")));
        const OUString separator(OUString(RTL_CONSTASCII_USTRINGPARAM(",")));
        const OUString emptyString(OUString(RTL_CONSTASCII_USTRINGPARAM("")));
        const OUString equalSign(OUString(RTL_CONSTASCII_USTRINGPARAM("=")));

        sal_Int32 index = 0;
        sal_Bool  left = sal_True;
        sal_Bool  quote = sal_False;
        sal_Bool  inString = sal_False;

        const sal_Unicode * raw_url = locationUrl.getStr();
        OUString token;
        OUString attribute;

        while(index <= locationUrl.getLength()) {
            if(index >= locationUrl.getLength() || (raw_url[index] == separator.getStr()[0] && !quote && !inString)) { // a separator or end?
                OUString value;

                if(left)
                    attribute = token.trim();

                else
                    value = token.trim();

#if OSL_DEBUG_LEVEL > 1
                OString attribute_tmp = OUStringToOString(attribute, RTL_TEXTENCODING_ASCII_US);
                OSL_TRACE("DynamicLoader - attribute %s", attribute_tmp.getStr());
                OString value_tmp = OUStringToOString(value, RTL_TEXTENCODING_ASCII_US);
                OSL_TRACE("DynamicLoader - value %s", value_tmp.getStr());
#endif

                if(attribute.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("servicename"))))
                    *serviceName = value;

                else if(attribute.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("link"))))
                    *link = value;

                else if(attribute.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("resolver"))))
                    *resolver = value;

                else  {
                    OUString message(RTL_CONSTASCII_USTRINGPARAM("help called"));

                    if(!attribute.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("help")))) {
                        message = OUString(RTL_CONSTASCII_USTRINGPARAM("DynamicLoader - unknown attribute: "));
                        message += attribute;
                    }

                    fprintf(stdout, "DynamicLoader - help\n");
                    fprintf(stdout, "attributes:\n");
                    fprintf(stdout, "\tservicename:  service name of dynamic component\n");
                    fprintf(stdout, "\tlink:         link to a single service factory for dynamic component\n");
                    fprintf(stdout, "\tresolver:     the service which resolves the link\n");
                    fprintf(stdout, "\thelp:         this help\n");

                    throw RuntimeException(message, Reference<XInterface>());
                }

                left = sal_True; // reset state to be left
                token = emptyString;
            }
            else if(raw_url[index] == bSlash.getStr()[0] && !quote) // a back slash?
                quote = sal_True;

            else if(raw_url[index] == equalSign.getStr()[0] && !quote && !inString) { // equalSign (switch from left to right)?
                left = sal_False;

                attribute = token.trim();
                token = emptyString;
            }
            else if(raw_url[index] == tuedle.getStr()[0] && !quote) // begin or end of string?
                inString = !inString;

            else { // no special handling
                token += OUString(raw_url + index, 1);
                quote = sal_False;
            }

            ++ index;
        }

        // enshure, that attributes are set properly
        if(!(*serviceName).getLength())
            throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM("dynamic_loader::DynamicLoader.parseUrl - missing or empty attribute: servicename")),
                                   Reference<XInterface>());

        if(!(*link).getLength())
            throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM("dynamic_loader::DynamicLoader.parseUrl - missing or empty attribute: link")),
                                   Reference<XInterface>());

        if(!(*resolver).getLength())
            throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM("dynamic_loader::DynamicLoader.parseUrl - missing or empty attribute: resolver")),
                                   Reference<XInterface>());
    }


    // XImplementationLoader
    sal_Bool SAL_CALL DynamicLoader::writeRegistryInfo(const Reference<XRegistryKey> & xKey,
                                                       const OUString & implementationLoaderUrl,
                                                       const OUString & locationUrl)
        throw(CannotRegisterImplementationException, RuntimeException)
    {
        OSL_TRACE("DynamicLoader::writeRegistryInfo");

        OUString serviceName;
        OUString link;
        OUString resolver;

        try {
            parseUrl(locationUrl, &serviceName, &link, &resolver);
        }
        catch(RuntimeException & runtimeException) {
            throw CannotRegisterImplementationException(runtimeException.Message, Reference<XInterface>());
        }

        // create the keys
        OUString keyName (RTL_CONSTASCII_USTRINGPARAM("/"));
        keyName += implementationLoaderUrl;
        keyName += OUString(RTL_CONSTASCII_USTRINGPARAM("_"));
        keyName += serviceName;
        keyName += OUString(RTL_CONSTASCII_USTRINGPARAM("/UNO/SERVICES"));

        Reference<XRegistryKey> xNewKey(xKey->createKey(keyName));
        xNewKey->createKey(serviceName);

        sal_Bool bSuccess = sal_True;

        return bSuccess;
    }

    Reference<XInterface> SAL_CALL DynamicLoader::activate(const OUString &,
                                                           const OUString &,
                                                           const OUString & locationUrl,
                                                           const Reference<XRegistryKey> &)
        throw(CannotActivateFactoryException, RuntimeException)
    {
        OSL_TRACE("DynamicLoader::activate");

        OUString serviceName;
        OUString link;
        OUString resolver;

        parseUrl(locationUrl, &serviceName, &link, &resolver);

        XSingleServiceFactory * xFactory = (XSingleServiceFactory *)new SingleServiceFactory(_xSMgr,
                                                                                             serviceName,
                                                                                             link,
                                                                                             resolver);

          Reference<XInterface> xReturn;

        if(xFactory)
            xReturn = Reference<XInterface>::query(xFactory);

        return xReturn;
    }
}




extern "C" {
    void SAL_CALL component_getImplementationEnvironment(const sal_Char ** ppEnvTypeName, uno_Environment **)   {
        *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
    }

    sal_Bool SAL_CALL component_writeInfo(XMultiServiceFactory *, XRegistryKey * pRegistryKey) {
        sal_Bool bRes = sal_False;

        if (pRegistryKey) {
            try {
                OUString x (RTL_CONSTASCII_USTRINGPARAM("/"));
                x += ::dynamic_loader::DynamicLoader::implname;
                x += OUString(RTL_CONSTASCII_USTRINGPARAM("/UNO/SERVICES"));


                Reference<XRegistryKey> xNewKey(pRegistryKey->createKey(x));

                const Sequence<OUString> rSNL = ::dynamic_loader::DynamicLoader::getSupportedServiceNames_Static();
                const OUString * pArray = rSNL.getConstArray();
                for (sal_Int32 nPos = rSNL.getLength(); nPos--;)
                    xNewKey->createKey(pArray[nPos]);


                bRes = sal_True;
            }
            catch (InvalidRegistryException &) {
                  OSL_ENSURE( sal_False, "### InvalidRegistryException!" );
            }
        }

        return bRes;
    }

    void * SAL_CALL component_getFactory(const sal_Char * pImplName, XMultiServiceFactory * pServiceManager, XRegistryKey *) {
        void * pRet = 0;

        if (pServiceManager && OUString::createFromAscii(pImplName).equals(::dynamic_loader::DynamicLoader::implname)) {
            Reference<XSingleServiceFactory> xFactory(createOneInstanceFactory(pServiceManager,
                                                                               ::dynamic_loader::DynamicLoader::implname,
                                                                               ::dynamic_loader::DynamicLoader::createInstance,
                                                                               ::dynamic_loader::DynamicLoader::getSupportedServiceNames_Static()));

            if (xFactory.is()) {
                xFactory->acquire();
                pRet = xFactory.get();
            }
        }
        else
            OSL_TRACE("DynamicLoader - warning - given wrong implName: %s", pImplName);

        return pRet;
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
