/* -*- 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/.
 */

#pragma once

#include <com/sun/star/geometry/RealPoint2D.hpp>
#include <com/sun/star/geometry/RealSize2D.hpp>
#include <com/sun/star/util/DateTime.hpp>

#include <svx/svdundo.hxx>
#include <svx/svxdllapi.h>

#include <com/sun/star/office/XAnnotation.hpp>
#include <comphelper/compbase.hxx>
#include <cppuhelper/propertysetmixin.hxx>
#include <svx/annotation/TextAPI.hxx>
#include <tools/UniqueID.hxx>
#include <vcl/bitmap.hxx>

class SdrUndoAction;
class SfxViewShell;
class SdrPage;

namespace sdr::annotation
{
class Annotation;

/** Type of the annotation / comment change. */
enum class CommentNotificationType
{
    Add,
    Modify,
    Remove
};

/** Kit notify for a view */
SVXCORE_DLLPUBLIC void KitCommentNotify(CommentNotificationType nType,
                                        const SfxViewShell* pViewShell, Annotation& rAnnotation);

/** Kit notify for all views */
SVXCORE_DLLPUBLIC void KitCommentNotifyAll(CommentNotificationType nType, Annotation& rAnnotation);

/** Type of the annotation (that is supported) */
enum class AnnotationType
{
    None,
    Square,
    Polygon,
    Circle,
    Ink,
    Highlight,
    Line,
    FreeText,
    Stamp
};

/** Annotation data that is used at annotation creation */
struct CreationInfo
{
    AnnotationType meType = AnnotationType::None;

    std::vector<basegfx::B2DPolygon> maPolygons;
    basegfx::B2DRectangle maRectangle;

    float mnWidth = 0.0f;

    bool mbFillColor = false;
    Color maFillColor = COL_TRANSPARENT;

    bool mbColor = false;
    Color maColor = COL_TRANSPARENT;

    Bitmap maBitmap;
};

/** Data of an annotation */
struct SVXCORE_DLLPUBLIC AnnotationData
{
    css::geometry::RealPoint2D m_Position;
    css::geometry::RealSize2D m_Size;
    OUString m_Author;
    OUString m_Initials;
    css::util::DateTime m_DateTime;
    OUString m_Text;

    /// Threaded-comment participation. Set by filters that support
    /// per-annotation reply/resolve (currently only the PDF filter).
    bool m_Threaded = false;
    /// Resolved / done flag. Only meaningful when m_Threaded is true.
    bool m_Resolved = false;
    /// GetId() of the parent annotation; 0 means "thread root".
    /// Only meaningful when m_Threaded is true.
    sal_uInt64 m_ParentId = 0;

    /// True when m_Size is the user/import-supplied anchor area; see
    /// Annotation::m_SizeExplicit.
    bool m_SizeExplicit = false;
};

/** Annotation object, responsible for handling of the annotation.
 *
 * Implements the XAnnotation UNO API, handles undo/redo and notifications ()
 *
 **/
class SVXCORE_DLLPUBLIC Annotation
    : public ::comphelper::WeakComponentImplHelper<css::office::XAnnotation>,
      public ::cppu::PropertySetMixin<css::office::XAnnotation>
{
private:
    css::uno::Reference<css::text::XText> getTextRangeImpl(const std::unique_lock<std::mutex>& g);
    OUString GetTextImpl(const std::unique_lock<std::mutex>& g);
    void SetTextImpl(OUString const& rText, const std::unique_lock<std::mutex>& g);

protected:
    SdrPage* mpPage;
    UniqueID maUniqueID;

    css::geometry::RealPoint2D m_Position;
    css::geometry::RealSize2D m_Size;
    OUString m_Author;
    OUString m_Initials;
    css::util::DateTime m_DateTime;
    rtl::Reference<sdr::annotation::TextApiObject> m_TextRange;

    CreationInfo maCreationInfo;

    // Threaded-comment metadata; see AnnotationData for semantics.
    bool m_Threaded = false;
    bool m_Resolved = false;
    sal_uInt64 m_ParentId = 0;

    // True when m_Size carries the user/import-supplied anchor area (e.g.
    // PDF /Rect or the Width/Height args of .uno:InsertAnnotation) and
    // must not be overwritten by AnnotationObject's auto-grown text-frame
    // size. Default false preserves tdf#161911.
    bool m_SizeExplicit = false;

    std::unique_ptr<SdrUndoAction> createUndoAnnotation();

public:
    Annotation(const css::uno::Reference<css::uno::XComponentContext>& context, SdrPage* pPage);
    Annotation(const Annotation&) = delete;
    Annotation& operator=(const Annotation&) = delete;

    // XInterface:
    virtual cpo::uno::Any SAL_CALL queryInterface(cpo::uno::Type const& type) override;
    virtual void SAL_CALL acquire() noexcept override
    {
        comphelper::WeakComponentImplHelper<css::office::XAnnotation>::acquire();
    }
    virtual void SAL_CALL release() noexcept override
    {
        comphelper::WeakComponentImplHelper<css::office::XAnnotation>::release();
    }

    // Changes without triggering notification broadcast
    void SetPosition(const css::geometry::RealPoint2D& rValue);
    void SetSize(const css::geometry::RealSize2D& rValue);

    virtual css::uno::Reference<css::text::XText> SAL_CALL getTextRange() override;

    // override WeakComponentImplHelperBase::disposing()
    // This function is called upon disposing the component,
    // if your component needs special work when it becomes
    // disposed, do it here.
    virtual void disposing(std::unique_lock<std::mutex>& rGuard) override;

    OUString GetText();

    OString ToJSON(CommentNotificationType nType);
    void toData(AnnotationData& rData);
    void fromData(const AnnotationData& rData);

    const rtl::Reference<sdr::annotation::TextApiObject>& getTextApiObject() { return m_TextRange; }

    SdrModel* GetModel() const;
    SdrPage const* getPage() const { return mpPage; }
    SdrPage* getPage() { return mpPage; }

    // Unique ID of the annotation
    sal_uInt64 GetId() const { return maUniqueID.getID(); }

    CreationInfo const& getCreationInfo() { return maCreationInfo; }
    void setCreationInfo(CreationInfo const& rCreationInfo) { maCreationInfo = rCreationInfo; }

    /// Returns true if this annotation participates in the threaded-comments
    /// feature (currently set only by the PDF filter).
    bool IsThreaded() const { return m_Threaded; }
    void SetThreaded(bool bThreaded);

    /// Returns the resolved/done flag. Only meaningful if IsThreaded().
    bool IsResolved() const { return m_Resolved; }
    void SetResolved(bool bResolved);

    /// GetId() of the parent annotation in the thread; 0 means "root".
    /// Only meaningful if IsThreaded().
    sal_uInt64 GetParentId() const { return m_ParentId; }
    void SetParentId(sal_uInt64 nParentId);

    /// True when m_Size was set explicitly to the comment's anchor area
    /// and AnnotationObject's auto-grow back-sync should leave it alone.
    bool IsSizeExplicit() const { return m_SizeExplicit; }
    void SetSizeExplicit(bool bExplicit) { m_SizeExplicit = bExplicit; }

    SdrObject* findAnnotationObject();

    virtual rtl::Reference<Annotation> clone(SdrPage* pTargetPage) = 0;
};

/** Vector of annotations */
typedef std::vector<rtl::Reference<Annotation>> AnnotationVector;

} // namespace sdr::annotation

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