/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */


#include <comphelper/processfactory.hxx>
#include <cppuhelper/factory.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <cppuhelper/weak.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>

#include <sax/tools/documenthandleradapter.hxx>

#include <com/sun/star/lang/XServiceInfo.hpp>

#include <com/sun/star/beans/PropertyValue.hpp>

#include <com/sun/star/xml/XImportFilter.hpp>
#include <com/sun/star/xml/XImportFilter2.hpp>
#include <com/sun/star/xml/XExportFilter.hpp>
#include <com/sun/star/xml/sax/Parser.hpp>
#include <com/sun/star/xml/sax/InputSource.hpp>
#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
#include <com/sun/star/xml/sax/Writer.hpp>
#include <com/sun/star/xml/sax/XFastParser.hpp>

#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/io/XActiveDataSource.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <comphelper/diagnose_ex.hxx>

using namespace ::cppu;
using namespace ::sax;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::xml;
using namespace ::com::sun::star::xml::sax;

namespace filter::odfflatxml {
        namespace {

        /*
         * OdfFlatXml export and imports ODF flat XML documents by plugging a pass-through
         * filter implementation into XmlFilterAdaptor.
         */
        class OdfFlatXml : public WeakImplHelper<XImportFilter, XImportFilter2,
                                                  XExportFilter, DocumentHandlerAdapter, css::lang::XServiceInfo>
        {
        private:
            Reference< XComponentContext > m_xContext;

        public:

            explicit OdfFlatXml(const Reference<XComponentContext> &r) :
                m_xContext(r)
            {
            }

            // XImportFilter
            virtual sal_Bool SAL_CALL
            importer(const Sequence< PropertyValue >& sourceData,
                     const Reference< XDocumentHandler >& docHandler,
                     const Sequence< OUString >& userData) override;

            // XImportFilter2
            virtual sal_Bool SAL_CALL
            importer(const Sequence< PropertyValue >& sourceData,
                     const Reference< XFastParser >& fastParser,
                     const Sequence< OUString >& userData) override;

            // XExportFilter
            virtual sal_Bool SAL_CALL
            exporter(
                     const Sequence< PropertyValue >& sourceData,
                     const Sequence< OUString >& userData) override;

            OUString SAL_CALL getImplementationName() override
            { return u"com.sun.star.comp.filter.OdfFlatXml"_ustr; }

            sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
            { return cppu::supportsService(this, ServiceName); }

            css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
            {
                return css::uno::Sequence<OUString>{
                    u"com.sun.star.document.ImportFilter"_ustr,
                        u"com.sun.star.document.ExportFilter"_ustr};
            }

        };

        }
}

using namespace ::filter::odfflatxml;

sal_Bool
OdfFlatXml::importer(
                     const Sequence< PropertyValue >& sourceData,
                     const Reference< XDocumentHandler >& docHandler,
                     const Sequence< OUString >& /* userData */)
{
    // Read InputStream to read from and a URL used for the system id
    // of the InputSource we create from the given sourceData sequence
    Reference<XInputStream> inputStream;
    OUString paramName;
    OUString url;

    sal_Int32 paramCount = sourceData.getLength();
    for (sal_Int32 paramIdx = 0; paramIdx < paramCount; paramIdx++)
    {
        paramName = sourceData[paramIdx].Name;
        if ( paramName == "InputStream" )
            sourceData[paramIdx].Value >>= inputStream;
        else if ( paramName == "URL" )
            sourceData[paramIdx].Value >>= url;
    }

    OSL_ASSERT(inputStream.is());
    if (!inputStream.is())
        return false;

    InputSource inputSource;
    inputSource.sSystemId = url;
    inputSource.sPublicId = url;
    inputSource.aInputStream = inputStream;
    try
    {
        css::uno::Reference< css::io::XSeekable > xSeekable( inputStream, css::uno::UNO_QUERY );
        if ( xSeekable.is() )
            xSeekable->seek( 0 );

        css::uno::Reference< css::xml::sax::XFastParser > xFastParser (docHandler, UNO_QUERY );
        if( xFastParser.is() )
            xFastParser->parseStream( inputSource );
        else
        {
            Reference<XParser> saxParser = Parser::create(m_xContext);
            saxParser->setDocumentHandler(docHandler);
            saxParser->parseStream(inputSource);
        }
    }
    catch (const Exception &)
    {
        TOOLS_WARN_EXCEPTION("filter.odfflatxml", "");
        return false;
    }
    catch (const std::exception &exc)
    {
        SAL_WARN("filter.odfflatxml", exc.what());
        return false;
    }
    return true;
}

sal_Bool
OdfFlatXml::importer(
                     const Sequence< PropertyValue >& sourceData,
                     const Reference< XFastParser >& xFastParser,
                     const Sequence< OUString >& /* userData */)
{
    // Read InputStream to read from and a URL used for the system id
    // of the InputSource we create from the given sourceData sequence
    Reference<XInputStream> inputStream;
    OUString paramName;
    OUString url;

    sal_Int32 paramCount = sourceData.getLength();
    for (sal_Int32 paramIdx = 0; paramIdx < paramCount; paramIdx++)
    {
        paramName = sourceData[paramIdx].Name;
        if ( paramName == "InputStream" )
            sourceData[paramIdx].Value >>= inputStream;
        else if ( paramName == "URL" )
            sourceData[paramIdx].Value >>= url;
    }

    OSL_ASSERT(inputStream.is());
    if (!inputStream.is())
        return false;

    InputSource inputSource;
    inputSource.sSystemId = url;
    inputSource.sPublicId = url;
    inputSource.aInputStream = inputStream;
    try
    {
        css::uno::Reference< css::io::XSeekable > xSeekable( inputStream, css::uno::UNO_QUERY );
        if ( xSeekable.is() )
            xSeekable->seek( 0 );

        xFastParser->parseStream( inputSource );
    }
    catch (const Exception &)
    {
        TOOLS_WARN_EXCEPTION("filter.odfflatxml", "");
        return false;
    }
    catch (const std::exception &exc)
    {
        SAL_WARN("filter.odfflatxml", exc.what());
        return false;
    }
    return true;
}

sal_Bool
OdfFlatXml::exporter(const Sequence< PropertyValue >& sourceData,
                     const Sequence< OUString >& /*msUserData*/)
{
    OUString paramName;
    Reference<XOutputStream> outputStream;

    // Read output stream and target URL from the parameters given in sourceData.
    sal_Int32 paramCount = sourceData.getLength();
    for (sal_Int32 paramIdx = 0; paramIdx < paramCount; paramIdx++)
    {
        paramName = sourceData[paramIdx].Name;
        if ( paramName == "OutputStream" )
            sourceData[paramIdx].Value >>= outputStream;
    }

    if (!getDelegate().is())
    {
        Reference< XDocumentHandler > saxWriter = Writer::create(m_xContext);
        setDelegate(saxWriter);
    }
    // get data source interface ...
    Reference<XActiveDataSource> dataSource(getDelegate(), UNO_QUERY);
    OSL_ASSERT(dataSource.is());
    if (!dataSource.is())
        return false;
    OSL_ASSERT(outputStream.is());
    if (!outputStream.is())
        return false;
    dataSource->setOutputStream(outputStream);

    return true;
}

extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
filter_OdfFlatXml_get_implementation(
    css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
{
    return cppu::acquire(new OdfFlatXml(context));
}

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