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

#include <memory>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/chart/ChartDataRowSource.hpp>
#include <com/sun/star/chart2/data/XDataReceiver.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>

#include <tools/gen.hxx>
#include <svx/svdoole2.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdundo.hxx>
#include <unotools/moduleoptions.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/classids.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <tools/globname.hxx>
#include <svtools/embedhlp.hxx>
#include <comphelper/sequence.hxx>
#include <vcl/svapp.hxx>

#include <TablePivotChart.hxx>
#include <TablePivotCharts.hxx>
#include <PivotTableDataProvider.hxx>
#include <ChartTools.hxx>

#include <miscuno.hxx>
#include <docsh.hxx>
#include <drwlayer.hxx>

using namespace css;

namespace sc
{

SC_SIMPLE_SERVICE_INFO(TablePivotCharts, u"TablePivotCharts"_ustr, u"com.sun.star.table.TablePivotCharts"_ustr)

TablePivotCharts::TablePivotCharts(ScDocShell* pDocShell, SCTAB nTab)
    : m_pDocShell(pDocShell)
    , m_nTab(nTab)
{
    m_pDocShell->GetDocument().AddUnoObject(*this);
}

TablePivotCharts::~TablePivotCharts()
{
    SolarMutexGuard aGuard;

    if (m_pDocShell)
        m_pDocShell->GetDocument().RemoveUnoObject(*this);
}

void TablePivotCharts::Notify(SfxBroadcaster& /*rBroadcaster*/, const SfxHint& rHint)
{
    if (rHint.GetId() == SfxHintId::Dying)
        m_pDocShell = nullptr;
}

// XTablePivotCharts
void SAL_CALL TablePivotCharts::addNewByName(OUString const & rName,
                                             const awt::Rectangle& aRect,
                                             OUString const & rDataPilotName)
{
    SolarMutexGuard aGuard;

    if (!m_pDocShell)
        return;

    ScDocument& rDoc = m_pDocShell->GetDocument();
    ScDrawLayer* pModel = m_pDocShell->MakeDrawLayer();
    SdrPage* pPage = pModel->GetPage(sal_uInt16(m_nTab));
    if (!pPage)
        return;

    //  chart can't be inserted if any ole object with that name exists on any table
    //  (empty string: generate valid name)

    OUString aName = rName;
    SCTAB nDummy;
    if (!aName.isEmpty() && pModel->GetNamedObject(aName, SdrObjKind::OLE2, nDummy))
    {
        //  object exists - only RuntimeException is specified
        throw uno::RuntimeException();
    }

    uno::Reference<embed::XEmbeddedObject> xObject;

    if (SvtModuleOptions().IsChartInstalled())
        xObject = m_pDocShell->GetEmbeddedObjectContainer().CreateEmbeddedObject(SvGlobalName(SO3_SCH_CLASSID).GetByteSequence(), aName);

    if (!xObject.is())
            return;

    Point aRectPos(aRect.X, aRect.Y);
    bool bLayoutRTL = rDoc.IsLayoutRTL(m_nTab);
    if ((aRectPos.X() < 0 && !bLayoutRTL) || (aRectPos.X() > 0 && bLayoutRTL))
        aRectPos.setX( 0 );

    if (aRectPos.Y() < 0)
         aRectPos.setY( 0 );

    Size aRectSize(aRect.Width, aRect.Height);
    if (aRectSize.Width() <= 0)
        aRectSize.setWidth( 5000 ); // default size

    if (aRectSize.Height() <= 0)
        aRectSize.setHeight( 5000 );

    ::tools::Rectangle aInsRect(aRectPos, aRectSize);

    sal_Int64 nAspect(embed::Aspects::MSOLE_CONTENT);
    MapUnit aMapUnit(VCLUnoHelper::UnoEmbed2VCLMapUnit(xObject->getMapUnit(nAspect)));
    Size aSize(aInsRect.GetSize());
    aSize = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(aMapUnit));
    awt::Size aAwtSize;
    aAwtSize.Width = aSize.Width();
    aAwtSize.Height = aSize.Height();

    rtl::Reference<sc::PivotTableDataProvider> pPivotTableDataProvider(new sc::PivotTableDataProvider(rDoc));
    pPivotTableDataProvider->setPivotTableName(rDataPilotName);

    uno::Reference<chart2::data::XDataReceiver> xReceiver;

    if (xObject.is())
        xReceiver.set(xObject->getComponent(), uno::UNO_QUERY);

    if (xReceiver.is())
    {
        xReceiver->attachDataProvider(uno::Reference<chart2::data::XDataProvider>(pPivotTableDataProvider));

        uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(cppu::getXWeak(m_pDocShell->GetModel()), uno::UNO_QUERY);
        xReceiver->attachNumberFormatsSupplier(xNumberFormatsSupplier);

        cpo::uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
                    { "CellRangeRepresentation", cpo::uno::Any(rDataPilotName) },
                    { "HasCategories", cpo::uno::Any(true) },
                    { "DataRowSource", cpo::uno::Any(chart::ChartDataRowSource_COLUMNS) }
                }));
        xReceiver->setArguments(aArgs);
    }

    rtl::Reference<SdrOle2Obj> pObject = new SdrOle2Obj(
            *pModel,
            svt::EmbeddedObjectRef(xObject, embed::Aspects::MSOLE_CONTENT),
            aName,
            aInsRect);

    if (xObject.is())
        xObject->setVisualAreaSize(nAspect, aAwtSize);

    pPage->InsertObject(pObject.get());
    pModel->AddUndo(std::make_unique<SdrUndoInsertObj>(*pObject));
}

void SAL_CALL TablePivotCharts::removeByName(const OUString& rName)
{
    SolarMutexGuard aGuard;
    SdrOle2Obj* pObject = sctools::findChartsByName(m_pDocShell, m_nTab, rName, sctools::ChartSourceType::PIVOT_TABLE);
    if (pObject)
    {
        ScDocument& rDoc = m_pDocShell->GetDocument();
        ScDrawLayer* pModel = rDoc.GetDrawLayer();
        SdrPage* pPage = pModel->GetPage(sal_uInt16(m_nTab));
        pModel->AddUndo(std::make_unique<SdrUndoDelObj>(*pObject));
        pPage->RemoveObject(pObject->GetOrdNum());
    }
}

// XIndexAccess
sal_Int32 SAL_CALL TablePivotCharts::getCount()
{
    SolarMutexGuard aGuard;
    sal_Int32 nCount = 0;

    if (!m_pDocShell)
        return nCount;

    sctools::ChartIterator aIterator(m_pDocShell, m_nTab, sctools::ChartSourceType::PIVOT_TABLE);

    SdrOle2Obj* pOleObject = aIterator.next();
    while (pOleObject)
    {
        if (pOleObject->GetObjRef().is())
            nCount++;
        pOleObject = aIterator.next();
    }
    return nCount;
}

cpo::uno::Any SAL_CALL TablePivotCharts::getByIndex(sal_Int32 nIndex)
{
    SolarMutexGuard aGuard;
    SdrOle2Obj* pObject = sctools::getChartByIndex(m_pDocShell, m_nTab, nIndex,
                                                     sctools::ChartSourceType::PIVOT_TABLE);
    if (!pObject)
        throw lang::IndexOutOfBoundsException();

    OUString aName;
    uno::Reference<embed::XEmbeddedObject> xObject = pObject->GetObjRef();
    if (xObject.is())
        aName = m_pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName(xObject);

    if (aName.isEmpty())
        throw lang::IndexOutOfBoundsException();

    uno::Reference<table::XTablePivotChart> xChart(new TablePivotChart(m_pDocShell, m_nTab, aName));
    if (!xChart.is())
        throw lang::IndexOutOfBoundsException();

    return cpo::uno::Any(xChart);
}

cpo::uno::Type SAL_CALL TablePivotCharts::getElementType()
{
    return cppu::UnoType<table::XTablePivotChart>::get();
}

bool SAL_CALL TablePivotCharts::hasElements()
{
    SolarMutexGuard aGuard;
    return getCount() != 0;
}

cpo::uno::Any SAL_CALL TablePivotCharts::getByName(OUString const & rName)
{
    SolarMutexGuard aGuard;

    if (!sctools::findChartsByName(m_pDocShell, m_nTab, rName, sctools::ChartSourceType::PIVOT_TABLE))
        throw container::NoSuchElementException();

    uno::Reference<table::XTablePivotChart> xChart(new TablePivotChart(m_pDocShell, m_nTab, rName));
    if (!xChart.is())
        throw container::NoSuchElementException();

    return cpo::uno::Any(xChart);
}

cpo::uno::Sequence<OUString> SAL_CALL TablePivotCharts::getElementNames()
{
    SolarMutexGuard aGuard;

    std::vector<OUString> aElements;
    sctools::ChartIterator aIterator(m_pDocShell, m_nTab, sctools::ChartSourceType::PIVOT_TABLE);

    SdrOle2Obj* pOleObject = aIterator.next();
    while (pOleObject)
    {
        uno::Reference<embed::XEmbeddedObject> xObject = pOleObject->GetObjRef();
        if (xObject.is())
        {
            OUString aName = m_pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName(xObject);
            aElements.push_back(aName);
        }
        pOleObject = aIterator.next();
    }
    return comphelper::containerToSequence(aElements);
}

bool SAL_CALL TablePivotCharts::hasByName(OUString const & rName)
{
    SolarMutexGuard aGuard;

    return sctools::findChartsByName(m_pDocShell, m_nTab, rName, sctools::ChartSourceType::PIVOT_TABLE) != nullptr;
}

} // end sc namespace

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