/***************************************************************************
 *   Copyright (C) 2006 by Pino Toscano <pino@kde.org>                     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#include "annotationwidgets.h"

// qt/kde includes
#include <qcheckbox.h>
#include <qgroupbox.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qspinbox.h>
#include <qvariant.h>
#include <kcolorbutton.h>
#include <kcombobox.h>
#include <kfontrequester.h>
#include <KMessageBox>
#include <KMessageWidget>
#include <QIcon>
#include <QFileDialog>
#include <kiconloader.h>
#include <KLocalizedString>
#include <QDebug>
#include <QGuiApplication>
#include <QList>
#include <QMimeDatabase>
#include <QPair>
#include <QSize>
#include <KFormat>
#include <QFormLayout>

#include "core/document.h"
#include "guiutils.h"
#include "pagepainter.h"

#define FILEATTACH_ICONSIZE 48

PixmapPreviewSelector::PixmapPreviewSelector( QWidget * parent, PreviewPosition position )
  : QWidget( parent ), m_previewPosition( position )
{
    QVBoxLayout * mainlay = new QVBoxLayout( this );
    mainlay->setContentsMargins( 0, 0, 0, 0 );
    QHBoxLayout * toplay = new QHBoxLayout( this );
    toplay->setContentsMargins( 0, 0, 0, 0 );
    mainlay->addLayout( toplay );
    m_comboItems = new KComboBox( this );
    toplay->addWidget( m_comboItems );
    m_stampPushButton = new QPushButton(QIcon::fromTheme( QStringLiteral("document-open") ), QString(), this );
    m_stampPushButton->setVisible( false );
    m_stampPushButton->setToolTip( i18nc( "@info:tooltip", "Select a custom stamp symbol from file") );
    toplay->addWidget(m_stampPushButton);
    m_iconLabel = new QLabel( this );
    switch ( m_previewPosition )
    {
        case Side:
            toplay->addWidget( m_iconLabel );
            break;
        case Below:
            mainlay->addWidget( m_iconLabel );
            mainlay->setAlignment( m_iconLabel, Qt::AlignHCenter );
            break;
    }
    m_iconLabel->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
    m_iconLabel->setAlignment( Qt::AlignCenter );
    m_iconLabel->setFrameStyle( QFrame::StyledPanel );
    setPreviewSize( 32 );

    setFocusPolicy( Qt::TabFocus );
    setFocusProxy( m_comboItems );

    connect( m_comboItems, QOverload<const QString &>::of(&QComboBox::currentIndexChanged), this, &PixmapPreviewSelector::iconComboChanged );
    connect( m_comboItems, &QComboBox::editTextChanged, this, &PixmapPreviewSelector::iconComboChanged );
    connect( m_stampPushButton, &QPushButton::clicked, this, &PixmapPreviewSelector::selectCustomStamp );
}

PixmapPreviewSelector::~PixmapPreviewSelector()
{
}

void PixmapPreviewSelector::setIcon( const QString& icon )
{
    int id = m_comboItems->findData( QVariant( icon ), Qt::UserRole, Qt::MatchFixedString );
    if ( id == -1 )
        id = m_comboItems->findText( icon, Qt::MatchFixedString );
    if ( id > -1 )
    {
        m_comboItems->setCurrentIndex( id );
    }
    else if ( m_comboItems->isEditable() )
    {
        m_comboItems->addItem( icon, QVariant( icon ) );
        m_comboItems->setCurrentIndex( m_comboItems->findText( icon, Qt::MatchFixedString ) );
    }
}

QString PixmapPreviewSelector::icon() const
{
    return m_icon;
}

void PixmapPreviewSelector::addItem( const QString& item, const QString& id )
{
    m_comboItems->addItem( item, QVariant( id ) );
    setIcon( m_icon );
}

void PixmapPreviewSelector::setPreviewSize( int size )
{
    m_previewSize = size;
    switch( m_previewPosition )
    {
        case Side:
            m_iconLabel->setFixedSize( m_previewSize + 8, m_previewSize + 8 );
            break;
        case Below:
            m_iconLabel->setFixedSize( 3 * m_previewSize + 8, m_previewSize + 8 );
            break;
    }
    iconComboChanged( m_icon );
}

int PixmapPreviewSelector::previewSize() const
{
    return m_previewSize;
}

void PixmapPreviewSelector::setEditable( bool editable )
{
    m_comboItems->setEditable( editable );
    m_stampPushButton->setVisible( editable );
}

void PixmapPreviewSelector::iconComboChanged( const QString& icon )
{
    int id = m_comboItems->findText( icon, Qt::MatchFixedString );
    if ( id >= 0 )
    {
        m_icon = m_comboItems->itemData( id ).toString();
    }
    else
    {
        m_icon = icon;
    }

    QPixmap pixmap = GuiUtils::loadStamp( m_icon, m_previewSize );
    const QRect cr = m_iconLabel->contentsRect();
    if ( pixmap.width() > cr.width() || pixmap.height() > cr.height() )
        pixmap = pixmap.scaled( cr.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
    m_iconLabel->setPixmap( pixmap );

    emit iconChanged( m_icon );
}

void PixmapPreviewSelector::selectCustomStamp()
{
    const QString customStampFile = QFileDialog::getOpenFileName(this, i18nc("@title:window file chooser", "Select custom stamp symbol"),
        QString(), i18n("*.ico *.png *.xpm *.svg *.svgz | Icon Files (*.ico *.png *.xpm *.svg *.svgz)") );
    if ( !customStampFile.isEmpty() )
    {
        QPixmap pixmap = GuiUtils::loadStamp( customStampFile, m_previewSize );
        if( pixmap.isNull() ) {
            KMessageBox::sorry( this,
                                xi18nc("@info", "Could not load the file <filename>%1</filename>", customStampFile ),
                                i18nc("@title:window", "Invalid file")
                              );
        } else {
            m_comboItems->setEditText(customStampFile);
        }
    }
}

AnnotationWidget * AnnotationWidgetFactory::widgetFor( Okular::Annotation * ann )
{
    switch ( ann->subType() )
    {
        case Okular::Annotation::AStamp:
            return new StampAnnotationWidget( ann );
            break;
        case Okular::Annotation::AText:
            return new TextAnnotationWidget( ann );
            break;
        case Okular::Annotation::ALine:
            return new LineAnnotationWidget( ann );
            break;
        case Okular::Annotation::AHighlight:
            return new HighlightAnnotationWidget( ann );
            break;
        case Okular::Annotation::AInk:
            return new InkAnnotationWidget( ann );
            break;
        case Okular::Annotation::AGeom:
            return new GeomAnnotationWidget( ann );
            break;
        case Okular::Annotation::AFileAttachment:
            return new FileAttachmentAnnotationWidget( ann );
            break;
        case Okular::Annotation::ACaret:
            return new CaretAnnotationWidget( ann );
            break;
        // shut up gcc
        default:
            ;
    }
    // cases not covered yet: return a generic widget
    return new AnnotationWidget( ann );
}


AnnotationWidget::AnnotationWidget( Okular::Annotation * ann )
    : m_ann( ann )
{
}

AnnotationWidget::~AnnotationWidget()
{
}

Okular::Annotation::SubType AnnotationWidget::annotationType() const
{
    return m_ann->subType();
}

QWidget * AnnotationWidget::appearanceWidget()
{
    if ( m_appearanceWidget )
        return m_appearanceWidget;

    m_appearanceWidget = createAppearanceWidget();
    return m_appearanceWidget;
}

QWidget * AnnotationWidget::extraWidget()
{
    if ( m_extraWidget )
        return m_extraWidget;

    m_extraWidget = createExtraWidget();
    return m_extraWidget;
}

void AnnotationWidget::applyChanges()
{
    if (m_colorBn)
        m_ann->style().setColor( m_colorBn->color() );
    if (m_opacity)
        m_ann->style().setOpacity( (double)m_opacity->value() / 100.0 );
}

QWidget * AnnotationWidget::createAppearanceWidget()
{
    QWidget * widget = new QWidget();
    QFormLayout * formlayout = new QFormLayout( widget );
    formlayout->setLabelAlignment( Qt::AlignRight );
    formlayout->setFieldGrowthPolicy( QFormLayout::AllNonFixedFieldsGrow );

    createStyleWidget( formlayout );

    return widget;
}

void AnnotationWidget::createStyleWidget( QFormLayout * formlayout )
{
    Q_UNUSED( formlayout );
}

void AnnotationWidget::addColorButton( QWidget * widget, QFormLayout * formlayout )
{
    m_colorBn = new KColorButton( widget );
    m_colorBn->setColor( m_ann->style().color() );
    formlayout->addRow( i18n( "&Color:" ), m_colorBn );
    connect( m_colorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged );
}

void AnnotationWidget::addOpacitySpinBox( QWidget * widget, QFormLayout * formlayout )
{
    m_opacity = new QSpinBox( widget );
    m_opacity->setRange( 0, 100 );
    m_opacity->setValue( (int)( m_ann->style().opacity() * 100 ) );
    m_opacity->setSuffix( i18nc( "Suffix for the opacity level, eg '80 %'", " %" ) );
    formlayout->addRow( i18n( "&Opacity:" ), m_opacity);
    connect( m_opacity, QOverload<int>::of(&QSpinBox::valueChanged), this, &AnnotationWidget::dataChanged );
}

void AnnotationWidget::addVerticalSpacer( QFormLayout * formlayout )
{
    formlayout->addItem( new QSpacerItem( 0, 5, QSizePolicy::Fixed, QSizePolicy::Fixed ) );
}

QWidget * AnnotationWidget::createExtraWidget()
{
    return nullptr;
}


TextAnnotationWidget::TextAnnotationWidget( Okular::Annotation * ann )
    : AnnotationWidget( ann )
{
    m_textAnn = static_cast< Okular::TextAnnotation * >( ann );
}

void TextAnnotationWidget::createStyleWidget( QFormLayout * formlayout )
{
    QWidget * widget = qobject_cast<QWidget *>( formlayout->parent() );

    if ( m_textAnn->textType() == Okular::TextAnnotation::Linked )
    {
        createPopupNoteStyleUi( widget, formlayout );
    }
    else if ( m_textAnn->textType() == Okular::TextAnnotation::InPlace )
    {
        if ( isTypewriter() )
            createTypewriterStyleUi( widget, formlayout );
        else
            createInlineNoteStyleUi( widget, formlayout );
    }
}

void TextAnnotationWidget::applyChanges()
{
    AnnotationWidget::applyChanges();
    if ( m_textAnn->textType() == Okular::TextAnnotation::Linked )
    {
        Q_ASSERT( m_pixmapSelector );
        m_textAnn->setTextIcon( m_pixmapSelector->icon() );
    }
    else if ( m_textAnn->textType() == Okular::TextAnnotation::InPlace )
    {
        Q_ASSERT( m_fontReq );
        m_textAnn->setTextFont( m_fontReq->font() );
        if ( !isTypewriter() )
        {
            Q_ASSERT( m_textAlign && m_spinWidth );
            m_textAnn->setInplaceAlignment( m_textAlign->currentIndex() );
            m_textAnn->style().setWidth( m_spinWidth->value() );
        }
        else
        {
            Q_ASSERT( m_textColorBn );
            m_textAnn->setTextColor( m_textColorBn->color() );
        }
    }
}

void TextAnnotationWidget::createPopupNoteStyleUi( QWidget * widget, QFormLayout * formlayout  ) {
    addColorButton( widget, formlayout );
    addOpacitySpinBox( widget, formlayout );
    addVerticalSpacer( formlayout );
    addPixmapSelector( widget, formlayout );
}

void TextAnnotationWidget::createInlineNoteStyleUi( QWidget * widget, QFormLayout * formlayout  ) {
    addColorButton( widget, formlayout );
    addOpacitySpinBox( widget, formlayout );
    addVerticalSpacer( formlayout );
    addFontRequester( widget, formlayout );
    addTextAlignComboBox( widget, formlayout );
    addVerticalSpacer( formlayout );
    addWidthSpinBox( widget, formlayout );
}

void TextAnnotationWidget::createTypewriterStyleUi( QWidget * widget, QFormLayout * formlayout  ) {
    addFontRequester( widget, formlayout );
    addTextColorButton( widget, formlayout );
}

void TextAnnotationWidget::addPixmapSelector( QWidget * widget, QFormLayout * formlayout )
{
    m_pixmapSelector = new PixmapPreviewSelector( widget );
    formlayout->addRow( i18n( "Icon:" ), m_pixmapSelector );
    m_pixmapSelector->addItem( i18n( "Comment" ), QStringLiteral("Comment") );
    m_pixmapSelector->addItem( i18n( "Help" ), QStringLiteral("Help") );
    m_pixmapSelector->addItem( i18n( "Insert" ), QStringLiteral("Insert") );
    m_pixmapSelector->addItem( i18n( "Key" ), QStringLiteral("Key") );
    m_pixmapSelector->addItem( i18n( "New paragraph" ), QStringLiteral("NewParagraph") );
    m_pixmapSelector->addItem( i18n( "Note" ), QStringLiteral("Note") );
    m_pixmapSelector->addItem( i18n( "Paragraph" ), QStringLiteral("Paragraph") );
    m_pixmapSelector->setIcon( m_textAnn->textIcon() );
    connect( m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged );
}

void TextAnnotationWidget::addFontRequester( QWidget * widget, QFormLayout * formlayout )
{
    m_fontReq = new KFontRequester( widget );
    formlayout->addRow( i18n( "Font:" ), m_fontReq );
    m_fontReq->setFont( m_textAnn->textFont() );
    connect( m_fontReq, &KFontRequester::fontSelected, this, &AnnotationWidget::dataChanged );
}

void TextAnnotationWidget::addTextColorButton( QWidget * widget, QFormLayout * formlayout )
{
    m_textColorBn = new KColorButton( widget );
    m_textColorBn->setColor( m_textAnn->textColor() );
    formlayout->addRow( i18n( "Text &color:" ), m_textColorBn );
    connect( m_textColorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged );
}

void TextAnnotationWidget::addTextAlignComboBox( QWidget * widget, QFormLayout * formlayout )
{

    m_textAlign = new KComboBox( widget );
    formlayout->addRow( i18n( "&Align:" ), m_textAlign );
    m_textAlign->addItem( i18n("Left") );
    m_textAlign->addItem( i18n("Center") );
    m_textAlign->addItem( i18n("Right") );
    m_textAlign->setCurrentIndex( m_textAnn->inplaceAlignment() );
    connect( m_textAlign, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &AnnotationWidget::dataChanged );
}

void TextAnnotationWidget::addWidthSpinBox( QWidget * widget, QFormLayout * formlayout )
{
    m_spinWidth = new QDoubleSpinBox( widget );
    formlayout->addRow( i18n( "Border &width:" ), m_spinWidth );
    m_spinWidth->setRange( 0, 100 );
    m_spinWidth->setValue( m_textAnn->style().width() );
    m_spinWidth->setSingleStep( 0.1 );
    connect( m_spinWidth, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &AnnotationWidget::dataChanged );
}

StampAnnotationWidget::StampAnnotationWidget( Okular::Annotation * ann )
    : AnnotationWidget( ann ), m_pixmapSelector( nullptr )
{
    m_stampAnn = static_cast< Okular::StampAnnotation * >( ann );
}

void StampAnnotationWidget::createStyleWidget( QFormLayout * formlayout )
{
    QWidget * widget = qobject_cast<QWidget *>( formlayout->parent() );

    KMessageWidget * brokenStampSupportWarning = new KMessageWidget( widget );
    brokenStampSupportWarning->setText( xi18nc("@info", "<warning>experimental feature.<nl/>"
                                                        "Stamps inserted in PDF documents are not visible in PDF readers other than Okular.</warning>") );
    brokenStampSupportWarning->setMessageType( KMessageWidget::Warning );
    brokenStampSupportWarning->setWordWrap( true );
    brokenStampSupportWarning->setCloseButtonVisible( false );
    formlayout->insertRow( 0, brokenStampSupportWarning );

    addOpacitySpinBox( widget, formlayout );
    addVerticalSpacer( formlayout );

    m_pixmapSelector = new PixmapPreviewSelector( widget, PixmapPreviewSelector::Below );
    formlayout->addRow( i18n( "Stamp symbol:" ), m_pixmapSelector );
    m_pixmapSelector->setEditable( true );

    m_pixmapSelector->addItem( i18n( "Okular" ), QStringLiteral("okular") );
    m_pixmapSelector->addItem( i18n( "Bookmark" ), QStringLiteral("bookmarks") );
    m_pixmapSelector->addItem( i18n( "KDE" ), QStringLiteral("kde") );
    m_pixmapSelector->addItem( i18n( "Information" ), QStringLiteral("help-about") );
    m_pixmapSelector->addItem( i18n( "Approved" ), QStringLiteral("Approved") );
    m_pixmapSelector->addItem( i18n( "As Is" ), QStringLiteral("AsIs") );
    m_pixmapSelector->addItem( i18n( "Confidential" ), QStringLiteral("Confidential") );
    m_pixmapSelector->addItem( i18n( "Departmental" ), QStringLiteral("Departmental") );
    m_pixmapSelector->addItem( i18n( "Draft" ), QStringLiteral("Draft") );
    m_pixmapSelector->addItem( i18n( "Experimental" ), QStringLiteral("Experimental") );
    m_pixmapSelector->addItem( i18n( "Expired" ), QStringLiteral("Expired") );
    m_pixmapSelector->addItem( i18n( "Final" ), QStringLiteral("Final") );
    m_pixmapSelector->addItem( i18n( "For Comment" ), QStringLiteral("ForComment") );
    m_pixmapSelector->addItem( i18n( "For Public Release" ), QStringLiteral("ForPublicRelease") );
    m_pixmapSelector->addItem( i18n( "Not Approved" ), QStringLiteral("NotApproved") );
    m_pixmapSelector->addItem( i18n( "Not For Public Release" ), QStringLiteral("NotForPublicRelease") );
    m_pixmapSelector->addItem( i18n( "Sold" ), QStringLiteral("Sold") );
    m_pixmapSelector->addItem( i18n( "Top Secret" ), QStringLiteral("TopSecret") );
    m_pixmapSelector->setIcon( m_stampAnn->stampIconName() );
    m_pixmapSelector->setPreviewSize( 64 );

    connect( m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged );
}

void StampAnnotationWidget::applyChanges()
{
    AnnotationWidget::applyChanges();
    m_stampAnn->setStampIconName( m_pixmapSelector->icon() );
}



LineAnnotationWidget::LineAnnotationWidget( Okular::Annotation * ann )
    : AnnotationWidget( ann )
{
    m_lineAnn = static_cast< Okular::LineAnnotation * >( ann );
    if ( m_lineAnn->linePoints().count() == 2 )
        m_lineType = 0; // line
    else if ( m_lineAnn->lineClosed() )
        m_lineType = 1; // polygon
    else
        m_lineType = 2; // polyline
}

void LineAnnotationWidget::createStyleWidget( QFormLayout * formlayout )
{
    QWidget * widget = qobject_cast<QWidget *>( formlayout->parent() );

    addColorButton( widget, formlayout );
    addOpacitySpinBox( widget, formlayout );

    m_spinSize = new QDoubleSpinBox( widget );
    m_spinSize->setRange( 1, 100 );
    m_spinSize->setValue( m_lineAnn->style().width() );

    connect( m_spinSize, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &LineAnnotationWidget::dataChanged );

    // Straight line
    if ( m_lineType == 0 )
    {
        addVerticalSpacer( formlayout );
        formlayout->addRow( i18n( "&Width:" ), m_spinSize );

        //Line Term Styles
        addVerticalSpacer( formlayout );
        m_startStyleCombo = new QComboBox( widget );
        formlayout->addRow( i18n( "Line start:" ), m_startStyleCombo);
        m_endStyleCombo = new QComboBox( widget );
        formlayout->addRow( i18n( "Line end:" ), m_endStyleCombo);
        //FIXME: Where does the tooltip goes??

        const QList<QPair<Okular::LineAnnotation::TermStyle, QString>> termStyles
        {
            { Okular::LineAnnotation::Square, i18n( "Square" ) },
            { Okular::LineAnnotation::Circle, i18n( "Circle" ) },
            { Okular::LineAnnotation::Diamond, i18n( "Diamond" ) },
            { Okular::LineAnnotation::OpenArrow, i18n( "Open Arrow" ) },
            { Okular::LineAnnotation::ClosedArrow, i18n( "Closed Arrow" ) },
            { Okular::LineAnnotation::None, i18n( "None" ) },
            { Okular::LineAnnotation::Butt, i18n( "Butt" ) },
            { Okular::LineAnnotation::ROpenArrow, i18n( "Right Open Arrow" ) },
            { Okular::LineAnnotation::RClosedArrow, i18n( "Right Closed Arrow" ) },
            { Okular::LineAnnotation::Slash, i18n( "Slash" ) }
        };
        for ( const auto &item: termStyles )
        {
            const QIcon icon = endStyleIcon( item.first, QGuiApplication::palette().color( QPalette::WindowText ) );
            m_startStyleCombo->addItem( icon, item.second );
            m_endStyleCombo->addItem( icon, item.second );
        }

        m_startStyleCombo->setCurrentIndex( m_lineAnn->lineStartStyle() );
        m_endStyleCombo->setCurrentIndex( m_lineAnn->lineEndStyle() );

        //Leaders lengths
        addVerticalSpacer( formlayout );
        m_spinLL = new QDoubleSpinBox( widget );
        formlayout->addRow( i18n( "Leader line length:" ), m_spinLL );
        m_spinLLE = new QDoubleSpinBox( widget );
        formlayout->addRow( i18n( "Leader line extensions length:" ), m_spinLLE );

        m_spinLL->setRange( -500, 500 );
        m_spinLL->setValue( m_lineAnn->lineLeadingForwardPoint() );
        m_spinLLE->setRange( 0, 500 );
        m_spinLLE->setValue( m_lineAnn->lineLeadingBackwardPoint() );

        connect( m_startStyleCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &LineAnnotationWidget::dataChanged );
        connect( m_endStyleCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &LineAnnotationWidget::dataChanged );
        connect( m_spinLL, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &LineAnnotationWidget::dataChanged );
        connect( m_spinLLE, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &LineAnnotationWidget::dataChanged );
    }
    else if ( m_lineType == 1 ) // Polygon
    {
        QHBoxLayout * colorlay = new QHBoxLayout();
        m_useColor = new QCheckBox( i18n( "Enabled" ), widget );
        colorlay->addWidget( m_useColor );
        m_innerColor = new KColorButton( widget );
        colorlay->addWidget( m_innerColor);
        formlayout->addRow( i18n( "Shape fill:" ), colorlay );

        m_innerColor->setColor( m_lineAnn->lineInnerColor() );
        if ( m_lineAnn->lineInnerColor().isValid() )
        {
            m_useColor->setChecked( true );
        }
        else
        {
            m_innerColor->setEnabled( false );
        }

        addVerticalSpacer( formlayout );
        formlayout->addRow( i18n( "&Width:" ), m_spinSize );

        connect( m_innerColor, &KColorButton::changed, this, &AnnotationWidget::dataChanged );
        connect( m_useColor, &QAbstractButton::toggled, this, &AnnotationWidget::dataChanged );
        connect( m_useColor, &QCheckBox::toggled, m_innerColor, &KColorButton::setEnabled );
    }
}

void LineAnnotationWidget::applyChanges()
{
    AnnotationWidget::applyChanges();
    if ( m_lineType == 0 )
    {
        Q_ASSERT(m_spinLL && m_spinLLE && m_startStyleCombo && m_endStyleCombo);
        m_lineAnn->setLineLeadingForwardPoint( m_spinLL->value() );
        m_lineAnn->setLineLeadingBackwardPoint( m_spinLLE->value() );
        m_lineAnn->setLineStartStyle( (Okular::LineAnnotation::TermStyle)m_startStyleCombo->currentIndex() );
        m_lineAnn->setLineEndStyle( (Okular::LineAnnotation::TermStyle)m_endStyleCombo->currentIndex() );
    }
    else if ( m_lineType == 1 )
    {
        Q_ASSERT( m_useColor && m_innerColor );
        if ( !m_useColor->isChecked() )
        {
            m_lineAnn->setLineInnerColor( QColor() );
        }
        else
        {
            m_lineAnn->setLineInnerColor( m_innerColor->color() );
        }
    }
    Q_ASSERT( m_spinSize );
    m_lineAnn->style().setWidth( m_spinSize->value() );
}

QIcon LineAnnotationWidget::endStyleIcon( Okular::LineAnnotation::TermStyle endStyle, const QColor &lineColor ) {
    const int iconSize { 48 };
    QImage image { iconSize, iconSize, QImage::Format_ARGB32};
    image.fill( qRgba(0, 0, 0, 0) );
    Okular::LineAnnotation prototype;
    prototype.setLinePoints( { { 0, 0.5 }, { 0.65, 0.5 } } );
    prototype.setLineStartStyle(Okular::LineAnnotation::TermStyle::None);
    prototype.setLineEndStyle( endStyle );
    prototype.style().setWidth( 4 );
    prototype.style().setColor( lineColor );
    prototype.style().setLineStyle( Okular::Annotation::LineStyle::Solid );
    prototype.setBoundingRectangle( { 0, 0, 1, 1 } );
    LineAnnotPainter linepainter { &prototype, QSize { iconSize, iconSize }, 1, QTransform() };
    linepainter.draw( image );
    return QIcon( QPixmap::fromImage( image ) );
}



InkAnnotationWidget::InkAnnotationWidget( Okular::Annotation * ann )
    : AnnotationWidget( ann )
{
    m_inkAnn = static_cast< Okular::InkAnnotation * >( ann );
}

void InkAnnotationWidget::createStyleWidget( QFormLayout * formlayout )
{
    QWidget * widget = qobject_cast<QWidget *>( formlayout->parent() );

    addColorButton( widget, formlayout );
    addOpacitySpinBox( widget, formlayout );

    addVerticalSpacer( formlayout );

    m_spinSize = new QDoubleSpinBox( widget );
    formlayout->addRow( i18n( "&Width:" ), m_spinSize );

    m_spinSize->setRange( 1, 100 );
    m_spinSize->setValue( m_inkAnn->style().width() );

    connect( m_spinSize, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &AnnotationWidget::dataChanged );
}

void InkAnnotationWidget::applyChanges()
{
    AnnotationWidget::applyChanges();
    m_inkAnn->style().setWidth( m_spinSize->value() );
}



HighlightAnnotationWidget::HighlightAnnotationWidget( Okular::Annotation * ann )
    : AnnotationWidget( ann )
{
    m_hlAnn = static_cast< Okular::HighlightAnnotation * >( ann );
}

void HighlightAnnotationWidget::createStyleWidget( QFormLayout * formlayout )
{
    QWidget * widget = qobject_cast<QWidget *>( formlayout->parent() );

    m_typeCombo = new KComboBox( widget );
    formlayout->addRow( i18n( "Type:" ), m_typeCombo );
    m_typeCombo->addItem( i18n( "Highlight" ) );
    m_typeCombo->addItem( i18n( "Squiggle" ) );
    m_typeCombo->addItem( i18n( "Underline" ) );
    m_typeCombo->addItem( i18n( "Strike out" ) );
    m_typeCombo->setCurrentIndex( m_hlAnn->highlightType() );

    addVerticalSpacer( formlayout );
    addColorButton( widget, formlayout );
    addOpacitySpinBox( widget, formlayout );

    connect( m_typeCombo, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &AnnotationWidget::dataChanged );
}

void HighlightAnnotationWidget::applyChanges()
{
    AnnotationWidget::applyChanges();
    m_hlAnn->setHighlightType( (Okular::HighlightAnnotation::HighlightType)m_typeCombo->currentIndex() );
}



GeomAnnotationWidget::GeomAnnotationWidget( Okular::Annotation * ann )
    : AnnotationWidget( ann )
{
    m_geomAnn = static_cast< Okular::GeomAnnotation * >( ann );
}

void GeomAnnotationWidget::createStyleWidget( QFormLayout * formlayout )
{
    QWidget * widget = qobject_cast<QWidget *>( formlayout->parent() );

    m_typeCombo = new KComboBox( widget );
    formlayout->addRow( i18n( "Type:" ), m_typeCombo );
    addVerticalSpacer( formlayout );
    addColorButton( widget, formlayout );
    addOpacitySpinBox( widget, formlayout );
    QHBoxLayout * colorlay = new QHBoxLayout();
    m_useColor = new QCheckBox( i18n( "Enabled" ), widget );
    colorlay->addWidget(m_useColor);
    m_innerColor = new KColorButton( widget );
    colorlay->addWidget( m_innerColor);
    formlayout->addRow( i18n( "Shape fill:" ), colorlay );
    addVerticalSpacer( formlayout );
    m_spinSize = new QDoubleSpinBox( widget );
    formlayout->addRow( i18n( "&Width:" ), m_spinSize );

    m_typeCombo->addItem( i18n( "Rectangle" ) );
    m_typeCombo->addItem( i18n( "Ellipse" ) );
    m_typeCombo->setCurrentIndex( m_geomAnn->geometricalType() );
    m_innerColor->setColor( m_geomAnn->geometricalInnerColor() );
    if ( m_geomAnn->geometricalInnerColor().isValid() )
    {
        m_useColor->setChecked( true );
    }
    else
    {
        m_innerColor->setEnabled( false );
    }
    m_spinSize->setRange( 0, 100 );
    m_spinSize->setValue( m_geomAnn->style().width() );

    connect( m_typeCombo, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &AnnotationWidget::dataChanged );
    connect( m_innerColor, &KColorButton::changed, this, &AnnotationWidget::dataChanged );
    connect( m_useColor, &QAbstractButton::toggled, this, &AnnotationWidget::dataChanged );
    connect(m_useColor, &QCheckBox::toggled, m_innerColor, &KColorButton::setEnabled);
    connect( m_spinSize, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &AnnotationWidget::dataChanged );
}

void GeomAnnotationWidget::applyChanges()
{
    AnnotationWidget::applyChanges();
    m_geomAnn->setGeometricalType( (Okular::GeomAnnotation::GeomType)m_typeCombo->currentIndex() );
    if ( !m_useColor->isChecked() )
    {
        m_geomAnn->setGeometricalInnerColor( QColor() );
    }
    else
    {
        m_geomAnn->setGeometricalInnerColor( m_innerColor->color() );
    }
    m_geomAnn->style().setWidth( m_spinSize->value() );
}



FileAttachmentAnnotationWidget::FileAttachmentAnnotationWidget( Okular::Annotation * ann )
    : AnnotationWidget( ann ), m_pixmapSelector( nullptr )
{
    m_attachAnn = static_cast< Okular::FileAttachmentAnnotation * >( ann );
}

void FileAttachmentAnnotationWidget::createStyleWidget( QFormLayout * formlayout )
{
    QWidget * widget = qobject_cast<QWidget *>( formlayout->parent() );

    addOpacitySpinBox( widget, formlayout );

    m_pixmapSelector = new PixmapPreviewSelector( widget );
    formlayout->addRow( i18n( "File attachment symbol:" ), m_pixmapSelector );
    m_pixmapSelector->setEditable( true );

    m_pixmapSelector->addItem( i18nc( "Symbol for file attachment annotations", "Graph" ), QStringLiteral("graph") );
    m_pixmapSelector->addItem( i18nc( "Symbol for file attachment annotations", "Push Pin" ), QStringLiteral("pushpin") );
    m_pixmapSelector->addItem( i18nc( "Symbol for file attachment annotations", "Paperclip" ), QStringLiteral("paperclip") );
    m_pixmapSelector->addItem( i18nc( "Symbol for file attachment annotations", "Tag" ), QStringLiteral("tag") );
    m_pixmapSelector->setIcon( m_attachAnn->fileIconName() );

    connect( m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged );
}

QWidget * FileAttachmentAnnotationWidget::createExtraWidget()
{
    QWidget * widget = new QWidget();
    widget->setWindowTitle( i18nc( "'File' as normal file, that can be opened, saved, etc..", "File" ) );

    Okular::EmbeddedFile *ef = m_attachAnn->embeddedFile();
    const int size = ef->size();
    const QString sizeString = size <= 0 ? i18nc( "Not available size", "N/A" ) : KFormat().formatByteSize( size );
    const QString descString = ef->description().isEmpty() ? i18n( "No description available." ) : ef->description();

    QHBoxLayout * mainLay = new QHBoxLayout( widget );
    QFormLayout * lay = new QFormLayout();
    mainLay->addLayout( lay );

    QLabel * tmplabel = new QLabel( ef->name() , widget );
    tmplabel->setTextInteractionFlags( Qt::TextSelectableByMouse );
    lay->addRow( i18n( "Name:" ), tmplabel );

    tmplabel = new QLabel( sizeString, widget );
    tmplabel->setTextInteractionFlags( Qt::TextSelectableByMouse );
    lay->addRow( i18n( "&Width:" ), tmplabel );

    tmplabel = new QLabel( widget );
    tmplabel->setTextFormat( Qt::PlainText );
    tmplabel->setWordWrap( true );
    tmplabel->setText( descString );
    tmplabel->setTextInteractionFlags( Qt::TextSelectableByMouse );
    lay->addRow( i18n( "Description:" ), tmplabel );

    QMimeDatabase db;
    QMimeType mime = db.mimeTypeForFile( ef->name(), QMimeDatabase::MatchExtension);
    if ( mime.isValid() )
    {
        tmplabel = new QLabel( widget );
        tmplabel->setPixmap( QIcon::fromTheme( mime.iconName() ).pixmap( FILEATTACH_ICONSIZE, FILEATTACH_ICONSIZE ) );
        tmplabel->setFixedSize( FILEATTACH_ICONSIZE, FILEATTACH_ICONSIZE );
        QVBoxLayout * tmpLayout = new QVBoxLayout( widget );
        tmpLayout->setAlignment( Qt::AlignTop );
        mainLay->addLayout( tmpLayout );
        tmpLayout->addWidget( tmplabel );
    }

    return widget;
}

void FileAttachmentAnnotationWidget::applyChanges()
{
    AnnotationWidget::applyChanges();
    m_attachAnn->setFileIconName( m_pixmapSelector->icon() );
}



static QString caretSymbolToIcon( Okular::CaretAnnotation::CaretSymbol symbol )
{
    switch ( symbol )
    {
        case Okular::CaretAnnotation::None:
            return QStringLiteral( "caret-none" );
        case Okular::CaretAnnotation::P:
            return QStringLiteral( "caret-p" );
    }
    return QString();
}

static Okular::CaretAnnotation::CaretSymbol caretSymbolFromIcon( const QString &icon )
{
    if ( icon == QLatin1String( "caret-none" ) )
        return Okular::CaretAnnotation::None;
    else if ( icon == QLatin1String( "caret-p" ) )
        return Okular::CaretAnnotation::P;
    return Okular::CaretAnnotation::None;
}

CaretAnnotationWidget::CaretAnnotationWidget( Okular::Annotation * ann )
    : AnnotationWidget( ann ), m_pixmapSelector( nullptr )
{
    m_caretAnn = static_cast< Okular::CaretAnnotation * >( ann );
}

void CaretAnnotationWidget::createStyleWidget( QFormLayout * formlayout )
{
    QWidget * widget = qobject_cast<QWidget *>( formlayout->parent() );

    addColorButton( widget, formlayout );
    addOpacitySpinBox( widget, formlayout );

    m_pixmapSelector = new PixmapPreviewSelector( widget );
    formlayout->addRow( i18n( "Caret symbol:" ), m_pixmapSelector );

    m_pixmapSelector->addItem( i18nc( "Symbol for caret annotations", "None" ), QStringLiteral("caret-none") );
    m_pixmapSelector->addItem( i18nc( "Symbol for caret annotations", "P" ), QStringLiteral("caret-p") );
    m_pixmapSelector->setIcon( caretSymbolToIcon( m_caretAnn->caretSymbol() ) );

    connect( m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged );
}

void CaretAnnotationWidget::applyChanges()
{
    AnnotationWidget::applyChanges();
    m_caretAnn->setCaretSymbol( caretSymbolFromIcon( m_pixmapSelector->icon() ) );
}



#include "moc_annotationwidgets.cpp"
