/* -*- 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 <vcl/ColorDialog.hxx>
#include <vcl/alpha.hxx>
#include <vcl/event.hxx>
#include <vcl/metaact.hxx>
#include <vcl/virdev.hxx>
#include <svtools/valueset.hxx>
#include <svl/eitem.hxx>
#include <svl/itemset.hxx>
#include <sfx2/dispatch.hxx>

#include <svx/colorbox.hxx>
#include <svx/dialmgr.hxx>
#include <svx/bmpmask.hxx>
#include <svx/strings.hrc>
#include <svx/svxids.hrc>
#include <memory>
#include <helpids.h>

#define OWN_CALLMODE    SfxCallMode::ASYNCHRON | SfxCallMode::RECORD


#define TEST_COLS()                                                 \
{                                                                   \
    nR = aCol.GetRed(); nG = aCol.GetGreen(); nB = aCol.GetBlue();  \
    for( i = 0; i < nCount; i++ )                                   \
    {                                                               \
        if ( ( pMinR[i] <= nR ) && ( pMaxR[i] >= nR ) &&            \
             ( pMinG[i] <= nG ) && ( pMaxG[i] >= nG ) &&            \
             ( pMinB[i] <= nB ) && ( pMaxB[i] >= nB ) )             \
        {                                                           \
            aCol = pDstCols[i]; bReplace = true; break;             \
        }                                                           \
    }                                                               \
}

SFX_IMPL_DOCKINGWINDOW_WITHID( SvxBmpMaskChildWindow, SID_BMPMASK )

class BmpColorWindow : public weld::CustomWidgetController
{
    Color       aColor;


public:
    explicit BmpColorWindow()
        : aColor( COL_WHITE )
    {
    }

    void SetColor( const Color& rColor )
    {
        aColor = rColor;
        Invalidate();
    }

    virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;

    virtual void SetDrawingArea(weld::DrawingArea* pArea) override
    {
        Size aSize(pArea->get_ref_device().LogicToPixel(Size(43, 14), MapMode(MapUnit::MapAppFont)));
        CustomWidgetController::SetDrawingArea(pArea);
        pArea->set_size_request(aSize.Width(), aSize.Height());
        SetOutputSizePixel(aSize);
    }
};

class MaskSet : public ValueSet
{
    VclPtr<SvxBmpMask> pSvxBmpMask;

public:
    MaskSet(SvxBmpMask* pMask);
    virtual void Select() override;
    virtual bool KeyInput( const KeyEvent& rKEvt ) override;
    virtual void GetFocus() override;
    virtual void SetDrawingArea(weld::DrawingArea* pArea) override
    {
        Size aSize(pArea->get_ref_device().LogicToPixel(Size(24, 12), MapMode(MapUnit::MapAppFont)));
        ValueSet::SetDrawingArea(pArea);
        pArea->set_size_request(aSize.Width(), aSize.Height());
        SetOutputSizePixel(aSize);
        SetHelpId(HID_BMPMASK_CTL_QCOL_1);
    }
    void onEditColor();
};

MaskSet::MaskSet(SvxBmpMask* pMask)
    : ValueSet(nullptr)
    , pSvxBmpMask(pMask)
{
}

void MaskSet::Select()
{
    ValueSet::Select();

    pSvxBmpMask->onSelect( this );
}

void MaskSet::GetFocus()
{
    ValueSet::GetFocus();
    SelectItem( 1 );
    pSvxBmpMask->onSelect( this );
}

bool MaskSet::KeyInput( const KeyEvent& rKEvt )
{
    bool bRet = false;

    vcl::KeyCode aCode = rKEvt.GetKeyCode();

    // if the key has a modifier we don't care
    if( aCode.GetModifier() )
    {
        bRet = ValueSet::KeyInput( rKEvt );
    }
    else
    {
        // check for keys that interests us
        switch ( aCode.GetCode() )
        {
            case KEY_SPACE:
                onEditColor();
                bRet = true;
                break;
            default:
                bRet = ValueSet::KeyInput( rKEvt );
        }
    }
    return bRet;
}

void MaskSet::onEditColor()
{
    ColorDialog aColorDlg(pSvxBmpMask->GetFrameWeld());

    aColorDlg.SetColor(GetItemColor(1));

    if (aColorDlg.Execute())
        SetItemColor(1, aColorDlg.GetColor());
}

class MaskData
{
    VclPtr<SvxBmpMask> pMask;
    bool            bIsReady;
    bool            bExecState;
    SfxBindings&    rBindings;

public:
                MaskData( SvxBmpMask* pBmpMask, SfxBindings& rBind );

    bool        IsCbxReady() const { return bIsReady; }
    void        SetExecState( bool bState ) { bExecState = bState; }
    bool        IsExecReady() const { return bExecState; }

                DECL_LINK( PipetteHdl, const OUString&, void );
                DECL_LINK( CbxHdl, weld::Toggleable&, void);
                DECL_LINK( CbxTransHdl, weld::Toggleable&, void );
                DECL_LINK( FocusLbHdl, weld::Widget&, void );
                DECL_LINK(ExecHdl, weld::Button&, void);
};


MaskData::MaskData( SvxBmpMask* pBmpMask, SfxBindings& rBind ) :

    pMask       ( pBmpMask ),
    bIsReady    ( false ),
    bExecState  ( false ),
    rBindings   ( rBind )

{
}

IMPL_LINK( MaskData, PipetteHdl, const OUString&, rId, void )
{
    SfxBoolItem aBItem( SID_BMPMASK_PIPETTE,
                        pMask->m_xTbxPipette->get_item_active(rId) );

    rBindings.GetDispatcher()->ExecuteList(SID_BMPMASK_PIPETTE, OWN_CALLMODE,
            { &aBItem });
}

IMPL_LINK( MaskData, CbxHdl, weld::Toggleable&, rCbx, void )
{
    bIsReady =  pMask->m_xCbx1->get_active() || pMask->m_xCbx2->get_active() ||
                pMask->m_xCbx3->get_active() || pMask->m_xCbx4->get_active();

    if ( bIsReady && IsExecReady() )
        pMask->m_xBtnExec->set_sensitive(true);
    else
        pMask->m_xBtnExec->set_sensitive(false);

    // When a checkbox is checked, the pipette is enabled
    if ( !rCbx.get_active() )
        return;

    MaskSet* pSet = nullptr;

    if (&rCbx == pMask->m_xCbx1.get())
        pSet = pMask->m_xQSet1.get();
    else if (&rCbx == pMask->m_xCbx2.get())
        pSet = pMask->m_xQSet2.get();
    else if (&rCbx == pMask->m_xCbx3.get())
        pSet = pMask->m_xQSet3.get();
    else // if ( &rCbx == pMask->m_xCbx4 )
        pSet = pMask->m_xQSet4.get();

    pSet->SelectItem( 1 );
    pSet->Select();

    pMask->m_xTbxPipette->set_item_active(u"pipette"_ustr, true);
    PipetteHdl(u"pipette"_ustr);
}

IMPL_LINK( MaskData, CbxTransHdl, weld::Toggleable&, rCbx, void )
{
    bIsReady = rCbx.get_active();
    if ( bIsReady )
    {
        pMask->m_xQSet1->Disable();
        pMask->m_xQSet2->Disable();
        pMask->m_xQSet3->Disable();
        pMask->m_xQSet4->Disable();
        pMask->m_xCtlPipette->Disable();
        pMask->m_xCbx1->set_sensitive(false);
        pMask->m_xSp1->set_sensitive(false);
        pMask->m_xCbx2->set_sensitive(false);
        pMask->m_xSp2->set_sensitive(false);
        pMask->m_xCbx3->set_sensitive(false);
        pMask->m_xSp3->set_sensitive(false);
        pMask->m_xCbx4->set_sensitive(false);
        pMask->m_xSp4->set_sensitive(false);
        pMask->m_xTbxPipette->set_sensitive(false);

        pMask->m_xLbColor1->set_sensitive(false);
        pMask->m_xLbColor2->set_sensitive(false);
        pMask->m_xLbColor3->set_sensitive(false);
        pMask->m_xLbColor4->set_sensitive(false);
        pMask->m_xLbColorTrans->set_sensitive(true);
    }
    else
    {
        pMask->m_xQSet1->Enable();
        pMask->m_xQSet2->Enable();
        pMask->m_xQSet3->Enable();
        pMask->m_xQSet4->Enable();
        pMask->m_xCtlPipette->Enable();
        pMask->m_xCbx1->set_sensitive(true);
        pMask->m_xSp1->set_sensitive(true);
        pMask->m_xCbx2->set_sensitive(true);
        pMask->m_xSp2->set_sensitive(true);
        pMask->m_xCbx3->set_sensitive(true);
        pMask->m_xSp3->set_sensitive(true);
        pMask->m_xCbx4->set_sensitive(true);
        pMask->m_xSp4->set_sensitive(true);
        pMask->m_xTbxPipette->set_sensitive(true);

        pMask->m_xLbColor1->set_sensitive(true);
        pMask->m_xLbColor2->set_sensitive(true);
        pMask->m_xLbColor3->set_sensitive(true);
        pMask->m_xLbColor4->set_sensitive(true);
        pMask->m_xLbColorTrans->set_sensitive(false);

        bIsReady = pMask->m_xCbx1->get_active() || pMask->m_xCbx2->get_active() ||
                   pMask->m_xCbx3->get_active() || pMask->m_xCbx4->get_active();
    }

    if ( bIsReady && IsExecReady() )
        pMask->m_xBtnExec->set_sensitive(true);
    else
        pMask->m_xBtnExec->set_sensitive(false);
}

IMPL_LINK( MaskData, FocusLbHdl, weld::Widget&, rLb, void )
{
    pMask->m_xQSet1->SelectItem( &rLb == &pMask->m_xLbColor1->get_widget() ? 1 : 0 /* , false */ );
    pMask->m_xQSet2->SelectItem( &rLb == &pMask->m_xLbColor2->get_widget() ? 1 : 0 /* , false */ );
    pMask->m_xQSet3->SelectItem( &rLb == &pMask->m_xLbColor3->get_widget() ? 1 : 0 /* , false */ );
    pMask->m_xQSet4->SelectItem( &rLb == &pMask->m_xLbColor4->get_widget() ? 1 : 0 /* , false */ );
}

IMPL_LINK_NOARG(MaskData, ExecHdl, weld::Button&, void)
{
    SfxBoolItem aBItem( SID_BMPMASK_EXEC, true );
    rBindings.GetDispatcher()->ExecuteList(SID_BMPMASK_EXEC, OWN_CALLMODE,
            { &aBItem });
}

void BmpColorWindow::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& /*Rect*/)
{
    auto popIt = rRenderContext.ScopedPush(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
    rRenderContext.SetLineColor(aColor);
    rRenderContext.SetFillColor(aColor);
    rRenderContext.DrawRect(tools::Rectangle(Point(), GetOutputSizePixel()));
}

SvxBmpMaskSelectItem::SvxBmpMaskSelectItem( SvxBmpMask& rMask,
                                            SfxBindings& rBindings ) :
            SfxControllerItem   ( SID_BMPMASK_EXEC, rBindings ),
            m_rBmpMask            ( rMask)
{
}

void SvxBmpMaskSelectItem::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState /*eState*/,
                                         const SfxPoolItem* pItem )
{
    if ( ( nSID == SID_BMPMASK_EXEC ) && pItem )
    {
        const SfxBoolItem* pStateItem = dynamic_cast<const SfxBoolItem*>( pItem  );
        assert(pStateItem); // SfxBoolItem expected
        if (pStateItem)
            m_rBmpMask.SetExecState( pStateItem->GetValue() );
    }
}

SvxBmpMaskChildWindow::SvxBmpMaskChildWindow(vcl::Window* pParent_, sal_uInt16 nId,
                                             SfxBindings* pBindings,
                                             SfxChildWinInfo* pInfo)
    : SfxChildWindow(pParent_, nId)
{
    VclPtr<SvxBmpMask> pDlg = VclPtr<SvxBmpMask>::Create(pBindings, this, pParent_);

    SetWindow( pDlg );

    pDlg->Initialize( pInfo );
}

SvxBmpMask::SvxBmpMask(SfxBindings *pBindinx, SfxChildWindow *pCW, vcl::Window* pParent)
    : SfxDockingWindow(pBindinx, pCW, pParent, u"DockingColorReplace"_ustr,
                       u"svx/ui/dockingcolorreplace.ui"_ustr)
    , m_xTbxPipette(m_xBuilder->weld_toolbar(u"toolbar"_ustr))
    , m_xCtlPipette(new BmpColorWindow)
    , m_xCtlPipetteWin(new weld::CustomWeld(*m_xBuilder, u"toolcolor"_ustr, *m_xCtlPipette))
    , m_xBtnExec(m_xBuilder->weld_button(u"replace"_ustr))
    , m_xCbx1(m_xBuilder->weld_check_button(u"cbx1"_ustr))
    , m_xQSet1(new MaskSet(this))
    , m_xQSetWin1(new weld::CustomWeld(*m_xBuilder, u"qset1"_ustr, *m_xQSet1))
    , m_xSp1(m_xBuilder->weld_metric_spin_button(u"tol1"_ustr, FieldUnit::PERCENT))
    , m_xLbColor1(new ColorListBox(m_xBuilder->weld_menu_button(u"color1"_ustr), [this]{ return GetFrameWeld(); }))
    , m_xCbx2(m_xBuilder->weld_check_button(u"cbx2"_ustr))
    , m_xQSet2(new MaskSet(this))
    , m_xQSetWin2(new weld::CustomWeld(*m_xBuilder, u"qset2"_ustr, *m_xQSet2))
     , m_xSp2(m_xBuilder->weld_metric_spin_button(u"tol2"_ustr, FieldUnit::PERCENT))
    , m_xLbColor2(new ColorListBox(m_xBuilder->weld_menu_button(u"color2"_ustr), [this]{ return GetFrameWeld(); }))
    , m_xCbx3(m_xBuilder->weld_check_button(u"cbx3"_ustr))
    , m_xQSet3(new MaskSet(this))
    , m_xQSetWin3(new weld::CustomWeld(*m_xBuilder, u"qset3"_ustr, *m_xQSet3))
    , m_xSp3(m_xBuilder->weld_metric_spin_button(u"tol3"_ustr, FieldUnit::PERCENT))
    , m_xLbColor3(new ColorListBox(m_xBuilder->weld_menu_button(u"color3"_ustr), [this]{ return GetFrameWeld(); }))
    , m_xCbx4(m_xBuilder->weld_check_button(u"cbx4"_ustr))
    , m_xQSet4(new MaskSet(this))
    , m_xQSetWin4(new weld::CustomWeld(*m_xBuilder, u"qset4"_ustr, *m_xQSet4))
    , m_xSp4(m_xBuilder->weld_metric_spin_button(u"tol4"_ustr, FieldUnit::PERCENT))
    , m_xLbColor4(new ColorListBox(m_xBuilder->weld_menu_button(u"color4"_ustr), [this]{ return GetFrameWeld(); }))
    , m_xCbxTrans(m_xBuilder->weld_check_button(u"cbx5"_ustr))
    , m_xLbColorTrans(new ColorListBox(m_xBuilder->weld_menu_button(u"color5"_ustr), [this]{ return GetFrameWeld(); }))
    , m_xData(new MaskData(this, *pBindinx))
    , m_aPipetteColor(COL_WHITE)
    , m_aSelItem(*this, *pBindinx)
{
    SetText(SvxResId(RID_SVXDLG_BMPMASK_STR_TITLE));

    m_xLbColor1->SetSlotId(SID_BMPMASK_COLOR);
    m_xLbColor2->SetSlotId(SID_BMPMASK_COLOR);
    m_xLbColor3->SetSlotId(SID_BMPMASK_COLOR);
    m_xLbColor4->SetSlotId(SID_BMPMASK_COLOR);

    m_xLbColorTrans->SelectEntry(COL_BLACK);
    m_xLbColor1->SelectEntry(COL_TRANSPARENT);
    m_xLbColor2->SelectEntry(COL_TRANSPARENT);
    m_xLbColor3->SelectEntry(COL_TRANSPARENT);
    m_xLbColor4->SelectEntry(COL_TRANSPARENT);

    m_xTbxPipette->connect_clicked( LINK( m_xData.get(), MaskData, PipetteHdl ) );
    m_xBtnExec->connect_clicked( LINK( m_xData.get(), MaskData, ExecHdl ) );

    m_xCbx1->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) );
    m_xCbx2->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) );
    m_xCbx3->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) );
    m_xCbx4->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) );
    m_xCbxTrans->connect_toggled( LINK( m_xData.get(), MaskData, CbxTransHdl ) );

    SetAccessibleNames ();

    m_xLbColor1->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) );
    m_xLbColor2->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) );
    m_xLbColor3->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) );
    m_xLbColor4->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) );
    m_xLbColorTrans->set_sensitive(false);

    OUString sColorPalette (SvxResId( RID_SVXDLG_BMPMASK_STR_PALETTE));
    OUString sColorPaletteN;

    m_xQSet1->SetStyle( m_xQSet1->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER );
    m_xQSet1->SetColCount();
    m_xQSet1->SetLineCount( 1 );
    sColorPaletteN = sColorPalette + " 1";
    m_xQSet1->InsertItem( 1, m_aPipetteColor, sColorPaletteN);
    m_xQSet1->SelectItem( 1 );

    m_xQSet2->SetStyle( m_xQSet2->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER );
    m_xQSet2->SetColCount();
    m_xQSet2->SetLineCount( 1 );
    sColorPaletteN = sColorPalette + " 2";
    m_xQSet2->InsertItem( 1, m_aPipetteColor, sColorPaletteN);
    m_xQSet2->SelectItem( 0 );

    m_xQSet3->SetStyle( m_xQSet3->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER );
    m_xQSet3->SetColCount();
    m_xQSet3->SetLineCount( 1 );
    sColorPaletteN = sColorPalette + " 3";
    m_xQSet3->InsertItem( 1, m_aPipetteColor, sColorPaletteN);
    m_xQSet3->SelectItem( 0 );

    m_xQSet4->SetStyle( m_xQSet4->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER );
    m_xQSet4->SetColCount();
    m_xQSet4->SetLineCount( 1 );
    sColorPaletteN = sColorPalette + " 4";
    m_xQSet4->InsertItem( 1, m_aPipetteColor, sColorPaletteN);
    m_xQSet4->SelectItem( 0 );

    m_xQSet1->Show();
    m_xQSet2->Show();
    m_xQSet3->Show();
    m_xQSet4->Show();
}

SvxBmpMask::~SvxBmpMask()
{
    disposeOnce();
}

void SvxBmpMask::dispose()
{
    m_xQSetWin1.reset();
    m_xQSet1.reset();
    m_xQSetWin2.reset();
    m_xQSet2.reset();
    m_xQSetWin3.reset();
    m_xQSet3.reset();
    m_xQSetWin4.reset();
    m_xQSet4.reset();
    m_xCtlPipetteWin.reset();
    m_xCtlPipette.reset();
    m_xData.reset();
    m_xTbxPipette.reset();
    m_xBtnExec.reset();
    m_xCbx1.reset();
    m_xSp1.reset();
    m_xLbColor1.reset();
    m_xCbx2.reset();
    m_xSp2.reset();
    m_xLbColor2.reset();
    m_xCbx3.reset();
    m_xSp3.reset();
    m_xLbColor3.reset();
    m_xCbx4.reset();
    m_xSp4.reset();
    m_xLbColor4.reset();
    m_xCbxTrans.reset();
    m_xLbColorTrans.reset();
    m_aSelItem.dispose();
    SfxDockingWindow::dispose();
}

/** is called by a MaskSet when it is selected */
void SvxBmpMask::onSelect( const MaskSet* pSet )
{
    // now deselect all other value sets
    if( pSet != m_xQSet1.get() )
        m_xQSet1->SelectItem( 0 );

    if( pSet != m_xQSet2.get() )
        m_xQSet2->SelectItem( 0 );

    if( pSet != m_xQSet3.get() )
        m_xQSet3->SelectItem( 0 );

    if( pSet != m_xQSet4.get() )
        m_xQSet4->SelectItem( 0 );
}

bool SvxBmpMask::Close()
{
    SfxBoolItem aItem2( SID_BMPMASK_PIPETTE, false );
    GetBindings().GetDispatcher()->ExecuteList(SID_BMPMASK_PIPETTE,
            OWN_CALLMODE, { &aItem2 });

    return SfxDockingWindow::Close();
}

void SvxBmpMask::SetColor( const Color& rColor )
{
    m_aPipetteColor = rColor;
    m_xCtlPipette->SetColor( m_aPipetteColor );
}

void SvxBmpMask::PipetteClicked()
{
    if( m_xQSet1->GetSelectedItemId() == 1 )
    {
        m_xCbx1->set_active(true);
        m_xData->CbxHdl(*m_xCbx1);
        m_xQSet1->SetItemColor( 1, m_aPipetteColor );
        m_xQSet1->SetFormat();
    }
    else if( m_xQSet2->GetSelectedItemId() == 1 )
    {
        m_xCbx2->set_active(true);
        m_xData->CbxHdl(*m_xCbx2);
        m_xQSet2->SetItemColor( 1, m_aPipetteColor );
        m_xQSet2->SetFormat();
    }
    else if( m_xQSet3->GetSelectedItemId() == 1 )
    {
        m_xCbx3->set_active(true);
        m_xData->CbxHdl(*m_xCbx3);
        m_xQSet3->SetItemColor( 1, m_aPipetteColor );
        m_xQSet3->SetFormat();
    }
    else if( m_xQSet4->GetSelectedItemId() == 1 )
    {
        m_xCbx4->set_active(true);
        m_xData->CbxHdl(*m_xCbx4);
        m_xQSet4->SetItemColor( 1, m_aPipetteColor );
        m_xQSet4->SetFormat();
    }

    m_xTbxPipette->set_item_active(u"pipette"_ustr, false);
    m_xData->PipetteHdl(u"pipette"_ustr);
}

void SvxBmpMask::SetExecState( bool bEnable )
{
    m_xData->SetExecState( bEnable );

    if ( m_xData->IsExecReady() && m_xData->IsCbxReady() )
        m_xBtnExec->set_sensitive(true);
    else
        m_xBtnExec->set_sensitive(false);
}


sal_uInt16 SvxBmpMask::InitColorArrays( Color* pSrcCols, Color* pDstCols, sal_uInt8* pTols )
{
    sal_uInt16  nCount = 0;

    if ( m_xCbx1->get_active() )
    {
        pSrcCols[nCount] = m_xQSet1->GetItemColor( 1 );
        pDstCols[nCount] = m_xLbColor1->GetSelectEntryColor();
        pTols[nCount++] = static_cast<sal_uInt8>(m_xSp1->get_value(FieldUnit::PERCENT));
    }

    if ( m_xCbx2->get_active() )
    {
        pSrcCols[nCount] = m_xQSet2->GetItemColor( 1 );
        pDstCols[nCount] = m_xLbColor2->GetSelectEntryColor();
        pTols[nCount++] = static_cast<sal_uInt8>(m_xSp2->get_value(FieldUnit::PERCENT));
    }

    if ( m_xCbx3->get_active() )
    {
        pSrcCols[nCount] = m_xQSet3->GetItemColor( 1 );
        pDstCols[nCount] = m_xLbColor3->GetSelectEntryColor();
        pTols[nCount++] = static_cast<sal_uInt8>(m_xSp3->get_value(FieldUnit::PERCENT));
    }

    if ( m_xCbx4->get_active() )
    {
        pSrcCols[nCount] = m_xQSet4->GetItemColor( 1 );
        pDstCols[nCount] = m_xLbColor4->GetSelectEntryColor();
        pTols[nCount++] = static_cast<sal_uInt8>(m_xSp4->get_value(FieldUnit::PERCENT));
    }

    return nCount;
}

void SvxBmpMask::ImpMask( Bitmap& rBitmap )
{
    Color           pSrcCols[4];
    Color           pDstCols[4];
    sal_uInt8       pTols[4];
    const sal_uInt16 nCount = InitColorArrays( pSrcCols, pDstCols, pTols );

    EnterWait();
    rBitmap.Replace( pSrcCols, pDstCols, nCount, pTols );
    LeaveWait();
}

Bitmap SvxBmpMask::ImpMaskTransparent( const Bitmap& rBitmap, const Color& rColor, const sal_uInt8 nTol )
{
    EnterWait();

    AlphaMask   aMask( rBitmap.CreateColorBitmap().CreateAlphaMask( rColor, nTol ) );

    if( rBitmap.HasAlpha() )
        aMask.AlphaCombineOr( rBitmap.CreateAlphaMask() );

    Bitmap aBmp( rBitmap.CreateColorBitmap(), aMask );
    LeaveWait();

    return aBmp;
}


Animation SvxBmpMask::ImpMask( const Animation& rAnimation )
{
    Animation   aAnimation( rAnimation );
    Color       pSrcCols[4];
    Color       pDstCols[4];
    sal_uInt8   pTols[4];
    InitColorArrays( pSrcCols, pDstCols, pTols );
    sal_uInt16  nAnimationCount = aAnimation.Count();

    for( sal_uInt16 i = 0; i < nAnimationCount; i++ )
    {
        AnimationFrame aAnimationFrame( aAnimation.Get( i ) );
        aAnimationFrame.maBitmap = Mask(aAnimationFrame.maBitmap).GetBitmap();
        aAnimation.Replace(aAnimationFrame, i);
    }

    return aAnimation;
}


GDIMetaFile SvxBmpMask::ImpMask( const GDIMetaFile& rMtf )
{
    GDIMetaFile aMtf;
    Color       pSrcCols[4];
    Color       pDstCols[4];
    sal_uInt8   pTols[4];
    sal_uInt16  nCount = InitColorArrays( pSrcCols, pDstCols, pTols );

    // If no color is selected, we copy only the Mtf
    if( !nCount )
        aMtf = rMtf;
    else
    {
        bool        pTrans[4];
        Color       aCol;
        tools::Long        nR;
        tools::Long        nG;
        tools::Long        nB;
        std::unique_ptr<tools::Long[]> pMinR(new tools::Long[nCount]);
        std::unique_ptr<tools::Long[]> pMaxR(new tools::Long[nCount]);
        std::unique_ptr<tools::Long[]> pMinG(new tools::Long[nCount]);
        std::unique_ptr<tools::Long[]> pMaxG(new tools::Long[nCount]);
        std::unique_ptr<tools::Long[]> pMinB(new tools::Long[nCount]);
        std::unique_ptr<tools::Long[]> pMaxB(new tools::Long[nCount]);
        sal_uInt16      i;

        aMtf.SetPrefSize( rMtf.GetPrefSize() );
        aMtf.SetPrefMapMode( rMtf.GetPrefMapMode() );

        // Prepare Color comparison array
        for( i = 0; i < nCount; i++ )
        {
            tools::Long nTol = ( pTols[i] * 255 ) / 100;

            tools::Long nVal = static_cast<tools::Long>(pSrcCols[i].GetRed());
            pMinR[i] = std::max( nVal - nTol, tools::Long(0) );
            pMaxR[i] = std::min( nVal + nTol, tools::Long(255) );

            nVal = static_cast<tools::Long>(pSrcCols[i].GetGreen());
            pMinG[i] = std::max( nVal - nTol, tools::Long(0) );
            pMaxG[i] = std::min( nVal + nTol, tools::Long(255) );

            nVal = static_cast<tools::Long>(pSrcCols[i].GetBlue());
            pMinB[i] = std::max( nVal - nTol, tools::Long(0) );
            pMaxB[i] = std::min( nVal + nTol, tools::Long(255) );

            pTrans[ i ] = (pDstCols[ i ] == COL_TRANSPARENT);
        }

        // Investigate actions and if necessary replace colors
        for( size_t nAct = 0, nActCount = rMtf.GetActionSize(); nAct < nActCount; nAct++ )
        {
            MetaAction* pAction = rMtf.GetAction( nAct );

            bool bReplace = false;

            switch( pAction->GetType() )
            {
                case MetaActionType::PIXEL:
                {
                    MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);

                    aCol = pAct->GetColor();
                    TEST_COLS();

                    if( bReplace )
                        pAct = new MetaPixelAction( pAct->GetPoint(), aCol );

                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::LINECOLOR:
                {
                    MetaLineColorAction* pAct = static_cast<MetaLineColorAction*>(pAction);

                    aCol = pAct->GetColor();
                    TEST_COLS();

                    if( bReplace )
                        pAct = new MetaLineColorAction( aCol, !pTrans[ i ] );

                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::FILLCOLOR:
                {
                    MetaFillColorAction* pAct = static_cast<MetaFillColorAction*>(pAction);

                    aCol = pAct->GetColor();
                    TEST_COLS();

                    if( bReplace )
                        pAct = new MetaFillColorAction( aCol, !pTrans[ i ] );

                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::TEXTCOLOR:
                {
                    MetaTextColorAction* pAct = static_cast<MetaTextColorAction*>(pAction);

                    aCol = pAct->GetColor();
                    TEST_COLS();

                    if( bReplace )
                        pAct = new MetaTextColorAction( aCol );

                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::TEXTFILLCOLOR:
                {
                    MetaTextFillColorAction* pAct = static_cast<MetaTextFillColorAction*>(pAction);

                    aCol = pAct->GetColor();
                    TEST_COLS();

                    if( bReplace )
                        pAct = new MetaTextFillColorAction( aCol, !pTrans[ i ] );

                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::FONT:
                {
                    MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
                    vcl::Font       aFont( pAct->GetFont() );

                    aCol = aFont.GetColor();
                    TEST_COLS();

                    if( bReplace )
                    {
                        aFont.SetColor( aCol );
                        pAct = new MetaFontAction( std::move(aFont) );
                    }

                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::WALLPAPER:
                {
                    MetaWallpaperAction*    pAct = static_cast<MetaWallpaperAction*>(pAction);
                    Wallpaper               aWall( pAct->GetWallpaper() );

                    aCol = aWall.GetColor();
                    TEST_COLS();

                    if( bReplace )
                    {
                        aWall.SetColor( aCol );
                        pAct = new MetaWallpaperAction( pAct->GetRect(), std::move(aWall) );
                    }

                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::BMP:
                {
                    MetaBmpAction*  pAct = static_cast<MetaBmpAction*>(pAction);
                    const Bitmap    aBmp( Mask(pAct->GetBitmap()).GetBitmap().CreateColorBitmap() );

                    pAct = new MetaBmpAction( pAct->GetPoint(), aBmp );
                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::BMPSCALE:
                {
                    MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
                    const Bitmap        aBmp( Mask(pAct->GetBitmap()).GetBitmap().CreateColorBitmap() );

                    pAct = new MetaBmpScaleAction( pAct->GetPoint(), pAct->GetSize(), aBmp );
                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::BMPSCALEPART:
                {
                    MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
                    const Bitmap            aBmp( Mask(pAct->GetBitmap()).GetBitmap().CreateColorBitmap() );

                    pAct = new MetaBmpScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
                                                       pAct->GetSrcPoint(), pAct->GetSrcSize(), aBmp );
                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::BMPEX:
                {
                    MetaBmpExAction*    pAct = static_cast<MetaBmpExAction*>(pAction);
                    const Bitmap        aBmp( Mask( pAct->GetBitmap() ).GetBitmap() );

                    pAct = new MetaBmpExAction( pAct->GetPoint(), aBmp );
                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::BMPEXSCALE:
                {
                    MetaBmpExScaleAction*   pAct = static_cast<MetaBmpExScaleAction*>(pAction);
                    const Bitmap            aBmp( Mask( pAct->GetBitmap() ).GetBitmap() );

                    pAct = new MetaBmpExScaleAction( pAct->GetPoint(), pAct->GetSize(), aBmp );
                    aMtf.AddAction( pAct );
                }
                break;

                case MetaActionType::BMPEXSCALEPART:
                {
                    MetaBmpExScalePartAction*   pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
                    const Bitmap                aBmp( Mask( pAct->GetBitmap() ).GetBitmap() );

                    pAct = new MetaBmpExScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
                                                         pAct->GetSrcPoint(), pAct->GetSrcSize(), aBmp );
                    aMtf.AddAction( pAct );
                }
                break;

                default:
                {
                    aMtf.AddAction( pAction );
                }
                break;
            }
        }
    }

    LeaveWait();

    return aMtf;
}


Animation SvxBmpMask::ImpReplaceTransparency( const Animation& rAnim, const Color& rColor )
{
    Animation   aAnimation( rAnim );
    sal_uInt16      nAnimationCount = aAnimation.Count();

    for( sal_uInt16 i = 0; i < nAnimationCount; i++ )
    {
        AnimationFrame aAnimationFrame(aAnimation.Get(i));
        aAnimationFrame.maBitmap.ReplaceTransparency(rColor);
        aAnimation.Replace(aAnimationFrame, i);
    }

    return aAnimation;
}


GDIMetaFile SvxBmpMask::ImpReplaceTransparency( const GDIMetaFile& rMtf, const Color& rColor )
{
    ScopedVclPtrInstance< VirtualDevice > pVDev;
    GDIMetaFile     aMtf;
    const MapMode&  rPrefMap = rMtf.GetPrefMapMode();
    const Size&     rPrefSize = rMtf.GetPrefSize();
    const size_t    nActionCount = rMtf.GetActionSize();

    pVDev->EnableOutput( false );
    aMtf.Record( pVDev );
    aMtf.SetPrefSize( rPrefSize );
    aMtf.SetPrefMapMode( rPrefMap );
    pVDev->SetLineColor( rColor );
    pVDev->SetFillColor( rColor );

    // retrieve one action at the time; first
    // set the whole area to the replacement color.
    pVDev->DrawRect( tools::Rectangle( rPrefMap.GetOrigin(), rPrefSize ) );
    for ( size_t i = 0; i < nActionCount; i++ )
    {
        MetaAction* pAct = rMtf.GetAction( i );
        aMtf.AddAction( pAct );
    }

    aMtf.Stop();
    aMtf.WindStart();

    return aMtf;
}

GDIMetaFile SvxBmpMask::GetMetaFile(const Graphic& rGraphic)
{
    // Replace transparency?
    if (m_xCbxTrans->get_active())
        return ImpReplaceTransparency(rGraphic.GetGDIMetaFile(), m_xLbColorTrans->GetSelectEntryColor());
    return ImpMask(rGraphic.GetGDIMetaFile());
}

Graphic SvxBmpMask::Mask( const Graphic& rGraphic )
{
    Graphic     aGraphic( rGraphic );
    const Color aReplColor( m_xLbColorTrans->GetSelectEntryColor() );

    switch( rGraphic.GetType() )
    {
        case GraphicType::Bitmap:
        {
            if( rGraphic.IsAnimated() )
            {
                // Replace transparency?
                if ( m_xCbxTrans->get_active() )
                    aGraphic = ImpReplaceTransparency( rGraphic.GetAnimation(), aReplColor );
                else
                    aGraphic = ImpMask( rGraphic.GetAnimation() );
            }
            else
            {
                // Replace transparency?
                if( m_xCbxTrans->get_active() )
                {
                    Bitmap aBmp = aGraphic.GetBitmap();
                    aBmp.ReplaceTransparency(aReplColor);
                    aGraphic = aBmp;
                }
                else
                {
                    Color   pSrcCols[4];
                    Color   pDstCols[4];
                    sal_uInt8   pTols[4];
                    sal_uInt16  nCount = InitColorArrays( pSrcCols, pDstCols, pTols );

                    if( nCount )
                    {
                        // first set all transparent colors
                        for( sal_uInt16 i = 0; i < nCount; i++ )
                        {
                            // Do we have a transparent color?
                            if (pDstCols[i] == COL_TRANSPARENT)
                            {
                                Bitmap    aBmp( ImpMaskTransparent( aGraphic.GetBitmap(),
                                                                        pSrcCols[ i ], pTols[ i ] ) );
                                const Size  aSize( aBmp.GetSizePixel() );

                                if( aSize.Width() && aSize.Height() )
                                    aGraphic = aBmp;
                            }
                        }

                        // now replace it again with the normal colors
                        Bitmap  aBitmap( aGraphic.GetBitmap() );
                        if ( aBitmap.GetSizePixel().Width() && aBitmap.GetSizePixel().Height() )
                        {
                            ImpMask( aBitmap );
                            aGraphic = Graphic( aBitmap );
                        }
                    }
                }
            }
        }
        break;

        case GraphicType::GdiMetafile:
        {
            GDIMetaFile aMtf(GetMetaFile(rGraphic));
            Size aSize( aMtf.GetPrefSize() );
            if ( aSize.Width() && aSize.Height() )
                aGraphic = Graphic( aMtf );
            else
                aGraphic = rGraphic;
        }
        break;

        default:
            aGraphic = rGraphic;
        break;
    }

    if( aGraphic != rGraphic )
    {
        aGraphic.SetPrefSize( rGraphic.GetPrefSize() );
        aGraphic.SetPrefMapMode( rGraphic.GetPrefMapMode() );
    }

    return aGraphic;
}

bool SvxBmpMask::IsEyedropping() const
{
    return m_xTbxPipette->get_item_active(u"pipette"_ustr);
}

/** Set an accessible name for the source color check boxes.  Without this
    the lengthy description is read.
*/
void SvxBmpMask::SetAccessibleNames()
{
    // set the accessible name for valueset
    OUString sColorPalette (SvxResId( RID_SVXDLG_BMPMASK_STR_PALETTE));
    OUString sColorPaletteN;

    sColorPaletteN = sColorPalette + " 1";
    m_xQSet1->SetText (sColorPaletteN);
    sColorPaletteN = sColorPalette + " 2";
    m_xQSet2->SetText (sColorPaletteN);
    sColorPaletteN = sColorPalette + " 3";
    m_xQSet3->SetText (sColorPaletteN);
    sColorPaletteN = sColorPalette + " 4";
    m_xQSet4->SetText (sColorPaletteN);
}

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