/* -*- 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */

#include "SchXMLSeries2Context.hxx"
#include "SchXMLPlotAreaContext.hxx"
#include "SchXMLRegressionCurveObjectContext.hxx"
#include "SchXMLPropertyMappingContext.hxx"
#include "SchXMLTools.hxx"

#include <com/sun/star/chart2/XChartDocument.hpp>
#include <com/sun/star/chart2/XRegressionCurve.hpp>
#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
#include <com/sun/star/chart2/data/XDataSink.hpp>
#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
#include <com/sun/star/chart2/RelativePosition.hpp>

#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
#include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
#include <com/sun/star/chart2/DataPointCustomLabelField.hpp>

#include <com/sun/star/chart/ChartAxisAssign.hpp>
#include <com/sun/star/chart/ChartSymbolType.hpp>
#include <com/sun/star/chart/ChartDataCaption.hpp>
#include <com/sun/star/chart/ErrorBarStyle.hpp>
#include <com/sun/star/chart/XChartDocument.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/chart/ChartLegendPosition.hpp>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/embed/XVisualObject.hpp>

#include <comphelper/processfactory.hxx>

#include <sal/log.hxx>
#include <utility>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmlimp.hxx>
#include <xmloff/namespacemap.hxx>
#include <xmloff/SchXMLSeriesHelper.hxx>
#include <xmloff/prstylei.hxx>
#include <comphelper/diagnose_ex.hxx>

#include <algorithm> // std::find_if

using namespace ::com::sun::star;
using namespace ::xmloff::token;

using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Sequence;

namespace
{

class SchXMLDomain2Context : public SvXMLImportContext
{
private:
    ::std::vector< OUString > & mrAddresses;

public:
    SchXMLDomain2Context( SvXMLImport& rImport,
                          ::std::vector< OUString > & rAddresses );
    virtual void SAL_CALL startFastElement(
        sal_Int32 nElement,
        const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
};

SchXMLDomain2Context::SchXMLDomain2Context(
    SvXMLImport& rImport,
    ::std::vector< OUString > & rAddresses ) :
        SvXMLImportContext( rImport ),
        mrAddresses( rAddresses )
{
}

void SchXMLDomain2Context::startFastElement(
    sal_Int32 /*nElement*/,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
    {
        if (aIter.getToken() == XML_ELEMENT(TABLE, XML_CELL_RANGE_ADDRESS) )
            mrAddresses.push_back( aIter.toString() );
        else
            XMLOFF_WARN_UNKNOWN("xmloff", aIter);
    }
}

void lcl_setAutomaticSymbolSize( const uno::Reference< beans::XPropertySet >& xSeriesOrPointProp, const SvXMLImport& rImport )
{
    awt::Size aSymbolSize(140,140);//old default for standard sized charts 7cm height

    uno::Reference< chart::XChartDocument > xChartDoc( rImport.GetModel(), uno::UNO_QUERY );
    if( xChartDoc.is() )
    {
        double fScale = 1;
        uno::Reference< beans::XPropertySet > xLegendProp( xChartDoc->getLegend(), uno::UNO_QUERY );
        chart::ChartLegendPosition aLegendPosition = chart::ChartLegendPosition_NONE;
        if( xLegendProp.is() && (xLegendProp->getPropertyValue(u"Alignment"_ustr) >>= aLegendPosition)
            && chart::ChartLegendPosition_NONE != aLegendPosition )
        {

            double fFontHeight = 6.0;
            if( xLegendProp->getPropertyValue(u"CharHeight"_ustr) >>= fFontHeight )
                fScale = 0.75*fFontHeight/6.0;
        }
        else
        {
            uno::Reference< embed::XVisualObject > xVisualObject( rImport.GetModel(), uno::UNO_QUERY );
            if( xVisualObject.is() )
            {
                awt::Size aPageSize( xVisualObject->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) );
                fScale = aPageSize.Height/7000.0;
            }
        }
        if( fScale>0 )
        {
            aSymbolSize.Height = static_cast<sal_Int32>( fScale * aSymbolSize.Height );
            aSymbolSize.Width = aSymbolSize.Height;
        }
    }
    xSeriesOrPointProp->setPropertyValue(u"SymbolSize"_ustr,uno::Any( aSymbolSize ));
}

void lcl_setSymbolSizeIfNeeded( const uno::Reference< beans::XPropertySet >& xSeriesOrPointProp, const SvXMLImport& rImport )
{
    if( !xSeriesOrPointProp.is() )
        return;

    sal_Int32 nSymbolType = chart::ChartSymbolType::NONE;
    if( !(xSeriesOrPointProp.is() && ( xSeriesOrPointProp->getPropertyValue(u"SymbolType"_ustr) >>= nSymbolType)) )
        return;

    if(chart::ChartSymbolType::NONE!=nSymbolType)
    {
        if( chart::ChartSymbolType::BITMAPURL==nSymbolType )
        {
            //set special size for graphics to indicate to use the bitmap size itself
            xSeriesOrPointProp->setPropertyValue(u"SymbolSize"_ustr,uno::Any( awt::Size(-1,-1) ));
        }
        else
        {
            lcl_setAutomaticSymbolSize( xSeriesOrPointProp, rImport );
        }
    }
}

void lcl_resetSymbolSizeForPointsIfNecessary( const uno::Reference< beans::XPropertySet >& xPointProp, const SvXMLImport& rImport
    , const XMLPropStyleContext * pPropStyleContext, const SvXMLStylesContext* pStylesCtxt )
{
    uno::Any aASymbolSize( SchXMLTools::getPropertyFromContext( u"SymbolSize", pPropStyleContext, pStylesCtxt ) );
    if( !aASymbolSize.hasValue() )
        lcl_setSymbolSizeIfNeeded( xPointProp, rImport );
}

void lcl_setLinkNumberFormatToSourceIfNeeded( const uno::Reference< beans::XPropertySet >& xPointProp
    , const XMLPropStyleContext* pPropStyleContext, const SvXMLStylesContext* pStylesCtxt )
{
    uno::Any aAny( SchXMLTools::getPropertyFromContext(u"LinkNumberFormatToSource", pPropStyleContext, pStylesCtxt) );
    if( aAny.hasValue() )
        return;

    if( !xPointProp.is() )
        return;

    bool bLinkToSource = false;
    if( xPointProp.is() && (xPointProp->getPropertyValue(u"LinkNumberFormatToSource"_ustr) >>= bLinkToSource) )
    {
        if( bLinkToSource )
        {
            xPointProp->setPropertyValue(u"LinkNumberFormatToSource"_ustr, uno::Any(false));
        }
    }
}

void lcl_insertErrorBarLSequencesToMap(
    tSchXMLLSequencesPerIndex & rInOutMap,
    const uno::Reference< beans::XPropertySet > & xSeriesProp )
{
    Reference< chart2::data::XDataSource > xErrorBarSource;
    if( ( xSeriesProp->getPropertyValue( u"ErrorBarY"_ustr ) >>= xErrorBarSource ) &&
        xErrorBarSource.is() )
    {
        const Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSequences(
            xErrorBarSource->getDataSequences());
        for( const auto& rLSequence : aLSequences )
        {
            // use "0" as data index. This is ok, as it is not used for error bars
            rInOutMap.emplace(
                    tSchXMLIndexWithPart( 0, SCH_XML_PART_ERROR_BARS ), rLSequence );
        }
    }
}

Reference< chart2::data::XLabeledDataSequence2 > lcl_createAndAddSequenceToSeries( const OUString& rRole
        , const OUString& rRange
        , const Reference< chart2::XChartDocument >& xChartDoc
        , const Reference< chart2::XDataSeries >& xSeries )
{
    Reference< chart2::data::XLabeledDataSequence2 > xLabeledSeq;

    Reference< chart2::data::XDataSource > xSeriesSource( xSeries,uno::UNO_QUERY );
    Reference< chart2::data::XDataSink > xSeriesSink( xSeries, uno::UNO_QUERY );

    if( !(!rRange.isEmpty() && xChartDoc.is() && xSeriesSource.is() && xSeriesSink.is()) )
        return xLabeledSeq;

    // create a new sequence
    xLabeledSeq = SchXMLTools::GetNewLabeledDataSequence();

    // set values at the new sequence
    Reference< chart2::data::XDataSequence > xSeq = SchXMLTools::CreateDataSequence( rRange, xChartDoc );
    Reference< beans::XPropertySet > xSeqProp( xSeq, uno::UNO_QUERY );
    if( xSeqProp.is())
        xSeqProp->setPropertyValue(u"Role"_ustr, uno::Any( rRole));
    xLabeledSeq->setValues( xSeq );

    // add new sequence to data series / push to front to have the correct sequence order if charttype is changed afterwards
    const Sequence< Reference< chart2::data::XLabeledDataSequence > > aOldSeq( xSeriesSource->getDataSequences());
    sal_Int32 nOldCount = aOldSeq.getLength();
    Sequence< Reference< chart2::data::XLabeledDataSequence > > aNewSeq( nOldCount + 1 );
    auto pNewSeq = aNewSeq.getArray();
    pNewSeq[0].set(xLabeledSeq, uno::UNO_QUERY_THROW);
    std::copy(aOldSeq.begin(), aOldSeq.end(), std::next(pNewSeq));
    xSeriesSink->setData( aNewSeq );

    return xLabeledSeq;
}

XMLPropStyleContext* lcl_GetStylePropContext(
                        const SvXMLStylesContext* pStylesCtxt,
                        const SvXMLStyleContext*& rpStyle,
                        OUString const & rStyleName )
{
    rpStyle = pStylesCtxt->FindStyleChildContext( SchXMLImportHelper::GetChartFamilyID(), rStyleName );
    XMLPropStyleContext* pPropStyleContext =
                    const_cast< XMLPropStyleContext* >(dynamic_cast< const XMLPropStyleContext* >( rpStyle ));
    return pPropStyleContext;
}

} // anonymous namespace

SchXMLSeries2Context::SchXMLSeries2Context(
    SchXMLImportHelper& rImpHelper,
    SvXMLImport& rImport,
    const Reference< chart2::XChartDocument > & xNewDoc,
    std::vector< SchXMLAxis >& rAxes,
    ::std::vector< DataRowPointStyle >& rStyleVector,
    ::std::vector< RegressionStyle >& rRegressionStyleVector,
    sal_Int32 nSeriesIndex,
    bool bStockHasVolume,
    GlobalSeriesImportInfo& rGlobalSeriesImportInfo,
    const OUString & aGlobalChartTypeName,
    tSchXMLLSequencesPerIndex & rLSequencesPerIndex,
    bool& rGlobalChartTypeUsedBySeries,
    const awt::Size & rChartSize ) :
        SvXMLImportContext( rImport ),
        mrImportHelper( rImpHelper ),
        mxNewDoc( xNewDoc ),
        mrAxes( rAxes ),
        mrStyleVector( rStyleVector ),
        mrRegressionStyleVector( rRegressionStyleVector ),
        mnSeriesIndex( nSeriesIndex ),
        mnDataPointIndex( 0 ),
        m_bStockHasVolume( bStockHasVolume ),
        m_rGlobalSeriesImportInfo(rGlobalSeriesImportInfo),
        mpAttachedAxis( nullptr ),
        mnAttachedAxis( 0 ),
        maGlobalChartTypeName( aGlobalChartTypeName ),
        maSeriesChartTypeName( aGlobalChartTypeName ),
        m_bHasDomainContext(false),
        mrLSequencesPerIndex( rLSequencesPerIndex ),
        mrGlobalChartTypeUsedBySeries( rGlobalChartTypeUsedBySeries ),
        mbSymbolSizeIsMissingInFile(false),
        maChartSize( rChartSize ),
        // A series manages the DataRowPointStyle-struct of a data-label child element.
        mDataLabel(DataRowPointStyle::DATA_LABEL_SERIES, OUString{})
{
    if( aGlobalChartTypeName == "com.sun.star.chart2.DonutChartType" )
    {
        maSeriesChartTypeName = "com.sun.star.chart2.PieChartType";
        maGlobalChartTypeName = maSeriesChartTypeName;
    }
}

SchXMLSeries2Context::~SchXMLSeries2Context()
{
    SAL_WARN_IF( !maPostponedSequences.empty(), "xmloff.chart", "maPostponedSequences is NULL");
}

void SchXMLSeries2Context::startFastElement (sal_Int32 /*Element*/,
        const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // parse attributes
    mnAttachedAxis = 1;

    bool bHasRange = false;
    OUString aSeriesLabelRange;
    OUString aSeriesLabelString;
    bool bHideLegend = false;

    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
    {
        OUString aValue = aIter.toString();
        switch(aIter.getToken())
        {
            case XML_ELEMENT(CHART, XML_VALUES_CELL_RANGE_ADDRESS):
                m_aSeriesRange = aValue;
                bHasRange = true;
                break;
            case XML_ELEMENT(CHART, XML_LABEL_CELL_ADDRESS):
                aSeriesLabelRange = aValue;
                break;
            case XML_ELEMENT(LO_EXT, XML_LABEL_STRING):
                aSeriesLabelString = aValue;
                break;
            case XML_ELEMENT(CHART, XML_ATTACHED_AXIS):
                {
                    sal_Int32 nNumOfAxes = mrAxes.size();
                    for( sal_Int32 nCurrent = 0; nCurrent < nNumOfAxes; nCurrent++ )
                    {
                        if( aValue == mrAxes[ nCurrent ].aName &&
                            mrAxes[ nCurrent ].eDimension == SCH_XML_AXIS_Y )
                        {
                            mpAttachedAxis = &( mrAxes[ nCurrent ] );
                        }
                    }
                }
                break;
            case XML_ELEMENT(CHART, XML_STYLE_NAME):
                msAutoStyleName = aValue;
                break;
            case XML_ELEMENT(CHART, XML_CLASS):
                {
                    OUString aClassName;
                    sal_uInt16 nClassPrefix =
                        GetImport().GetNamespaceMap().GetKeyByAttrValueQName(
                            aValue, &aClassName );
                    if( XML_NAMESPACE_CHART == nClassPrefix )
                        maSeriesChartTypeName = SchXMLTools::GetChartTypeByClassName( aClassName, false /* bUseOldNames */ );

                    if( maSeriesChartTypeName.isEmpty())
                        maSeriesChartTypeName = aClassName;
                }
                break;
            case XML_ELEMENT(LO_EXT, XML_HIDE_LEGEND):
                bHideLegend = aValue.toBoolean();
                break;
            default:
                XMLOFF_WARN_UNKNOWN("xmloff", aIter);
        }
    }

    if( mpAttachedAxis )
    {
        if( mpAttachedAxis->nAxisIndex > 0 )
        {
            // secondary axis => property has to be set (primary is default)
            mnAttachedAxis = 2;
        }
    }

    try
    {
        SAL_WARN_IF( !mxNewDoc.is(), "xmloff.chart", "mxNewDoc is NULL");
        if( m_rGlobalSeriesImportInfo.rbAllRangeAddressesAvailable && ! bHasRange )
            m_rGlobalSeriesImportInfo.rbAllRangeAddressesAvailable = false;

        bool bIsCandleStick = maGlobalChartTypeName == "com.sun.star.chart2.CandleStickChartType";
        if( !maSeriesChartTypeName.isEmpty() )
        {
            bIsCandleStick = maSeriesChartTypeName == "com.sun.star.chart2.CandleStickChartType";
        }
        else
        {
            if( bIsCandleStick
                && m_bStockHasVolume
                && mnSeriesIndex == 0 )
            {
                maSeriesChartTypeName = "com.sun.star.chart2.ColumnChartType";
                bIsCandleStick = false;
            }
            else
            {
                maSeriesChartTypeName = maGlobalChartTypeName;
            }
        }
        if( ! mrGlobalChartTypeUsedBySeries )
            mrGlobalChartTypeUsedBySeries = (maSeriesChartTypeName == maGlobalChartTypeName);
        sal_Int32 const nCoordinateSystemIndex = 0;//so far we can only import one coordinate system
        m_xSeries.set(
            SchXMLImportHelper::GetNewDataSeries( mxNewDoc, nCoordinateSystemIndex, maSeriesChartTypeName, ! mrGlobalChartTypeUsedBySeries ));
        Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( SchXMLTools::GetNewLabeledDataSequence(), uno::UNO_QUERY_THROW );

        Reference< beans::XPropertySet > xSeriesProp( m_xSeries, uno::UNO_QUERY );
        if (xSeriesProp.is())
        {
            if (bHideLegend)
                xSeriesProp->setPropertyValue(u"ShowLegendEntry"_ustr, uno::Any(false));

            if( bIsCandleStick )
            {
                // set default color for range-line to black (before applying styles)
                xSeriesProp->setPropertyValue(u"Color"_ustr,
                        uno::Any( sal_Int32( 0x000000 ))); // black
            }
            else if ( maSeriesChartTypeName == "com.sun.star.chart2.PieChartType" )
            {
                //@todo: this property should be saved
                xSeriesProp->setPropertyValue(u"VaryColorsByPoint"_ustr,
                        uno::Any( true ));
            }

        }

        Reference<chart2::data::XDataProvider> xDataProvider(mxNewDoc->getDataProvider());
        Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xDataProvider, uno::UNO_QUERY);

        Reference<chart2::data::XDataSequence> xSequenceValues;

        // values
        if (xPivotTableDataProvider.is()) // is pivot chart
        {
            xSequenceValues.set(xPivotTableDataProvider->createDataSequenceOfValuesByIndex(mnSeriesIndex));
        }
        else
        {
            if (bHasRange && !m_aSeriesRange.isEmpty())
                xSequenceValues = SchXMLTools::CreateDataSequence(m_aSeriesRange, mxNewDoc);
        }

        Reference<beans::XPropertySet> xSeqProp(xSequenceValues, uno::UNO_QUERY);
        if (xSeqProp.is())
        {
            OUString aMainRole(u"values-y"_ustr);
            if (maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType")
                aMainRole = "values-size";
            xSeqProp->setPropertyValue(u"Role"_ustr, uno::Any(aMainRole));
        }
        xLabeledSeq->setValues(xSequenceValues);

        // register for setting local data if external data provider is not present
        maPostponedSequences.emplace(
                tSchXMLIndexWithPart( m_rGlobalSeriesImportInfo.nCurrentDataIndex, SCH_XML_PART_VALUES ), xLabeledSeq );

        // label
        Reference<chart2::data::XDataSequence> xSequenceLabel;

        if (xPivotTableDataProvider.is())
        {
            xSequenceLabel.set(xPivotTableDataProvider->createDataSequenceOfLabelsByIndex(mnSeriesIndex));
        }
        else
        {
            if (!aSeriesLabelRange.isEmpty())
            {
                xSequenceLabel.set(SchXMLTools::CreateDataSequence(aSeriesLabelRange, mxNewDoc));
            }
            else if (!aSeriesLabelString.isEmpty())
            {
                xSequenceLabel.set(SchXMLTools::CreateDataSequenceWithoutConvert(aSeriesLabelString, mxNewDoc));
            }
        }

        //Labels should always include hidden cells
        Reference<beans::XPropertySet> xSeqLabelProp(xSequenceLabel, uno::UNO_QUERY);
        if (xSeqLabelProp.is() && xSeqLabelProp->getPropertySetInfo()->hasPropertyByName(u"IncludeHiddenCells"_ustr))
        {
            xSeqLabelProp->setPropertyValue( u"IncludeHiddenCells"_ustr, uno::Any(true));
        }

        xLabeledSeq->setLabel(xSequenceLabel);

        // Note: Even if we have no label, we have to register the label
        // for creation, because internal data always has labels. If
        // they don't exist in the original, auto-generated labels are
        // used for the internal data.
        maPostponedSequences.emplace(
                tSchXMLIndexWithPart( m_rGlobalSeriesImportInfo.nCurrentDataIndex, SCH_XML_PART_LABEL ), xLabeledSeq );

        Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeq( &xLabeledSeq, 1 );
        Reference< chart2::data::XDataSink > xSink( m_xSeries, uno::UNO_QUERY_THROW );
        xSink->setData( aSeq );
    }
    catch( const uno::Exception &)
    {
        DBG_UNHANDLED_EXCEPTION("xmloff.chart");
    }

    //init mbSymbolSizeIsMissingInFile:
    try
    {
        if( !msAutoStyleName.isEmpty() )
        {
            const SvXMLStylesContext* pStylesCtxt = mrImportHelper.GetAutoStylesContext();
            if( pStylesCtxt )
            {
                const SvXMLStyleContext* pStyle = pStylesCtxt->FindStyleChildContext(
                    SchXMLImportHelper::GetChartFamilyID(), msAutoStyleName );

                const XMLPropStyleContext* pPropStyleContext = dynamic_cast< const XMLPropStyleContext * >( pStyle );

                uno::Any aASymbolSize( SchXMLTools::getPropertyFromContext( u"SymbolSize"
                    , pPropStyleContext, pStylesCtxt ) );
                mbSymbolSizeIsMissingInFile = !aASymbolSize.hasValue();
            }
        }
    }
    catch( const uno::Exception & )
    {
    }
}

namespace {

struct DomainInfo
{
    DomainInfo( OUString _aRole, OUString _aRange, sal_Int32 nIndex )
        : aRole(std::move(_aRole)), aRange(std::move(_aRange)), nIndexForLocalData(nIndex)
    {}

    OUString aRole;
    OUString aRange;
    sal_Int32 nIndexForLocalData;
};

}

void SchXMLSeries2Context::endFastElement(sal_Int32 )
{
    // special handling for different chart types.  This is necessary as the
    // roles are not yet saved in the file format
    sal_Int32 nDomainCount = maDomainAddresses.size();
    bool bIsScatterChart = maSeriesChartTypeName == "com.sun.star.chart2.ScatterChartType";
    bool bIsBubbleChart = maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType";
    bool bDeleteSeries = false;
    std::vector< DomainInfo > aDomainInfos;

    //different handling for different chart types necessary
    if( bIsScatterChart || ( nDomainCount==1 && !bIsBubbleChart ) )
    {
        DomainInfo aDomainInfo( u"values-x"_ustr, m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress, m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex ) ;
        bool bCreateXValues = true;
        if( !maDomainAddresses.empty() )
        {
            if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() )
            {
                m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = maDomainAddresses.front();
                m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex;
            }
            aDomainInfo.aRange = maDomainAddresses.front();
            aDomainInfo.nIndexForLocalData = m_rGlobalSeriesImportInfo.nCurrentDataIndex;
            m_rGlobalSeriesImportInfo.nCurrentDataIndex++;
        }
        else if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() && !m_bHasDomainContext && mnSeriesIndex==0 )
        {
            if( SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( GetImport().GetModel() ) ) //wrong old chart files:
            {
                //for xy charts the first series needs to have a domain
                //if this by error iss not the case the first series is taken s x values
                //needed for wrong files created while having an addin (e.g. BoxPlot)
                m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = m_aSeriesRange;
                m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex++;
                bDeleteSeries = true;
                bCreateXValues = false;//they will be created for the next series
            }
        }
        if( bCreateXValues )
            aDomainInfos.push_back( aDomainInfo );
    }
    else if( bIsBubbleChart )
    {
        if( nDomainCount>1 )
        {
            DomainInfo aDomainInfo( u"values-x"_ustr, maDomainAddresses[1], m_rGlobalSeriesImportInfo.nCurrentDataIndex ) ;
            if( m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress.isEmpty() )
            {
                //for bubble chart the second domain contains the x values which should become an index smaller than y values for own data table
                //->so second first
                m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress = maDomainAddresses[1];
                m_rGlobalSeriesImportInfo.nFirstSecondDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex;
            }
            aDomainInfos.push_back( aDomainInfo );
            m_rGlobalSeriesImportInfo.nCurrentDataIndex++;
        }
        else if( !m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress.isEmpty() )
        {
            DomainInfo aDomainInfo( u"values-x"_ustr, m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress, m_rGlobalSeriesImportInfo.nFirstSecondDomainIndex ) ;
            aDomainInfos.push_back( aDomainInfo );
        }
        if( nDomainCount>0)
        {
            DomainInfo aDomainInfo( u"values-y"_ustr, maDomainAddresses.front(), m_rGlobalSeriesImportInfo.nCurrentDataIndex ) ;
            if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() )
            {
                m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = maDomainAddresses.front();
                m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex;
            }
            aDomainInfos.push_back( aDomainInfo );
            m_rGlobalSeriesImportInfo.nCurrentDataIndex++;
        }
        else if( !m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() )
        {
            DomainInfo aDomainInfo( u"values-y"_ustr, m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress, m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex ) ;
            aDomainInfos.push_back( aDomainInfo );
        }
    }

    if( bDeleteSeries )
    {
        //delete created series
        SchXMLImportHelper::DeleteDataSeries(
            m_xSeries, Reference< chart2::XChartDocument >( GetImport().GetModel(), uno::UNO_QUERY ) );
    }
    else
    {
        //add style
        if( !msAutoStyleName.isEmpty() || mnAttachedAxis != 1 )
        {
            DataRowPointStyle aStyle(
                DataRowPointStyle::DATA_SERIES,
                m_xSeries,
                -1, 1,
                msAutoStyleName, mnAttachedAxis );
            aStyle.mbSymbolSizeForSeriesIsMissingInFile=mbSymbolSizeIsMissingInFile;
            mrStyleVector.push_back( aStyle );
        }
        // And styles for a data-label child element too. In contrast to data-labels as child of data points,
        // an information about absolute position is useless here. We need only style information.
        if (!mDataLabel.msStyleName.isEmpty())
        {
            mDataLabel.msStyleNameOfParent = msAutoStyleName;
            mDataLabel.m_xSeries = m_xSeries;
            mDataLabel.mnAttachedAxis = mnAttachedAxis; // not needed, but be consistent with its parent
            mrStyleVector.push_back(mDataLabel);
        }
    }

    for( std::vector< DomainInfo >::reverse_iterator aIt( aDomainInfos.rbegin() ); aIt!= aDomainInfos.rend(); ++aIt )
    {
        DomainInfo aDomainInfo( *aIt );
        Reference< chart2::data::XLabeledDataSequence2 > xLabeledSeq =
            lcl_createAndAddSequenceToSeries( aDomainInfo.aRole, aDomainInfo.aRange, mxNewDoc, m_xSeries );
        if( xLabeledSeq.is() )
        {
            // register for setting local data if external data provider is not present
            mrLSequencesPerIndex.emplace(
                    tSchXMLIndexWithPart( aDomainInfo.nIndexForLocalData, SCH_XML_PART_VALUES ),
                    Reference< chart2::data::XLabeledDataSequence >(xLabeledSeq, uno::UNO_QUERY_THROW) );
        }
    }

    if( !bDeleteSeries )
    {
        for (auto const& postponedSequence : maPostponedSequences)
        {
            sal_Int32 nNewIndex = postponedSequence.first.first + nDomainCount;
            mrLSequencesPerIndex.emplace( tSchXMLIndexWithPart( nNewIndex, postponedSequence.first.second ), postponedSequence.second );
        }
        m_rGlobalSeriesImportInfo.nCurrentDataIndex++;
    }
    maPostponedSequences.clear();
}

css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLSeries2Context::createFastChildContext(
    sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >&  )
{
    SvXMLImportContext* pContext = nullptr;

    switch(nElement)
    {
        case XML_ELEMENT(CHART, XML_DOMAIN):
            if( m_xSeries.is())
            {
                m_bHasDomainContext = true;
                pContext = new SchXMLDomain2Context(
                    GetImport(), maDomainAddresses );
            }
            break;

        case XML_ELEMENT(CHART, XML_MEAN_VALUE):
            pContext = new SchXMLStatisticsObjectContext(
                mrImportHelper, GetImport(),
                msAutoStyleName,
                mrStyleVector, m_xSeries,
                SchXMLStatisticsObjectContext::CONTEXT_TYPE_MEAN_VALUE_LINE,
                mrLSequencesPerIndex );
            break;
        case XML_ELEMENT(CHART, XML_REGRESSION_CURVE):
            pContext = new SchXMLRegressionCurveObjectContext(
                mrImportHelper, GetImport(),
                mrRegressionStyleVector,
                m_xSeries, maChartSize );
            break;
        case XML_ELEMENT(CHART, XML_ERROR_INDICATOR):
            pContext = new SchXMLStatisticsObjectContext(
                mrImportHelper, GetImport(),
                msAutoStyleName,
                mrStyleVector, m_xSeries,
                SchXMLStatisticsObjectContext::CONTEXT_TYPE_ERROR_INDICATOR,
                mrLSequencesPerIndex );
            break;

        case XML_ELEMENT(CHART, XML_DATA_POINT):
            pContext = new SchXMLDataPointContext( GetImport(),
                                                   mrStyleVector, m_xSeries, mnDataPointIndex, mbSymbolSizeIsMissingInFile );
            break;
        case XML_ELEMENT(CHART, XML_DATA_LABEL):
            // CustomLabels are useless for a data label element as child of a series, because it serves as default
            // for all data labels. But the ctor expects it, so use that of the mDataLabel struct as ersatz.
            pContext = new SchXMLDataLabelContext(GetImport(), mDataLabel.mCustomLabels,
                                                  mDataLabel);
            break;

        case XML_ELEMENT(LO_EXT, XML_PROPERTY_MAPPING):
            pContext = new SchXMLPropertyMappingContext(
                    GetImport(),
                    mrLSequencesPerIndex, m_xSeries );
            break;
        default:
            XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
    }

    return pContext;
}

//static
void SchXMLSeries2Context::initSeriesPropertySets( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles
        , const uno::Reference< frame::XModel >& xChartModel )
{
    // iterate over series first and remind propertysets in map
    // new api <-> old api wrapper
    ::std::map< Reference< chart2::XDataSeries >, Reference< beans::XPropertySet > > aSeriesMap;
    for (auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector)
    {
        if( seriesStyle.meType != DataRowPointStyle::DATA_SERIES )
            continue;

        if( !seriesStyle.m_xOldAPISeries.is() )
            seriesStyle.m_xOldAPISeries = SchXMLSeriesHelper::createOldAPISeriesPropertySet( seriesStyle.m_xSeries, xChartModel );

        aSeriesMap[seriesStyle.m_xSeries] = seriesStyle.m_xOldAPISeries;

    }

    //initialize m_xOldAPISeries for all other styles also
    for (auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector)
    {
        if( seriesStyle.meType == DataRowPointStyle::DATA_SERIES )
            continue;
        seriesStyle.m_xOldAPISeries = aSeriesMap[seriesStyle.m_xSeries];
    }
}

//static
void SchXMLSeries2Context::setDefaultsToSeries( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles )
{
    // iterate over series
    // call initSeriesPropertySets first

    for (const auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector)
    {
        if( seriesStyle.meType != DataRowPointStyle::DATA_SERIES )
            continue;

        try
        {
            uno::Reference< beans::XPropertySet > xSeries( seriesStyle.m_xOldAPISeries );
            if( !xSeries.is() )
                continue;

            if( rSeriesDefaultsAndStyles.maSymbolTypeDefault.hasValue() )
                xSeries->setPropertyValue(u"SymbolType"_ustr,rSeriesDefaultsAndStyles.maSymbolTypeDefault);
            if( rSeriesDefaultsAndStyles.maDataCaptionDefault.hasValue() )
                xSeries->setPropertyValue(u"DataCaption"_ustr,rSeriesDefaultsAndStyles.maDataCaptionDefault);

            if( rSeriesDefaultsAndStyles.maErrorIndicatorDefault.hasValue() )
                xSeries->setPropertyValue(u"ErrorIndicator"_ustr,rSeriesDefaultsAndStyles.maErrorIndicatorDefault);
            if( rSeriesDefaultsAndStyles.maErrorCategoryDefault.hasValue() )
                xSeries->setPropertyValue(u"ErrorCategory"_ustr,rSeriesDefaultsAndStyles.maErrorCategoryDefault);
            if( rSeriesDefaultsAndStyles.maConstantErrorLowDefault.hasValue() )
                xSeries->setPropertyValue(u"ConstantErrorLow"_ustr,rSeriesDefaultsAndStyles.maConstantErrorLowDefault);
            if( rSeriesDefaultsAndStyles.maConstantErrorHighDefault.hasValue() )
                xSeries->setPropertyValue(u"ConstantErrorHigh"_ustr,rSeriesDefaultsAndStyles.maConstantErrorHighDefault);
            if( rSeriesDefaultsAndStyles.maPercentageErrorDefault.hasValue() )
                xSeries->setPropertyValue(u"PercentageError"_ustr,rSeriesDefaultsAndStyles.maPercentageErrorDefault);
            if( rSeriesDefaultsAndStyles.maErrorMarginDefault.hasValue() )
                xSeries->setPropertyValue(u"ErrorMargin"_ustr,rSeriesDefaultsAndStyles.maErrorMarginDefault);

            if( rSeriesDefaultsAndStyles.maMeanValueDefault.hasValue() )
                xSeries->setPropertyValue(u"MeanValue"_ustr,rSeriesDefaultsAndStyles.maMeanValueDefault);
            if( rSeriesDefaultsAndStyles.maRegressionCurvesDefault.hasValue() )
                xSeries->setPropertyValue(u"RegressionCurves"_ustr,rSeriesDefaultsAndStyles.maRegressionCurvesDefault);
        }
        catch( uno::Exception &  )
        {
            //end of series reached
        }
    }
}

// ODF has the line and fill properties in a <style:style> element, which is referenced by the
// <chart:data-label> element. But LibreOffice has them as special label properties of the series
// or point respectively. The following array maps the API name of the ODF property to the name of
// the internal property. Those are of kind "LabelFoo".
// The array is used in methods setStylesToSeries and setStylesToDataPoints.
const std::pair<OUString, OUString> aApiToLabelFooPairs[]
    = { { "LineStyle", "LabelBorderStyle" },
        { "LineWidth", "LabelBorderWidth" },
        { "LineColor", "LabelBorderColor" },
        // The name "LabelBorderDash" is defined, but the associated API name "LineDash" belongs to
        // the <draw:stroke-dash> element and is not used directly as line property.
        //{"LineDash", "LabelBorderDash"},
        { "LineDashName", "LabelBorderDashName" },
        { "LineTransparence", "LabelBorderTransparency" },
        { "FillStyle", "LabelFillStyle" },
        { "FillBackground", "LabelFillBackground" },
        { "FillHatchName", "LabelFillHatchName" },
        { "FillColor", "LabelFillColor" } };


//static
void SchXMLSeries2Context::setStylesToSeries( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles
        , const SvXMLStylesContext* pStylesCtxt
        , const SvXMLStyleContext*& rpStyle
        , OUString& rCurrStyleName
        , const SchXMLImportHelper& rImportHelper
        , const SvXMLImport& rImport
        , bool bIsStockChart
        , tSchXMLLSequencesPerIndex & rInOutLSequencesPerIndex )
{
    // iterate over series
    for (const auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector)
    {
        if (seriesStyle.meType != DataRowPointStyle::DATA_SERIES)
            continue;
        try
        {
            uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries );
            if( !xSeriesProp.is() )
                continue;

            if( seriesStyle.mnAttachedAxis != 1 )
            {
                xSeriesProp->setPropertyValue(u"Axis"_ustr
                    , uno::Any(chart::ChartAxisAssign::SECONDARY_Y) );
            }

            if( seriesStyle.msStyleName.isEmpty())
                continue;

            if( rCurrStyleName != seriesStyle.msStyleName )
            {
                rCurrStyleName = seriesStyle.msStyleName;
                rpStyle = pStylesCtxt->FindStyleChildContext(
                SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName );
            }

            //set style to series
            // note: SvXMLStyleContext::FillPropertySet is not const
            XMLPropStyleContext * pPropStyleContext =
                const_cast< XMLPropStyleContext * >(
                    dynamic_cast< const XMLPropStyleContext * >( rpStyle ));

            if (!pPropStyleContext)
                continue;

            // error bar style must be set before the other error
            // bar properties (which may be alphabetically before
            // this property)
            bool bHasErrorBarRangesFromData = false;
            {
                static constexpr OUString aErrorBarStylePropName( u"ErrorBarStyle"_ustr);
                uno::Any aErrorBarStyle(
                    SchXMLTools::getPropertyFromContext( aErrorBarStylePropName, pPropStyleContext, pStylesCtxt ));
                if( aErrorBarStyle.hasValue())
                {
                    xSeriesProp->setPropertyValue( aErrorBarStylePropName, aErrorBarStyle );
                    sal_Int32 eEBStyle = chart::ErrorBarStyle::NONE;
                    bHasErrorBarRangesFromData =
                        ( ( aErrorBarStyle >>= eEBStyle ) &&
                         eEBStyle == chart::ErrorBarStyle::FROM_DATA );
                }
            }

            //don't set the style to the min max line series of a stock chart
            //otherwise the min max line properties gets overwritten and the series becomes invisible typically
            if (bIsStockChart)
            {
                if (SchXMLSeriesHelper::isCandleStickSeries(
                        seriesStyle.m_xSeries,
                        rImportHelper.GetChartDocument()))
                    continue;
            }

            // Has the series a data-label child element?
            auto pItLabel
                = std::find_if(rSeriesDefaultsAndStyles.maSeriesStyleVector.begin(),
                               rSeriesDefaultsAndStyles.maSeriesStyleVector.end(),
                               [&seriesStyle](const DataRowPointStyle& rStyle) {
                                   return rStyle.meType == DataRowPointStyle::DATA_LABEL_SERIES
                                          && rStyle.msStyleNameOfParent == seriesStyle.msStyleName;
                               });
            if (pItLabel != rSeriesDefaultsAndStyles.maSeriesStyleVector.end())
            {
                // Bring the information from the data-label to the series
                const SvXMLStyleContext* pLabelStyleContext(pStylesCtxt->FindStyleChildContext(
                    SchXMLImportHelper::GetChartFamilyID(), (*pItLabel).msStyleName));
                // note: SvXMLStyleContext::FillPropertySet is not const
                XMLPropStyleContext* pLabelPropStyleContext = const_cast<XMLPropStyleContext*>(
                    dynamic_cast<const XMLPropStyleContext*>(pLabelStyleContext));
                if (pLabelPropStyleContext)
                {
                    // Test each to be mapped property whether the data-label has a value for it.
                    // If found, set it at series.
                    uno::Reference<beans::XPropertySetInfo> xSeriesPropInfo(
                        xSeriesProp->getPropertySetInfo());
                    for (const auto& rPropPair : aApiToLabelFooPairs)
                    {
                        uno::Any aPropValue(SchXMLTools::getPropertyFromContext(
                            rPropPair.first, pLabelPropStyleContext, pStylesCtxt));
                        if (aPropValue.hasValue()
                            && xSeriesPropInfo->hasPropertyByName(rPropPair.second))
                            xSeriesProp->setPropertyValue(rPropPair.second, aPropValue);
                    }
                }
            }

            pPropStyleContext->FillPropertySet( xSeriesProp );
            if( seriesStyle.mbSymbolSizeForSeriesIsMissingInFile )
                lcl_setSymbolSizeIfNeeded( xSeriesProp, rImport );
            if( bHasErrorBarRangesFromData )
                lcl_insertErrorBarLSequencesToMap( rInOutLSequencesPerIndex, xSeriesProp );

        }
        catch( const uno::Exception & )
        {
                TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to series" );
        }
    }
}

// static
void SchXMLSeries2Context::setStylesToRegressionCurves(
                                SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles,
                                const SvXMLStylesContext* pStylesCtxt,
                                const SvXMLStyleContext*& rpStyle,
                                OUString const & rCurrentStyleName )
{
    // iterate over regression etc
    for (auto const& regressionStyle : rSeriesDefaultsAndStyles.maRegressionStyleVector)
    {
        try
        {
            OUString aServiceName;
            XMLPropStyleContext* pPropStyleContext = nullptr;

            if (!rCurrentStyleName.isEmpty())
            {
                XMLPropStyleContext* pCurrent = lcl_GetStylePropContext(pStylesCtxt, rpStyle, rCurrentStyleName);
                if( pCurrent )
                {
                    pPropStyleContext = pCurrent;
                    uno::Any aAny = SchXMLTools::getPropertyFromContext(u"RegressionType", pPropStyleContext, pStylesCtxt);
                    if ( aAny.hasValue() )
                    {
                        aAny >>= aServiceName;
                    }
                }
            }

            if (!regressionStyle.msStyleName.isEmpty())
            {
                XMLPropStyleContext* pCurrent = lcl_GetStylePropContext(pStylesCtxt, rpStyle, regressionStyle.msStyleName);
                if( pCurrent )
                {
                    pPropStyleContext = pCurrent;
                    uno::Any aAny = SchXMLTools::getPropertyFromContext(u"RegressionType", pPropStyleContext, pStylesCtxt);
                    if ( aAny.hasValue() )
                    {
                        aAny >>= aServiceName;
                    }
                }
            }

            if( !aServiceName.isEmpty() )
            {
                Reference< lang::XMultiServiceFactory > xMSF = comphelper::getProcessServiceFactory();
                Reference< chart2::XRegressionCurve > xRegCurve( xMSF->createInstance( aServiceName ), uno::UNO_QUERY_THROW );
                Reference< chart2::XRegressionCurveContainer > xRegCurveCont( regressionStyle.m_xSeries, uno::UNO_QUERY_THROW );

                Reference< beans::XPropertySet > xCurveProperties( xRegCurve, uno::UNO_QUERY );
                if( pPropStyleContext != nullptr)
                    pPropStyleContext->FillPropertySet( xCurveProperties );

                xRegCurve->setEquationProperties( regressionStyle.m_xEquationProperties );

                xRegCurveCont->addRegressionCurve( xRegCurve );
            }
        }
        catch( const uno::Exception& )
        {
            TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to series" );
        }

    }
}

// static
void SchXMLSeries2Context::setStylesToStatisticsObjects( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles
        , const SvXMLStylesContext* pStylesCtxt
        , const SvXMLStyleContext*& rpStyle
        , OUString& rCurrStyleName )
{
    // iterate over regression etc
    for (auto const& seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector)
    {
        if( seriesStyle.meType == DataRowPointStyle::ERROR_INDICATOR ||
            seriesStyle.meType == DataRowPointStyle::MEAN_VALUE )
        {
            if ( seriesStyle.meType == DataRowPointStyle::ERROR_INDICATOR )
            {
                uno::Reference< beans::XPropertySet > xNewSeriesProp(seriesStyle.m_xSeries,uno::UNO_QUERY);

                if (seriesStyle.m_xErrorXProperties.is())
                    xNewSeriesProp->setPropertyValue(u"ErrorBarX"_ustr,uno::Any(seriesStyle.m_xErrorXProperties));

                if (seriesStyle.m_xErrorYProperties.is())
                    xNewSeriesProp->setPropertyValue(u"ErrorBarY"_ustr,uno::Any(seriesStyle.m_xErrorYProperties));
            }

            try
            {
                uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries );
                if( !xSeriesProp.is() )
                    continue;

                if( !seriesStyle.msStyleName.isEmpty())
                {
                    if( rCurrStyleName != seriesStyle.msStyleName )
                    {
                        rCurrStyleName = seriesStyle.msStyleName;
                        rpStyle = pStylesCtxt->FindStyleChildContext(
                            SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName );
                    }

                    // note: SvXMLStyleContext::FillPropertySet is not const
                    XMLPropStyleContext * pPropStyleContext =
                        const_cast< XMLPropStyleContext * >(
                            dynamic_cast< const XMLPropStyleContext * >( rpStyle ));
                    if( pPropStyleContext )
                    {
                        Reference< beans::XPropertySet > xStatPropSet;
                        switch( seriesStyle.meType )
                        {
                            case DataRowPointStyle::MEAN_VALUE:
                                xSeriesProp->getPropertyValue(u"DataMeanValueProperties"_ustr) >>= xStatPropSet;
                                break;
                            case DataRowPointStyle::ERROR_INDICATOR:
                                xSeriesProp->getPropertyValue(u"DataErrorProperties"_ustr)  >>= xStatPropSet;
                                break;
                            default:
                                break;
                        }
                        if( xStatPropSet.is())
                            pPropStyleContext->FillPropertySet( xStatPropSet );
                    }
                }
            }
            catch( const uno::Exception & )
            {
                TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to series" );
            }
        }
    }
}

//static
void SchXMLSeries2Context::setStylesToDataPoints( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles
        , const SvXMLStylesContext* pStylesCtxt
        , const SvXMLStyleContext*& rpStyle
        , OUString& rCurrStyleName
        , const SchXMLImportHelper& rImportHelper
        , const SvXMLImport& rImport
        , bool bIsStockChart, bool bIsDonutChart, bool bSwitchOffLinesForScatter )
{
    for (auto const& seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector)
    {
        if( seriesStyle.meType != DataRowPointStyle::DATA_POINT )
            continue;

        if( seriesStyle.m_nPointIndex == -1 )
            continue;

        uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries );
        if(!xSeriesProp.is())
            continue;

        //ignore datapoint properties for stock charts
        //... todo ...
        if( bIsStockChart )
        {
            if( SchXMLSeriesHelper::isCandleStickSeries( seriesStyle.m_xSeries, rImportHelper.GetChartDocument() ) )
                continue;
        }

        // data point style
        for( sal_Int32 i = 0; i < seriesStyle.m_nPointRepeat; i++ )
        {
            try
            {
                uno::Reference< beans::XPropertySet > xPointProp(
                    SchXMLSeriesHelper::createOldAPIDataPointPropertySet( seriesStyle.m_xSeries, seriesStyle.m_nPointIndex + i
                        , rImportHelper.GetChartDocument() ) );

                if( !xPointProp.is() )
                    continue;

                if( bIsDonutChart )
                {
                    //set special series styles for donut charts first
                    if( rCurrStyleName != seriesStyle.msSeriesStyleNameForDonuts )
                    {
                        rCurrStyleName = seriesStyle.msSeriesStyleNameForDonuts;
                        rpStyle = pStylesCtxt->FindStyleChildContext(
                            SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName );
                    }

                    // note: SvXMLStyleContext::FillPropertySet is not const
                    XMLPropStyleContext * pPropStyleContext =
                        const_cast< XMLPropStyleContext * >(
                            dynamic_cast< const XMLPropStyleContext * >( rpStyle ));
                    if( pPropStyleContext )
                        pPropStyleContext->FillPropertySet( xPointProp );
                }

                try
                {
                    //need to set this explicitly here for old files as the new api does not support this property fully anymore
                    if( bSwitchOffLinesForScatter )
                        xPointProp->setPropertyValue(u"Lines"_ustr,uno::Any(false));
                }
                catch( const uno::Exception & )
                {
                }

                if( rCurrStyleName != seriesStyle.msStyleName )
                {
                    rCurrStyleName = seriesStyle.msStyleName;
                    rpStyle = pStylesCtxt->FindStyleChildContext(
                        SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName );
                }

                // note: SvXMLStyleContext::FillPropertySet is not const
                XMLPropStyleContext * pPropStyleContext =
                    const_cast< XMLPropStyleContext * >(
                        dynamic_cast< const XMLPropStyleContext * >( rpStyle ));
                if (pPropStyleContext)
                {
                    // Has the point a data-label child element?
                    auto pItLabel = std::find_if(
                        rSeriesDefaultsAndStyles.maSeriesStyleVector.begin(),
                        rSeriesDefaultsAndStyles.maSeriesStyleVector.end(),
                        [&seriesStyle](const DataRowPointStyle& rStyle) {
                            return rStyle.meType == DataRowPointStyle::DATA_LABEL_POINT
                                   && rStyle.msStyleNameOfParent == seriesStyle.msStyleName;
                        });
                    if (pItLabel != rSeriesDefaultsAndStyles.maSeriesStyleVector.end())
                    {
                        // Bring the information from the data-label to the point
                        const SvXMLStyleContext* pLabelStyleContext(
                            pStylesCtxt->FindStyleChildContext(
                                SchXMLImportHelper::GetChartFamilyID(), (*pItLabel).msStyleName));
                        // note: SvXMLStyleContext::FillPropertySet is not const
                        XMLPropStyleContext* pLabelPropStyleContext
                            = const_cast<XMLPropStyleContext*>(
                                dynamic_cast<const XMLPropStyleContext*>(pLabelStyleContext));
                        if (pLabelPropStyleContext)
                        {
                            // Test each to be mapped property whether the data-label has a value for it.
                            // If found, set it at the point.
                            uno::Reference<beans::XPropertySetInfo> xPointPropInfo(
                                xPointProp->getPropertySetInfo());
                            for (const auto& rPropPair : aApiToLabelFooPairs)
                            {
                                uno::Any aPropValue(SchXMLTools::getPropertyFromContext(
                                    rPropPair.first, pLabelPropStyleContext, pStylesCtxt));
                                if (aPropValue.hasValue()
                                    && xPointPropInfo->hasPropertyByName(rPropPair.second))
                                    xPointProp->setPropertyValue(rPropPair.second, aPropValue);
                            }
                        }
                    }

                    pPropStyleContext->FillPropertySet( xPointProp );
                    if( seriesStyle.mbSymbolSizeForSeriesIsMissingInFile )
                        lcl_resetSymbolSizeForPointsIfNecessary( xPointProp, rImport, pPropStyleContext, pStylesCtxt );
                    if( !pPropStyleContext->isEmptyDataStyleName() )
                        lcl_setLinkNumberFormatToSourceIfNeeded( xPointProp, pPropStyleContext, pStylesCtxt );
                }

                // Custom labels might be passed as property
                if(const size_t nLabelCount = seriesStyle.mCustomLabels.mLabels.size(); nLabelCount > 0)
                {
                    auto& rCustomLabels = seriesStyle.mCustomLabels;

                    Sequence< Reference<chart2::XDataPointCustomLabelField>> xLabels(nLabelCount);
                    auto pxLabels = xLabels.getArray();
                    const Reference< uno::XComponentContext >& xContext( comphelper::getProcessComponentContext() );
                    for( size_t j = 0; j < nLabelCount; ++j )
                    {
                        Reference< chart2::XDataPointCustomLabelField > xCustomLabel = chart2::DataPointCustomLabelField::create(xContext);
                        pxLabels[j] = xCustomLabel;
                        xCustomLabel->setString(rCustomLabels.mLabels[j]);
                        if ( j == 0 && rCustomLabels.mbDataLabelsRange)
                        {
                            xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE);
                            xCustomLabel->setGuid(rCustomLabels.msLabelGuid);
                            xCustomLabel->setCellRange(rCustomLabels.msLabelsCellRange);
                            xCustomLabel->setDataLabelsRange(true);
                        }
                        else
                        {
                            xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT);
                        }

                        // Restore character properties on the text span manually, till
                        // SchXMLExportHelper_Impl::exportCustomLabel() does not write the style.
                        uno::Reference<beans::XPropertySetInfo> xPointPropInfo
                            = xPointProp->getPropertySetInfo();
                        if (xPointPropInfo.is())
                        {
                            uno::Sequence<beans::Property> aProperties = xPointPropInfo->getProperties();
                            for (const auto& rProperty : aProperties)
                            {
                                if (!rProperty.Name.startsWith("Char")
                                    || rProperty.Name.startsWith("Chart"))
                                {
                                    continue;
                                }

                                xCustomLabel->setPropertyValue(
                                    rProperty.Name, xPointProp->getPropertyValue(rProperty.Name));
                            }
                        }
                    }

                    xPointProp->setPropertyValue(u"CustomLabelFields"_ustr, uno::Any(xLabels));
                    xPointProp->setPropertyValue(u"DataCaption"_ustr, uno::Any(chart::ChartDataCaption::CUSTOM));
                }

                if( seriesStyle.mCustomLabelPos[0] != 0.0 || seriesStyle.mCustomLabelPos[1] != 0.0 )
                {
                    chart2::RelativePosition aCustomlabelPosition;
                    aCustomlabelPosition.Primary = seriesStyle.mCustomLabelPos[0];
                    aCustomlabelPosition.Secondary = seriesStyle.mCustomLabelPos[1];
                    xPointProp->setPropertyValue(u"CustomLabelPosition"_ustr, uno::Any(aCustomlabelPosition));
                }
            }
            catch( const uno::Exception & )
            {
                TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to data points" );
            }
        }
    }   // styles iterator
}

//static
void SchXMLSeries2Context::switchSeriesLinesOff( ::std::vector< DataRowPointStyle >& rSeriesStyleVector )
{
    // iterate over series
    for (auto const& seriesStyle : rSeriesStyleVector)
    {
        if( seriesStyle.meType != DataRowPointStyle::DATA_SERIES )
            continue;

        try
        {
            uno::Reference< beans::XPropertySet > xSeries( seriesStyle.m_xOldAPISeries );
            if( !xSeries.is() )
                continue;

            xSeries->setPropertyValue(u"Lines"_ustr,uno::Any(false));
        }
        catch( uno::Exception &  )
        {
            //end of series reached
        }
    }
}

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