/* -*- 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 <sal/config.h>

#include <cstddef>
#include <map>

#include <ObjectIdentifier.hxx>
#include <TitleHelper.hxx>
#include <ChartModel.hxx>
#include <ChartModelHelper.hxx>
#include <ChartType.hxx>
#include <GridProperties.hxx>
#include <Axis.hxx>
#include <AxisHelper.hxx>
#include <servicenames_charttypes.hxx>
#include <DiagramHelper.hxx>
#include <Diagram.hxx>
#include <unonames.hxx>
#include <BaseCoordinateSystem.hxx>
#include <DataSeries.hxx>
#include <RegressionCurveModel.hxx>

#include <com/sun/star/chart2/XAxis.hpp>
#include <com/sun/star/awt/Point.hpp>
#include <com/sun/star/drawing/XShape.hpp>

#include <rtl/ustrbuf.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <utility>

namespace com::sun::star::drawing { class XShape; }

namespace chart
{
using namespace ::com::sun::star;
using namespace ::com::sun::star::chart2;

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

constexpr OUString m_aMultiClick = u"MultiClick"_ustr;
constexpr OUString m_aDragMethodEquals = u"DragMethod="_ustr;
constexpr OUString m_aDragParameterEquals = u"DragParameter="_ustr;
constexpr OUString m_aProtocol = u"CID/"_ustr;
constexpr OUString m_aPieSegmentDragMethodServiceName(u"PieSegmentDragging"_ustr);

namespace
{

OUString lcl_createClassificationStringForType( ObjectType eObjectType
            , std::u16string_view rDragMethodServiceName
            , std::u16string_view rDragParameterString
            )
{
    OUStringBuffer aRet;
    switch( eObjectType )
    {
        //these object types are all selected only after their parents was selected before
        case OBJECTTYPE_LEGEND_ENTRY: //parent is intended to be OBJECTTYPE_LEGEND
        case OBJECTTYPE_DATA_POINT: //parent is intended to be OBJECTTYPE_DATA_SERIES
        case OBJECTTYPE_DATA_LABEL: //parent is intended to be OBJECTTYPE_DATA_LABELS
        case OBJECTTYPE_DATA_ERRORS_X: //parent is intended to be OBJECTTYPE_DATA_ERRORS
        case OBJECTTYPE_DATA_ERRORS_Y: //parent is intended to be OBJECTTYPE_DATA_ERRORS
        case OBJECTTYPE_DATA_ERRORS_Z: //parent is intended to be OBJECTTYPE_DATA_ERRORS
            aRet=m_aMultiClick;
            break;
        default:
            break;//empty string
    }
    if( !rDragMethodServiceName.empty() )
    {
        if( !aRet.isEmpty() )
            aRet.append(":");
        aRet.append( OUString::Concat(m_aDragMethodEquals) + rDragMethodServiceName );

        if( !rDragParameterString.empty() )
        {
            if( !aRet.isEmpty() )
                aRet.append(":");
            aRet.append( OUString::Concat(m_aDragParameterEquals) + rDragParameterString );
        }
    }
    return aRet.makeStringAndClear();
}

typedef std::map< TitleHelper::eTitleType, OUString > tTitleMap;
const tTitleMap& lcl_getTitleMap()
{
    //maps the title type to the ParentParticle for that title
    static tTitleMap s_aTitleMap{
        {TitleHelper::MAIN_TITLE, ""},
        {TitleHelper::SUB_TITLE, "D=0"},
        {TitleHelper::X_AXIS_TITLE, "D=0:CS=0:Axis=0,0"},
        {TitleHelper::Y_AXIS_TITLE, "D=0:CS=0:Axis=1,0"},
        {TitleHelper::Z_AXIS_TITLE, "D=0:CS=0:Axis=2,0"},
        {TitleHelper::SECONDARY_X_AXIS_TITLE, "D=0:CS=0:Axis=0,1"},
        {TitleHelper::SECONDARY_Y_AXIS_TITLE, "D=0:CS=0:Axis=1,1"}};
    return s_aTitleMap;
}

OUString lcl_getTitleParentParticle( TitleHelper::eTitleType aTitleType )
{
    OUString aRet;

    const tTitleMap& rMap = lcl_getTitleMap();
    tTitleMap::const_iterator aIt( rMap.find( aTitleType ) );
    if( aIt != rMap.end())
        aRet = (*aIt).second;

    return aRet;
}

rtl::Reference<ChartType> lcl_getFirstStockChartType( const rtl::Reference<::chart::ChartModel>& xChartModel )
{
    rtl::Reference< Diagram > xDiagram( xChartModel->getFirstChartDiagram() );
    if(!xDiagram.is())
        return nullptr;

    //iterate through all coordinate systems

    const std::vector< rtl::Reference< BaseCoordinateSystem > > & aCooSysList( xDiagram->getBaseCoordinateSystems() );
    for( rtl::Reference< BaseCoordinateSystem > const & coords : aCooSysList )
    {
        //iterate through all chart types in the current coordinate system
        for( rtl::Reference< ChartType > const & xChartType : coords->getChartTypes2() )
        {
            OUString aChartType = xChartType->getChartType();
            if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
                return xChartType;
        }
    }
    return nullptr;
}

std::u16string_view lcl_getIndexStringAfterString( std::u16string_view rString, std::u16string_view rSearchString )
{
    size_t nIndexStart = rString.rfind( rSearchString );
    if( nIndexStart == std::u16string_view::npos )
        return std::u16string_view();
    nIndexStart += rSearchString.size();
    size_t nIndexEnd = rString.size();
    size_t nNextColon = rString.find( ':', nIndexStart );
    if( nNextColon != std::u16string_view::npos )
        nIndexEnd = nNextColon;
    return rString.substr(nIndexStart,nIndexEnd-nIndexStart);
}

sal_Int32 lcl_StringToIndex( std::u16string_view rIndexString )
{
    sal_Int32 nRet = -1;
    if( !rIndexString.empty() )
    {
        nRet = o3tl::toInt32(rIndexString);
        if( nRet < -1 )
            nRet = -1;
    }
    return nRet;
}

void lcl_parseCooSysIndices( sal_Int32& rnDiagram, sal_Int32& rnCooSys, std::u16string_view rString )
{
    rnDiagram = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, u"D=" ) );
    rnCooSys = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, u"CS=" ) );
}

void lcl_parseAxisIndices( sal_Int32& rnDimensionIndex, sal_Int32& rnAxisIndex, std::u16string_view rString )
{
    std::u16string_view aAxisIndexString = lcl_getIndexStringAfterString( rString, u":Axis=" );
    sal_Int32 nCharacterIndex=0;
    rnDimensionIndex = lcl_StringToIndex( o3tl::getToken(aAxisIndexString, 0, ',', nCharacterIndex ) );
    rnAxisIndex = lcl_StringToIndex( o3tl::getToken(aAxisIndexString, 0, ',', nCharacterIndex ) );
}

void lcl_parseGridIndices( sal_Int32& rnSubGridIndex, std::u16string_view rString )
{
    rnSubGridIndex = -1;
    rnSubGridIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, u":SubGrid=" ) );
}

void lcl_parseSeriesIndices( sal_Int32& rnChartTypeIndex, sal_Int32& rnSeriesIndex, sal_Int32& rnPointIndex, std::u16string_view rString )
{
    rnChartTypeIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, u"CT=" ) );
    rnSeriesIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, u"Series=" ) );
    rnPointIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, u"Point=" ) );
}

void lcl_getDiagramAndCooSys( std::u16string_view rObjectCID
                , const rtl::Reference<::chart::ChartModel>& xChartModel
                , rtl::Reference< Diagram >& xDiagram
                , rtl::Reference< BaseCoordinateSystem >& xCooSys )
{
    sal_Int32 nDiagramIndex = -1;
    sal_Int32 nCooSysIndex = -1;
    lcl_parseCooSysIndices( nDiagramIndex, nCooSysIndex, rObjectCID );
    xDiagram = xChartModel->getFirstChartDiagram();//todo use nDiagramIndex when more than one diagram is possible in future
    if( !xDiagram.is() )
        return;

    if( nCooSysIndex > -1 )
    {
        const std::vector< rtl::Reference< BaseCoordinateSystem > > aCooSysList( xDiagram->getBaseCoordinateSystems() );
        if( o3tl::make_unsigned(nCooSysIndex) < aCooSysList.size() )
            xCooSys = aCooSysList[nCooSysIndex];
    }
}

} //anonymous namespace

ObjectIdentifier::ObjectIdentifier()
{
}

ObjectIdentifier::ObjectIdentifier( OUString aObjectCID )
    :m_aObjectCID(std::move( aObjectCID ))
{
}

ObjectIdentifier::ObjectIdentifier( const Reference< drawing::XShape >& rxShape )
    : m_xAdditionalShape( rxShape )
{
}

ObjectIdentifier::ObjectIdentifier( const Any& rAny )
{
    const uno::Type& rType = rAny.getValueType();
    if ( rType == cppu::UnoType<OUString>::get() )
    {
        rAny >>= m_aObjectCID;
    }
    else if ( rType == cppu::UnoType< drawing::XShape >::get() )
    {
        rAny >>= m_xAdditionalShape;
    }
}

bool ObjectIdentifier::operator==( const ObjectIdentifier& rOID ) const
{
    return areIdenticalObjects( m_aObjectCID, rOID.m_aObjectCID ) &&
         ( m_xAdditionalShape == rOID.m_xAdditionalShape );
}

bool ObjectIdentifier::operator!=( const ObjectIdentifier& rOID ) const
{
    return !operator==( rOID );
}

bool ObjectIdentifier::operator<( const ObjectIdentifier& rOID ) const
{
    bool bReturn = false;
    if ( !(m_aObjectCID.isEmpty() || rOID.m_aObjectCID.isEmpty()) )
    {
        bReturn = ( m_aObjectCID.compareTo( rOID.m_aObjectCID ) < 0 );
    }
    else if ( !m_aObjectCID.isEmpty() )
    {
        bReturn = true;
    }
    else if ( !rOID.m_aObjectCID.isEmpty() )
    {
        bReturn = false;
    }
    else if ( m_xAdditionalShape.is() && rOID.m_xAdditionalShape.is() )
    {
        bReturn = ( m_xAdditionalShape < rOID.m_xAdditionalShape );
    }
    return bReturn;
}

OUString ObjectIdentifier::createClassifiedIdentifierForObject(
          const rtl::Reference< ::chart::Title >& xTitle
        , const rtl::Reference<::chart::ChartModel>& xChartModel )
{
    TitleHelper::eTitleType aTitleType;
    OUString aRet;
    const std::u16string_view aObjectID;
    const std::u16string_view aDragMethodServiceName;
    const std::u16string_view aDragParameterString;
    if( TitleHelper::getTitleType( aTitleType, xTitle, xChartModel ) )
    {
        enum ObjectType eObjectType = OBJECTTYPE_TITLE;
        OUString aParentParticle = lcl_getTitleParentParticle( aTitleType );
        aRet = ObjectIdentifier::createClassifiedIdentifierWithParent(
            eObjectType, aObjectID, aParentParticle, aDragMethodServiceName, aDragParameterString );
    }
    return aRet;
}

OUString ObjectIdentifier::createClassifiedIdentifierForObject(
          const Reference< uno::XInterface >& xObject
        , const rtl::Reference<::chart::ChartModel>& xChartModel )
{
    OUString aRet;

    enum ObjectType eObjectType = OBJECTTYPE_UNKNOWN;
    const std::u16string_view aObjectID;
    OUString aParentParticle;
    const std::u16string_view aDragMethodServiceName;
    const std::u16string_view aDragParameterString;

    try
    {
        //title
        if( ::chart::Title* pTitle = dynamic_cast<::chart::Title*>(xObject.get()) )
            return createClassifiedIdentifierForObject(rtl::Reference<Title>(pTitle), xChartModel);

        uno::Reference<chart2::XDataTable> xDataTable(xObject, uno::UNO_QUERY);
        if (xDataTable.is())
        {
            return createClassifiedIdentifierForParticle(createParticleForDataTable(xChartModel));
        }

        //axis
        rtl::Reference< Axis > xAxis = dynamic_cast<Axis*>( xObject.get() );
        if( xAxis.is() )
        {
            rtl::Reference<Diagram> xDiagram = xChartModel->getFirstChartDiagram();
            rtl::Reference< BaseCoordinateSystem > xCooSys( AxisHelper::getCoordinateSystemOfAxis( xAxis, xDiagram ) );
            OUString aCooSysParticle( createParticleForCoordinateSystem( xCooSys, xChartModel ) );
            sal_Int32 nDimensionIndex=-1;
            sal_Int32 nAxisIndex=-1;
            AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex );
            OUString aAxisParticle( createParticleForAxis( nDimensionIndex, nAxisIndex ) );
            return createClassifiedIdentifierForParticles( aCooSysParticle, aAxisParticle );
        }

        //legend
        Reference< XLegend > xLegend( xObject, uno::UNO_QUERY );
        if( xLegend.is() )
        {
            return createClassifiedIdentifierForParticle( createParticleForLegend( xChartModel ) );
        }

        //diagram
        Reference< XDiagram > xDiagram( xObject, uno::UNO_QUERY );
        if( xDiagram.is() )
        {
            return createClassifiedIdentifierForParticle( createParticleForDiagram() );
        }

        //todo
        //XDataSeries
        //CooSys
        //charttype
        //datapoint?
        //Gridproperties
    }
    catch(const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("chart2");
    }

    if( eObjectType != OBJECTTYPE_UNKNOWN )
    {
        aRet = ObjectIdentifier::createClassifiedIdentifierWithParent(
            eObjectType, aObjectID, aParentParticle, aDragMethodServiceName, aDragParameterString );
    }
    else
    {
        OSL_FAIL("give object could not be identified in createClassifiedIdentifierForObject");
    }

    return aRet;
}

OUString ObjectIdentifier::createClassifiedIdentifierForObject(
          const rtl::Reference< Legend >& xLegend
        , const rtl::Reference<::chart::ChartModel>& xChartModel )
{
    try
    {
        if( xLegend.is() )
        {
            return createClassifiedIdentifierForParticle( createParticleForLegend( xChartModel ) );
        }
    }
    catch(const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("chart2");
    }

    OSL_FAIL("give object could not be identified in createClassifiedIdentifierForObject");

    return OUString();
}

OUString ObjectIdentifier::createClassifiedIdentifierForObject(
          const rtl::Reference<::chart::Axis>& xAxis
        , const rtl::Reference<::chart::ChartModel>& xChartModel )
{
    try
    {
        //axis
        if( xAxis.is() )
        {
            rtl::Reference<Diagram> xDiagram = xChartModel->getFirstChartDiagram();
            rtl::Reference< BaseCoordinateSystem > xCooSys( AxisHelper::getCoordinateSystemOfAxis( xAxis, xDiagram ) );
            OUString aCooSysParticle( createParticleForCoordinateSystem( xCooSys, xChartModel ) );
            sal_Int32 nDimensionIndex=-1;
            sal_Int32 nAxisIndex=-1;
            AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex );
            OUString aAxisParticle( createParticleForAxis( nDimensionIndex, nAxisIndex ) );
            return createClassifiedIdentifierForParticles( aCooSysParticle, aAxisParticle );
        }
    }
    catch(const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("chart2");
    }

    OSL_FAIL("give object could not be identified in createClassifiedIdentifierForObject");

    return OUString();
}

OUString ObjectIdentifier::createClassifiedIdentifierForParticle(
         std::u16string_view rParticle )
{
    return ObjectIdentifier::createClassifiedIdentifierForParticles( rParticle, u"" );
}

OUString ObjectIdentifier::createClassifiedIdentifierForParticles(
            std::u16string_view rParentParticle
          , std::u16string_view rChildParticle
          , std::u16string_view rDragMethodServiceName
          , std::u16string_view rDragParameterString )
{
    ObjectType eObjectType( ObjectIdentifier::getObjectType( rChildParticle ) );
    if( eObjectType == OBJECTTYPE_UNKNOWN )
        eObjectType = ObjectIdentifier::getObjectType( rParentParticle );

    OUStringBuffer aRet( m_aProtocol +
        lcl_createClassificationStringForType( eObjectType, rDragMethodServiceName, rDragParameterString ));
    if(aRet.getLength() > m_aProtocol.getLength())
        aRet.append("/");

    if(!rParentParticle.empty())
    {
        aRet.append(rParentParticle);
        if( !rChildParticle.empty() )
            aRet.append(":");
    }
    aRet.append(rChildParticle);

    return aRet.makeStringAndClear();
}

OUString ObjectIdentifier::createParticleForDiagram()
{
    //TODO: if more than one diagram is implemented, add the correct diagram index here
    return "D=0";
}

OUString ObjectIdentifier::createParticleForCoordinateSystem(
          const rtl::Reference< BaseCoordinateSystem >& xCooSys
        , const rtl::Reference<::chart::ChartModel>& xChartModel )
{
    OUString aRet;

    rtl::Reference< Diagram > xDiagram( xChartModel->getFirstChartDiagram() );
    if( xDiagram.is() )
    {
        std::size_t nCooSysIndex = 0;
        const std::vector< rtl::Reference< BaseCoordinateSystem > > & aCooSysList( xDiagram->getBaseCoordinateSystems() );
        for( ; nCooSysIndex < aCooSysList.size(); ++nCooSysIndex )
        {
            if( xCooSys == aCooSysList[nCooSysIndex] )
            {
                aRet = ObjectIdentifier::createParticleForDiagram() + ":CS=" + OUString::number( nCooSysIndex );
                break;
            }
        }
    }

    return aRet;
}

OUString ObjectIdentifier::createParticleForAxis(
            sal_Int32 nDimensionIndex
          , sal_Int32 nAxisIndex )
{
    return "Axis=" +
        OUString::number( nDimensionIndex ) +
        "," +
        OUString::number( nAxisIndex );
}

OUString ObjectIdentifier::createParticleForGrid(
            sal_Int32 nDimensionIndex
          , sal_Int32 nAxisIndex )
{
    OUString aRet = "Axis=" + OUString::number( nDimensionIndex )
                  + "," + OUString::number( nAxisIndex ) + ":Grid=0";

    return aRet;
}

OUString ObjectIdentifier::createClassifiedIdentifierForGrid(
          const Reference< XAxis >& xAxis
        , const rtl::Reference<::chart::ChartModel>& xChartModel
        , sal_Int32 nSubGridIndex )
{
    //-1: main grid, 0: first subgrid etc

    OUString aAxisCID( createClassifiedIdentifierForObject( xAxis, xChartModel ) );
    OUString aGridCID( addChildParticle( aAxisCID
        , createChildParticleWithIndex( OBJECTTYPE_GRID, 0 ) ) );
    if( nSubGridIndex >= 0 )
    {
        aGridCID = addChildParticle( aGridCID
            , createChildParticleWithIndex( OBJECTTYPE_SUBGRID, 0 ) );
    }
    return aGridCID;
}

OUString ObjectIdentifier::createParticleForSeries(
              sal_Int32 nDiagramIndex, sal_Int32 nCooSysIndex
            , sal_Int32 nChartTypeIndex, sal_Int32 nSeriesIndex )
{
    return
        "D=" + OUString::number( nDiagramIndex ) +
        ":CS=" + OUString::number( nCooSysIndex ) +
        ":CT=" + OUString::number( nChartTypeIndex ) +
        ":" + getStringForType( OBJECTTYPE_DATA_SERIES ) + "=" +
        OUString::number( nSeriesIndex );
}


OUString ObjectIdentifier::createParticleForLegend(
        const rtl::Reference<::chart::ChartModel>& )
{
    //todo: if more than one diagram is implemented, find the correct diagram which is owner of the given legend

    return ObjectIdentifier::createParticleForDiagram() + ":" + getStringForType( OBJECTTYPE_LEGEND ) + "=";
}

OUString ObjectIdentifier::createParticleForDataTable(const rtl::Reference<::chart::ChartModel>& /* xChartModel */)
{
    return ObjectIdentifier::createParticleForDiagram() + ":" + getStringForType(OBJECTTYPE_DATA_TABLE) + "=";
}

OUString ObjectIdentifier::createClassifiedIdentifier(
        enum ObjectType eObjectType //e.g. OBJECTTYPE_DATA_SERIES
        , std::u16string_view rParticleID )//e.g. SeriesID
{
    return createClassifiedIdentifierWithParent(
        eObjectType, rParticleID, u"" );
}

OUString ObjectIdentifier::createClassifiedIdentifierWithParent(
        enum ObjectType eObjectType //e.g. OBJECTTYPE_DATA_POINT or OBJECTTYPE_GRID
        , std::u16string_view rParticleID //e.g. Point Index or SubGrid Index
        , std::u16string_view rParentPartical //e.g. "Series=SeriesID" or "Grid=GridId"
        , std::u16string_view rDragMethodServiceName
        , std::u16string_view rDragParameterString
        )
        //, bool bIsMultiClickObject ) //e.g. true
{
    //e.g. "MultiClick/Series=2:Point=34"

    OUStringBuffer aRet( m_aProtocol +
        lcl_createClassificationStringForType( eObjectType, rDragMethodServiceName, rDragParameterString ));
    if(aRet.getLength() > m_aProtocol.getLength())
        aRet.append("/");
    aRet.append(rParentPartical);
    if(!rParentPartical.empty())
        aRet.append(":");

    aRet.append(getStringForType( eObjectType ) + "=" + rParticleID);

    return aRet.makeStringAndClear();
}

const OUString& ObjectIdentifier::getPieSegmentDragMethodServiceName()
{
    return m_aPieSegmentDragMethodServiceName;
}

OUString ObjectIdentifier::createPieSegmentDragParameterString(
          sal_Int32 nOffsetPercent
        , const awt::Point& rMinimumPosition
        , const awt::Point& rMaximumPosition )
{
    OUString aRet = OUString::number( nOffsetPercent )
                  + "," + OUString::number( rMinimumPosition.X )
                  + "," + OUString::number( rMinimumPosition.Y )
                  + "," + OUString::number( rMaximumPosition.X )
                  + "," + OUString::number( rMaximumPosition.Y );
    return aRet;
}

bool ObjectIdentifier::parsePieSegmentDragParameterString(
          std::u16string_view rDragParameterString
        , sal_Int32& rOffsetPercent
        , awt::Point& rMinimumPosition
        , awt::Point& rMaximumPosition )
{
    sal_Int32 nCharacterIndex = 0;

    std::u16string_view aValueString( o3tl::getToken(rDragParameterString, 0, ',', nCharacterIndex ) );
    rOffsetPercent = o3tl::toInt32(aValueString);
    if( nCharacterIndex < 0 )
        return false;

    aValueString = o3tl::getToken(rDragParameterString, 0, ',', nCharacterIndex );
    rMinimumPosition.X = o3tl::toInt32(aValueString);
    if( nCharacterIndex < 0 )
        return false;

    aValueString = o3tl::getToken(rDragParameterString, 0, ',', nCharacterIndex );
    rMinimumPosition.Y = o3tl::toInt32(aValueString);
    if( nCharacterIndex < 0 )
        return false;

    aValueString = o3tl::getToken(rDragParameterString, 0, ',', nCharacterIndex );
    rMaximumPosition.X = o3tl::toInt32(aValueString);
    if( nCharacterIndex < 0 )
        return false;

    aValueString = o3tl::getToken(rDragParameterString, 0, ',', nCharacterIndex );
    rMaximumPosition.Y = o3tl::toInt32(aValueString);
    return nCharacterIndex >= 0;
}

std::u16string_view ObjectIdentifier::getDragMethodServiceName( std::u16string_view rCID )
{
    std::u16string_view aRet;

    size_t nIndexStart = rCID.find( m_aDragMethodEquals );
    if( nIndexStart != std::u16string_view::npos )
    {
        nIndexStart = rCID.find( '=', nIndexStart );
        if( nIndexStart != std::u16string_view::npos )
        {
            nIndexStart++;
            size_t nNextSlash = rCID.find( '/', nIndexStart );
            if( nNextSlash != std::u16string_view::npos )
            {
                sal_Int32 nIndexEnd = nNextSlash;
                size_t nNextColon = rCID.find( ':', nIndexStart );
                if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
                    nIndexEnd = nNextColon;
                aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
            }
        }
    }
    return aRet;
}

std::u16string_view ObjectIdentifier::getDragParameterString( std::u16string_view rCID )
{
    std::u16string_view aRet;

    size_t nIndexStart = rCID.find( m_aDragParameterEquals );
    if( nIndexStart != std::u16string_view::npos )
    {
        nIndexStart = rCID.find( '=', nIndexStart );
        if( nIndexStart != std::u16string_view::npos )
        {
            nIndexStart++;
            size_t nNextSlash = rCID.find( '/', nIndexStart );
            if( nNextSlash != std::u16string_view::npos )
            {
                sal_Int32 nIndexEnd = nNextSlash;
                size_t nNextColon = rCID.find( ':', nIndexStart );
                if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
                    nIndexEnd = nNextColon;
                aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
            }
        }
    }
    return aRet;
}

bool ObjectIdentifier::isDragableObject( std::u16string_view rClassifiedIdentifier )
{
    bool bReturn = false;
    ObjectType eObjectType = ObjectIdentifier::getObjectType( rClassifiedIdentifier );
    switch( eObjectType )
    {
        case OBJECTTYPE_TITLE:
        case OBJECTTYPE_LEGEND:
        case OBJECTTYPE_DIAGRAM:
        case OBJECTTYPE_DATA_LABEL:
        case OBJECTTYPE_DATA_CURVE_EQUATION:
        //case OBJECTTYPE_DIAGRAM_WALL:
            bReturn = true;
            break;
        default:
            std::u16string_view aDragMethodServiceName( ObjectIdentifier::getDragMethodServiceName( rClassifiedIdentifier ) );
            bReturn = !aDragMethodServiceName.empty();
            break;
    }
    return bReturn;
}

bool ObjectIdentifier::isDragableObject() const
{
    bool bReturn = false;
    if ( isAutoGeneratedObject() )
    {
        bReturn = isDragableObject( m_aObjectCID );
    }
    else if ( isAdditionalShape() )
    {
        bReturn = true;
    }
    return bReturn;
}

bool ObjectIdentifier::isRotateableObject( std::u16string_view rClassifiedIdentifier )
{
    bool bReturn = false;
    ObjectType eObjectType = ObjectIdentifier::getObjectType( rClassifiedIdentifier );
    switch( eObjectType )
    {
        case OBJECTTYPE_DIAGRAM:
        //case OBJECTTYPE_DIAGRAM_WALL:
            bReturn = true;
            break;
        default:
            bReturn = false;
            break;
    }
    return bReturn;
}

bool ObjectIdentifier::isMultiClickObject( std::u16string_view rClassifiedIdentifier )
{
    //the name of a shape is it's ClassifiedIdentifier

    //a MultiClickObject is an object that is selectable by more than one click only ;
    //before a MultiClickObject can be selected it is necessary that a named parent group object
    //was selected before;

    //!!!!! by definition the name of a MultiClickObject starts with "CID/MultiClick:"
    bool bRet = o3tl::starts_with(rClassifiedIdentifier.substr( m_aProtocol.getLength() ), m_aMultiClick);
    return bRet;
}

bool ObjectIdentifier::areSiblings( std::u16string_view rCID1, std::u16string_view rCID2 )
{
    bool bRet=false;
    size_t nLastSign1 = rCID1.rfind( '=' );
    size_t nLastSign2 = rCID2.rfind( '=' );
    if( nLastSign1 == rCID1.find( '=' ) )//CID cannot be sibling if only one "=" occurs
        bRet=false;
    else if( nLastSign2 == rCID2.find( '=' ) )//CID cannot be sibling if only one "=" occurs
        bRet=false;
    else if( ObjectIdentifier::areIdenticalObjects( rCID1, rCID2 ) )
        bRet=false;
    else
    {
        std::u16string_view aParent1( ObjectIdentifier::getFullParentParticle( rCID1 ) );
        if( !aParent1.empty() )
        {
            std::u16string_view aParent2( ObjectIdentifier::getFullParentParticle( rCID2 ) );
            bRet=aParent1 == aParent2;
        }
        //legend entries are special:
        if(!bRet)
        {
            if( getObjectType(rCID1) == OBJECTTYPE_LEGEND_ENTRY
                && getObjectType(rCID2) == OBJECTTYPE_LEGEND_ENTRY )
                bRet = true;
        }
    }
    return bRet;
}

bool ObjectIdentifier::areIdenticalObjects( std::u16string_view rCID1, std::u16string_view rCID2 )
{
    if( rCID1 == rCID2 )
        return true;
    //draggable pie or donut segments need special treatment, as their CIDs do change with offset
    {
        if( rCID1.find( m_aPieSegmentDragMethodServiceName ) == std::u16string_view::npos
            || rCID2.find( m_aPieSegmentDragMethodServiceName ) == std::u16string_view::npos )
            return false;

        OUString aID1( ObjectIdentifier::getObjectID( rCID1 ) );
        OUString aID2( ObjectIdentifier::getObjectID( rCID2 ) );
        if( !aID1.isEmpty() &&  aID1 == aID2 )
            return true;
        }
    return false;
}

OUString ObjectIdentifier::getStringForType( ObjectType eObjectType )
{
    OUString aRet;
    switch( eObjectType )
    {
        case OBJECTTYPE_PAGE:
                aRet="Page";
                break;
        case OBJECTTYPE_TITLE:
                aRet="Title";
                break;
        case OBJECTTYPE_LEGEND:
                aRet="Legend";
                break;
        case OBJECTTYPE_LEGEND_ENTRY:
                aRet="LegendEntry";
                break;
        case OBJECTTYPE_DIAGRAM:
                aRet="D";
                break;
        case OBJECTTYPE_DIAGRAM_WALL:
                aRet="DiagramWall";
                break;
        case OBJECTTYPE_DIAGRAM_FLOOR:
                aRet="DiagramFloor";
                break;
        case OBJECTTYPE_AXIS:
                aRet="Axis";
                break;
        case OBJECTTYPE_AXIS_UNITLABEL:
                aRet="AxisUnitLabel";
                break;
        case OBJECTTYPE_GRID:
                aRet="Grid";
                break;
        case OBJECTTYPE_SUBGRID:
                aRet="SubGrid";
                break;
        case OBJECTTYPE_DATA_SERIES:
                aRet="Series";
                break;
        case OBJECTTYPE_DATA_POINT:
                aRet="Point";
                break;
        case OBJECTTYPE_DATA_LABELS:
                aRet="DataLabels";
                break;
        case OBJECTTYPE_DATA_LABEL:
                aRet="DataLabel";
                break;
        case OBJECTTYPE_DATA_ERRORS_X:
                aRet="ErrorsX";
                break;
        case OBJECTTYPE_DATA_ERRORS_Y:
                aRet="ErrorsY";
                break;
        case OBJECTTYPE_DATA_ERRORS_Z:
                aRet="ErrorsZ";
                break;
        case OBJECTTYPE_DATA_CURVE:
                aRet="Curve";
                break;
        case OBJECTTYPE_DATA_CURVE_EQUATION:
                aRet="Equation";
                break;
        case OBJECTTYPE_DATA_AVERAGE_LINE:
                aRet="Average";
                break;
        case OBJECTTYPE_DATA_STOCK_RANGE:
                aRet="StockRange";
                break;
        case OBJECTTYPE_DATA_STOCK_LOSS:
                aRet="StockLoss";
                break;
        case OBJECTTYPE_DATA_STOCK_GAIN:
                aRet="StockGain";
                break;
        case OBJECTTYPE_DATA_TABLE:
                aRet="DataTable";
                break;
        default: //OBJECTTYPE_UNKNOWN
            ;
    }
    return aRet;
}

ObjectType ObjectIdentifier::getObjectType( std::u16string_view aCID )
{
    ObjectType eRet;
    size_t nLastSign = aCID.rfind( ':' );//last sign before the type string
    if(nLastSign == std::u16string_view::npos)
        nLastSign = aCID.rfind( '/' );
    if(nLastSign == std::u16string_view::npos)
    {
        size_t nEndIndex = aCID.rfind( '=' );
        if(nEndIndex == std::u16string_view::npos)
            return OBJECTTYPE_UNKNOWN;
        nLastSign = 0;
    }
    if( nLastSign>0 )
        nLastSign++;

    aCID = aCID.substr(nLastSign);
    if( o3tl::starts_with(aCID, u"Page") )
        eRet = OBJECTTYPE_PAGE;
    else if( o3tl::starts_with(aCID, u"Title") )
        eRet = OBJECTTYPE_TITLE;
    else if( o3tl::starts_with(aCID, u"LegendEntry") )
        eRet = OBJECTTYPE_LEGEND_ENTRY;
    else if( o3tl::starts_with(aCID, u"Legend") )
        eRet = OBJECTTYPE_LEGEND;
    else if( o3tl::starts_with(aCID, u"DiagramWall") )
        eRet = OBJECTTYPE_DIAGRAM_WALL;
    else if( o3tl::starts_with(aCID, u"DiagramFloor") )
        eRet = OBJECTTYPE_DIAGRAM_FLOOR;
    else if( o3tl::starts_with(aCID, u"D=") )
        eRet = OBJECTTYPE_DIAGRAM;
    else if( o3tl::starts_with(aCID, u"AxisUnitLabel") )
        eRet = OBJECTTYPE_AXIS_UNITLABEL;
    else if( o3tl::starts_with(aCID, u"Axis") )
        eRet = OBJECTTYPE_AXIS;
    else if( o3tl::starts_with(aCID, u"Grid") )
        eRet = OBJECTTYPE_GRID;
    else if( o3tl::starts_with(aCID, u"SubGrid") )
        eRet = OBJECTTYPE_SUBGRID;
    else if( o3tl::starts_with(aCID, u"Series") )
        eRet = OBJECTTYPE_DATA_SERIES;
    else if( o3tl::starts_with(aCID, u"Point") )
        eRet = OBJECTTYPE_DATA_POINT;
    else if( o3tl::starts_with(aCID, u"DataLabels") )
        eRet = OBJECTTYPE_DATA_LABELS;
    else if( o3tl::starts_with(aCID, u"DataLabel") )
        eRet = OBJECTTYPE_DATA_LABEL;
    else if( o3tl::starts_with(aCID, u"ErrorsX") )
        eRet = OBJECTTYPE_DATA_ERRORS_X;
    else if( o3tl::starts_with(aCID, u"ErrorsY") )
        eRet = OBJECTTYPE_DATA_ERRORS_Y;
    else if( o3tl::starts_with(aCID, u"ErrorsZ") )
        eRet = OBJECTTYPE_DATA_ERRORS_Z;
    else if( o3tl::starts_with(aCID, u"Curve") )
        eRet = OBJECTTYPE_DATA_CURVE;
    else if( o3tl::starts_with(aCID, u"Equation") )
        eRet = OBJECTTYPE_DATA_CURVE_EQUATION;
    else if( o3tl::starts_with(aCID, u"Average") )
        eRet = OBJECTTYPE_DATA_AVERAGE_LINE;
    else if( o3tl::starts_with(aCID, u"StockRange") )
        eRet = OBJECTTYPE_DATA_STOCK_RANGE;
    else if( o3tl::starts_with(aCID, u"StockLoss") )
        eRet = OBJECTTYPE_DATA_STOCK_LOSS;
    else if( o3tl::starts_with(aCID, u"StockGain") )
        eRet = OBJECTTYPE_DATA_STOCK_GAIN;
    else if( o3tl::starts_with(aCID, u"DataTable") )
        eRet = OBJECTTYPE_DATA_TABLE;
    else
        eRet = OBJECTTYPE_UNKNOWN;

    return eRet;
}

ObjectType ObjectIdentifier::getObjectType() const
{
    ObjectType eObjectType( OBJECTTYPE_UNKNOWN );
    if ( isAutoGeneratedObject() )
    {
        eObjectType = getObjectType( m_aObjectCID );
    }
    else if ( isAdditionalShape() )
    {
        eObjectType = OBJECTTYPE_SHAPE;
    }
    return eObjectType;
}

OUString ObjectIdentifier::createDataCurveCID(
                                std::u16string_view rSeriesParticle
                                , sal_Int32 nCurveIndex
                                , bool bAverageLine )
{
    OUString aParticleID( OUString::number( nCurveIndex ) );
    ObjectType eType = bAverageLine ? OBJECTTYPE_DATA_AVERAGE_LINE : OBJECTTYPE_DATA_CURVE;
    return createClassifiedIdentifierWithParent( eType, aParticleID, rSeriesParticle );
}

OUString ObjectIdentifier::createDataCurveEquationCID(
                                std::u16string_view rSeriesParticle
                                , sal_Int32 nCurveIndex )
{
    OUString aParticleID( OUString::number( nCurveIndex ) );
    return createClassifiedIdentifierWithParent( OBJECTTYPE_DATA_CURVE_EQUATION, aParticleID, rSeriesParticle );
}

OUString ObjectIdentifier::addChildParticle( std::u16string_view rParticle, std::u16string_view rChildParticle )
{
    OUStringBuffer aRet(rParticle);

    if( !aRet.isEmpty() && !rChildParticle.empty() )
        aRet.append(":");
    if( !rChildParticle.empty() )
        aRet.append(rChildParticle);

    return aRet.makeStringAndClear();
}

OUString ObjectIdentifier::createChildParticleWithIndex( ObjectType eObjectType, sal_Int32 nIndex )
{
    OUStringBuffer aRet( getStringForType( eObjectType ) );
    if( !aRet.isEmpty() )
    {
        aRet.append("=" + OUString::number(nIndex));
    }
    return aRet.makeStringAndClear();
}

sal_Int32 ObjectIdentifier::getIndexFromParticleOrCID( std::u16string_view rParticleOrCID )
{
    const std::u16string_view aIndexString = lcl_getIndexStringAfterString( rParticleOrCID, u"=" );
    return lcl_StringToIndex( o3tl::getToken(aIndexString, 0, ',' ) );
}

OUString ObjectIdentifier::createSeriesSubObjectStub( ObjectType eSubObjectType
                    , std::u16string_view rSeriesParticle
                    , std::u16string_view rDragMethodServiceName
                    , std::u16string_view rDragParameterString )
{
    OUString aChildParticle = getStringForType( eSubObjectType ) + "=";

    return createClassifiedIdentifierForParticles(
            rSeriesParticle, aChildParticle
          , rDragMethodServiceName, rDragParameterString );
}

OUString ObjectIdentifier::createPointCID( std::u16string_view rPointCID_Stub, sal_Int32 nIndex  )
{
    return rPointCID_Stub + OUString::number( nIndex );
}

std::u16string_view ObjectIdentifier::getParticleID( std::u16string_view rCID )
{
    std::u16string_view aRet;
    size_t nLast = rCID.rfind('=');
    if(nLast != std::u16string_view::npos)
        aRet = rCID.substr(++nLast);
    return aRet;
}

std::u16string_view ObjectIdentifier::getFullParentParticle( std::u16string_view rCID )
{
    std::u16string_view aRet;

    size_t nStartPos = rCID.rfind('/');
    if( nStartPos != std::u16string_view::npos )
    {
        nStartPos++;
        size_t nEndPos = rCID.rfind(':');
        if( nEndPos != std::u16string_view::npos && nStartPos < nEndPos )
        {
            aRet = rCID.substr(nStartPos,nEndPos-nStartPos);
        }
    }

    return aRet;
}

OUString ObjectIdentifier::getObjectID( std::u16string_view rCID )
{
    OUString aRet;

    size_t nStartPos = rCID.rfind('/');
    if( nStartPos != std::u16string_view::npos )
    {
        nStartPos++;
        size_t nEndPos = rCID.size();
        aRet = rCID.substr(nStartPos,nEndPos-nStartPos);
    }

    return aRet;
}

bool ObjectIdentifier::isCID( std::u16string_view rName )
{
    return !rName.empty() && o3tl::starts_with( rName, m_aProtocol );
}

Reference< beans::XPropertySet > ObjectIdentifier::getObjectPropertySet(
                std::u16string_view rObjectCID
                , const rtl::Reference<::chart::ChartModel>& xChartModel )
{
    //return the model object that is indicated by rObjectCID
    if(rObjectCID.empty())
        return nullptr;
    if(!xChartModel.is())
        return nullptr;

    Reference< beans::XPropertySet > xObjectProperties;
    try
    {
        ObjectType eObjectType = ObjectIdentifier::getObjectType( rObjectCID );
        std::u16string_view aParticleID = ObjectIdentifier::getParticleID( rObjectCID );

        rtl::Reference< Diagram > xDiagram;
        rtl::Reference< BaseCoordinateSystem > xCooSys;
        lcl_getDiagramAndCooSys( rObjectCID, xChartModel, xDiagram, xCooSys );

        switch(eObjectType)
        {
            case OBJECTTYPE_PAGE:
                {
                    xObjectProperties.set( xChartModel->getPageBackground() );
                }
                break;
            case OBJECTTYPE_TITLE:
                {
                    TitleHelper::eTitleType aTitleType = getTitleTypeForCID( rObjectCID );
                    rtl::Reference< Title > xTitle( TitleHelper::getTitle( aTitleType, xChartModel ) );
                    xObjectProperties = xTitle;
                }
                break;
            case OBJECTTYPE_LEGEND:
                {
                    if( xDiagram.is() )
                        xObjectProperties.set( xDiagram->getLegend(), uno::UNO_QUERY );
                }
                break;
            case OBJECTTYPE_LEGEND_ENTRY:
                    break;
            case OBJECTTYPE_DIAGRAM:
                {
                    xObjectProperties = xDiagram;
                }
                break;
            case OBJECTTYPE_DIAGRAM_WALL:
                {
                    if( xDiagram.is() )
                        xObjectProperties.set( xDiagram->getWall() );
                }
                break;
            case OBJECTTYPE_DIAGRAM_FLOOR:
                {
                    if( xDiagram.is() )
                        xObjectProperties.set( xDiagram->getFloor() );
                }
                break;
            case OBJECTTYPE_AXIS:
                {
                    sal_Int32 nDimensionIndex = -1;
                    sal_Int32 nAxisIndex = -1;
                    lcl_parseAxisIndices( nDimensionIndex, nAxisIndex, rObjectCID );

                    rtl::Reference< Axis > xAxis =
                        AxisHelper::getAxis( nDimensionIndex, nAxisIndex, xCooSys );
                    if( xAxis.is() )
                        xObjectProperties = xAxis;
                }
                break;
            case OBJECTTYPE_AXIS_UNITLABEL:
                    break;
            case OBJECTTYPE_GRID:
            case OBJECTTYPE_SUBGRID:
                {
                    sal_Int32 nDimensionIndex = -1;
                    sal_Int32 nAxisIndex = -1;
                    lcl_parseAxisIndices( nDimensionIndex, nAxisIndex, rObjectCID );

                    sal_Int32 nSubGridIndex = -1;
                    lcl_parseGridIndices( nSubGridIndex, rObjectCID );

                    xObjectProperties = AxisHelper::getGridProperties( xCooSys , nDimensionIndex, nAxisIndex, nSubGridIndex );
                }
                break;
            case OBJECTTYPE_DATA_LABELS:
            case OBJECTTYPE_DATA_SERIES:
                {
                    rtl::Reference< DataSeries > xSeries( ObjectIdentifier::getDataSeriesForCID(
                        rObjectCID, xChartModel ) );
                    if( xSeries.is() )
                        xObjectProperties = xSeries;

                    break;
                }
            case OBJECTTYPE_DATA_LABEL:
            case OBJECTTYPE_DATA_POINT:
                {
                    rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID(
                        rObjectCID, xChartModel );
                    if(xSeries.is())
                    {
                        sal_Int32 nIndex = o3tl::toInt32(aParticleID);
                        xObjectProperties = xSeries->getDataPointByIndex( nIndex );
                    }
                    break;
                }
            case OBJECTTYPE_DATA_ERRORS_X:
            case OBJECTTYPE_DATA_ERRORS_Y:
            case OBJECTTYPE_DATA_ERRORS_Z:
                {
                    rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID(
                        rObjectCID, xChartModel );
                    if(xSeries.is())
                    {
                        Reference< beans::XPropertySet > xErrorBarProp;
                        OUString errorBar;

                        if ( eObjectType == OBJECTTYPE_DATA_ERRORS_X)
                            errorBar = CHART_UNONAME_ERRORBAR_X;
                        else if (eObjectType == OBJECTTYPE_DATA_ERRORS_Y)
                            errorBar = CHART_UNONAME_ERRORBAR_Y;
                        else
                            errorBar = "ErrorBarZ";

                        xSeries->getPropertyValue( errorBar ) >>= xErrorBarProp;
                        xObjectProperties = xErrorBarProp;
                    }
                    break;
                }
            case OBJECTTYPE_DATA_AVERAGE_LINE:
            case OBJECTTYPE_DATA_CURVE:
            case OBJECTTYPE_DATA_CURVE_EQUATION:
                {
                    rtl::Reference< DataSeries > xRegressionContainer = ObjectIdentifier::getDataSeriesForCID(
                        rObjectCID, xChartModel );
                    if(xRegressionContainer.is())
                    {
                        sal_Int32 nIndex = o3tl::toInt32(aParticleID);
                        const std::vector< rtl::Reference< RegressionCurveModel > > & aCurveList =
                            xRegressionContainer->getRegressionCurves2();
                        if( nIndex >= 0 && o3tl::make_unsigned(nIndex) < aCurveList.size() )
                        {
                            if( eObjectType == OBJECTTYPE_DATA_CURVE_EQUATION )
                                xObjectProperties = aCurveList[nIndex]->getEquationProperties();
                            else
                                xObjectProperties = aCurveList[nIndex];
                        }
                    }
                    break;
                }
            case OBJECTTYPE_DATA_STOCK_RANGE:
                    break;
            case OBJECTTYPE_DATA_STOCK_LOSS:
                    {
                        rtl::Reference<ChartType> xChartType( lcl_getFirstStockChartType( xChartModel ) );
                        if(xChartType.is())
                            xChartType->getPropertyValue( "BlackDay" ) >>= xObjectProperties;
                    }
                    break;
            case OBJECTTYPE_DATA_STOCK_GAIN:
                    {
                        rtl::Reference<ChartType> xChartType( lcl_getFirstStockChartType( xChartModel ) );
                        if(xChartType.is())
                            xChartType->getPropertyValue( "WhiteDay" ) >>= xObjectProperties;
                    }
                    break;
            case OBJECTTYPE_DATA_TABLE:
                    {
                        if (xDiagram.is())
                            xObjectProperties.set(xDiagram->getDataTable(), uno::UNO_QUERY);
                    }
                break;
                    break;
            default: //OBJECTTYPE_UNKNOWN
                    break;
        }
    }
    catch(const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("chart2");
    }
    return xObjectProperties;
}

rtl::Reference< Axis > ObjectIdentifier::getAxisForCID(
                std::u16string_view rObjectCID
                , const rtl::Reference<::chart::ChartModel>& xChartModel )
{
    rtl::Reference< Diagram > xDiagram;
    rtl::Reference< BaseCoordinateSystem > xCooSys;
    lcl_getDiagramAndCooSys( rObjectCID, xChartModel, xDiagram, xCooSys );

    sal_Int32 nDimensionIndex = -1;
    sal_Int32 nAxisIndex = -1;
    lcl_parseAxisIndices( nDimensionIndex, nAxisIndex, rObjectCID );

    return AxisHelper::getAxis( nDimensionIndex, nAxisIndex, xCooSys );
}

rtl::Reference< DataSeries > ObjectIdentifier::getDataSeriesForCID(
                std::u16string_view rObjectCID
                , const rtl::Reference<::chart::ChartModel>& xChartModel )
{
    rtl::Reference< Diagram > xDiagram;
    rtl::Reference< BaseCoordinateSystem > xCooSys;
    lcl_getDiagramAndCooSys( rObjectCID, xChartModel, xDiagram, xCooSys );

    sal_Int32 nChartTypeIndex = -1;
    sal_Int32 nSeriesIndex = -1;
    sal_Int32 nPointIndex = -1;
    lcl_parseSeriesIndices( nChartTypeIndex, nSeriesIndex, nPointIndex, rObjectCID );

    rtl::Reference< DataSeries > xSeries;
    if (xDiagram)
    {
        rtl::Reference< ChartType > xDataSeriesContainer( xDiagram->getChartTypeByIndex( nChartTypeIndex ) );
        if( xDataSeriesContainer.is() )
        {
            const std::vector< rtl::Reference< DataSeries > > & aDataSeriesSeq( xDataSeriesContainer->getDataSeries2() );
            if( nSeriesIndex >= 0 && o3tl::make_unsigned(nSeriesIndex) < aDataSeriesSeq.size() )
                xSeries = aDataSeriesSeq[nSeriesIndex];
        }
    }
    return xSeries;
}

rtl::Reference< Diagram > ObjectIdentifier::getDiagramForCID(
                  std::u16string_view rObjectCID
                , const rtl::Reference<::chart::ChartModel>& xChartModel )
{
    rtl::Reference< Diagram > xDiagram;
    rtl::Reference< BaseCoordinateSystem > xCooSys;
    lcl_getDiagramAndCooSys( rObjectCID, xChartModel, xDiagram, xCooSys );

    return xDiagram;
}

TitleHelper::eTitleType ObjectIdentifier::getTitleTypeForCID( std::u16string_view rCID )
{
    TitleHelper::eTitleType eRet( TitleHelper::MAIN_TITLE );

    std::u16string_view aParentParticle = ObjectIdentifier::getFullParentParticle( rCID );
    const tTitleMap& rMap = lcl_getTitleMap();
    tTitleMap::const_iterator aIt = std::find_if(rMap.begin(), rMap.end(),
        [&aParentParticle](tTitleMap::const_reference rEntry) { return aParentParticle == rEntry.second; });
    if (aIt != rMap.end())
        eRet = (*aIt).first;

    return eRet;
}

OUString ObjectIdentifier::getSeriesParticleFromCID( std::u16string_view rCID )
{
    sal_Int32 nDiagramIndex = -1;
    sal_Int32 nCooSysIndex = -1;
    lcl_parseCooSysIndices( nDiagramIndex, nCooSysIndex, rCID );

    sal_Int32 nChartTypeIndex = -1;
    sal_Int32 nSeriesIndex = -1;
    sal_Int32 nPointIndex = -1;
    lcl_parseSeriesIndices( nChartTypeIndex, nSeriesIndex, nPointIndex, rCID );

    return ObjectIdentifier::createParticleForSeries( nDiagramIndex, nCooSysIndex, nChartTypeIndex, nSeriesIndex );
}

OUString ObjectIdentifier::getMovedSeriesCID( std::u16string_view rObjectCID, bool bForward )
{
    sal_Int32 nDiagramIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rObjectCID, u"CID/D=" ) );
    sal_Int32 nCooSysIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rObjectCID, u"CS=" ) );
    sal_Int32 nChartTypeIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rObjectCID, u"CT=" ) );
    sal_Int32 nSeriesIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rObjectCID, u"Series=" ) );

    if( bForward )
        nSeriesIndex--;
    else
        nSeriesIndex++;

    OUString aRet = ObjectIdentifier::createParticleForSeries( nDiagramIndex, nCooSysIndex, nChartTypeIndex, nSeriesIndex );
    return ObjectIdentifier::createClassifiedIdentifierForParticle( aRet );
}

bool ObjectIdentifier::isValid() const
{
    return ( isAutoGeneratedObject() || isAdditionalShape() );
}

bool ObjectIdentifier::isAutoGeneratedObject() const
{
    return ( !m_aObjectCID.isEmpty() );
}

bool ObjectIdentifier::isAdditionalShape() const
{
    return m_xAdditionalShape.is();
}

Any ObjectIdentifier::getAny() const
{
    Any aAny;
    if ( isAutoGeneratedObject() )
    {
        aAny <<= getObjectCID();
    }
    else if ( isAdditionalShape() )
    {
        aAny <<= getAdditionalShape();
    }
    return aAny;
}

} //namespace chart

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