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

#include <comphelper/lok.hxx>
#include <osl/file.hxx>
#include <sfx2/app.hxx>
#include <sfx2/event.hxx>
#include <sfx2/frame.hxx>
#include <sfx2/viewfrm.hxx>
#include <sot/formats.hxx>
#include <sfx2/sfxsids.hrc>
#include <svl/macitem.hxx>
#include <ucbhelper/content.hxx>
#include <cuihyperdlg.hxx>
#include <hltpbase.hxx>
#include <macroass.hxx>
#include <svx/svxdlg.hxx>
#include <strings.hrc>
#include <dialmgr.hxx>
#include <bitmaps.hlst>

namespace {

OUString CreateUiNameFromURL( const OUString& aStrURL )
{
    OUString          aStrUiURL;
    INetURLObject   aURLObj( aStrURL );

    switch(aURLObj.GetProtocol())
    {
        case INetProtocol::File:
            osl::FileBase::getSystemPathFromFileURL(aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), aStrUiURL);
            break;
        case INetProtocol::Ftp :
            {
                //remove password from name
                INetURLObject   aTmpURL(aURLObj);
                aTmpURL.SetPass(u"");
                aStrUiURL = aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
            }
            break;
        default :
            {
                aStrUiURL = aURLObj.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);
            }
    }
    if(aStrUiURL.isEmpty())
        return aStrURL;
    return aStrUiURL;
}

}

// ComboBox-Control for URL's with History and Autocompletion
SvxHyperURLBox::SvxHyperURLBox(std::unique_ptr<weld::ComboBox> xControl)
    : SvtURLBox(std::move(xControl))
    , DropTargetHelper(getWidget()->get_drop_target())
{
    SetSmartProtocol(INetProtocol::Http);
}

sal_Int8 SvxHyperURLBox::AcceptDrop( const AcceptDropEvent& /* rEvt */ )
{
    return IsDropFormatSupported( SotClipboardFormatId::STRING ) ? DND_ACTION_COPY : DND_ACTION_NONE;
}

sal_Int8 SvxHyperURLBox::ExecuteDrop( const ExecuteDropEvent& rEvt )
{
    TransferableDataHelper  aDataHelper( rEvt.maDropEvent.Transferable );
    OUString                aString;
    sal_Int8                nRet = DND_ACTION_NONE;

    if( aDataHelper.GetString( SotClipboardFormatId::STRING, aString ) )
    {
        set_entry_text(aString);
        nRet = DND_ACTION_COPY;
    }

    return nRet;
}

//# Hyperlink-Dialog: Tabpages-Baseclass                                 #

SvxHyperlinkTabPageBase::SvxHyperlinkTabPageBase(weld::Container* pParent,
                                                 SvxHpLinkDlg* pDlg,
                                                 const OUString& rUIXMLDescription,
                                                 const OUString& rID,
                                                 const SfxItemSet* pItemSet)
  : IconChoicePage(pParent, rUIXMLDescription, rID, pItemSet)
  , mxCbbFrame(xBuilder->weld_combo_box(u"frame"_ustr))
  , mxLbForm(xBuilder->weld_combo_box(u"form"_ustr))
  , mxEdIndication(xBuilder->weld_entry(u"indication"_ustr))
  , mxEdText(xBuilder->weld_entry(u"name"_ustr))
  , mxBtScript(xBuilder->weld_button(u"script"_ustr))
  , mxFormLabel(xBuilder->weld_label(u"form_label"_ustr))
  , mxFrameLabel(xBuilder->weld_label(u"frame_label"_ustr))
  , mbIsCloseDisabled( false )
  , mpDialog( pDlg )
  , mbStdControlsInit( false )
  , maTimer("cui SvxHyperlinkTabPageBase maTimer")
{
    // create bookmark-window
}

SvxHyperlinkTabPageBase::~SvxHyperlinkTabPageBase()
{
    maTimer.Stop();

    HideMarkWnd();
}

bool SvxHyperlinkTabPageBase::QueryClose()
{
    return !mbIsCloseDisabled;
}

void SvxHyperlinkTabPageBase::InitStdControls ()
{
    if ( !mbStdControlsInit )
    {
        SfxDispatcher* pDispatch = GetDispatcher();
        SfxViewFrame* pViewFrame = pDispatch ? pDispatch->GetFrame() : nullptr;
        SfxFrame* pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr;
        if ( pFrame )
        {
            TargetList aList;
            SfxFrame::GetDefaultTargetList(aList);
            if( !aList.empty() )
            {
                size_t nCount = aList.size();
                size_t i;
                for ( i = 0; i < nCount; i++ )
                {
                    mxCbbFrame->append_text( aList.at( i ) );
                }
            }
        }

        mxBtScript->set_from_icon_name(RID_SVXBMP_SCRIPT);

        mxBtScript->connect_clicked ( LINK ( this, SvxHyperlinkTabPageBase, ClickScriptHdl_Impl ) );
    }

    mbStdControlsInit = true;
}

// Move Extra-Window
void SvxHyperlinkTabPageBase::MoveToExtraWnd( Point aNewPos )
{
    mxMarkWnd->MoveTo(aNewPos);
}

// Show Extra-Window
void SvxHyperlinkTabPageBase::ShowMarkWnd()
{
    if (mxMarkWnd)
    {
        mxMarkWnd->getDialog()->present();
        return;
    }

    weld::Dialog* pDialog = mpDialog->getDialog();

    mxMarkWnd = std::make_shared<SvxHlinkDlgMarkWnd>(pDialog, this);

    // Size of dialog-window in screen pixels
    Point aDlgPos(pDialog->get_position());
    Size aDlgSize(pDialog->get_size());

    // Absolute size of the screen
    ::tools::Rectangle aScreen(pDialog->get_monitor_workarea());

    // Size of Extrawindow
    Size aExtraWndSize(mxMarkWnd->getDialog()->get_preferred_size());

    // mxMarkWnd is a child of mpDialog, so coordinates for positioning must be relative to mpDialog
    if( aDlgPos.X()+(1.05*aDlgSize.Width())+aExtraWndSize.Width() > aScreen.Right() )
    {
        if( aDlgPos.X() - ( 0.05*aDlgSize.Width() ) - aExtraWndSize.Width() < 0 )
        {
            // Pos Extrawindow anywhere
            MoveToExtraWnd( Point(10,10) );  // very unlikely
        }
        else
        {
            // Pos Extrawindow on the left side of Dialog
            MoveToExtraWnd( Point(0,0) - Point( tools::Long(0.05*aDlgSize.Width()), 0 ) - Point( aExtraWndSize.Width(), 0 ) );
        }
    }
    else
    {
        // Pos Extrawindow on the right side of Dialog
        MoveToExtraWnd ( Point( tools::Long(1.05*aDlgSize.getWidth()), 0 ) );
    }

    // Set size of Extra-Window
    mxMarkWnd->getDialog()->set_size_request(aExtraWndSize.Width(), aDlgSize.Height());

    weld::DialogController::runAsync(mxMarkWnd, [this](sal_Int32 /*nResult*/) { mxMarkWnd.reset(); } );
}

void SvxHyperlinkTabPageBase::HideMarkWnd()
{
    if (mxMarkWnd)
    {
        mxMarkWnd->response(RET_CANCEL);
        mxMarkWnd.reset();
    }
}

// Fill Dialogfields
void SvxHyperlinkTabPageBase::FillStandardDlgFields ( const SvxHyperlinkItem* pHyperlinkItem )
{
    if (!comphelper::LibreOfficeKit::isActive())
    {
        // Frame
        sal_Int32 nPos = mxCbbFrame->find_text(pHyperlinkItem->GetTargetFrame());
        if (nPos != -1)
            mxCbbFrame->set_active(nPos);

        // Form
        OUString aStrFormText = CuiResId( RID_CUISTR_HYPERDLG_FROM_TEXT );

        OUString aStrFormButton = CuiResId( RID_CUISTR_HYPERDLG_FORM_BUTTON );

        if( pHyperlinkItem->GetInsertMode() & HLINK_HTMLMODE )
        {
            mxLbForm->clear();
            mxLbForm->append_text( aStrFormText );
            mxLbForm->set_active( 0 );
        }
        else
        {
            mxLbForm->clear();
            mxLbForm->append_text( aStrFormText );
            mxLbForm->append_text( aStrFormButton );
            mxLbForm->set_active( pHyperlinkItem->GetInsertMode() == HLINK_BUTTON ? 1 : 0 );
        }
    }
    else
    {
        mxCbbFrame->hide();
        mxLbForm->hide();
        mxFormLabel->hide();
        mxFrameLabel->hide();
    }

    // URL
    mxEdIndication->set_text( pHyperlinkItem->GetName() );

    // Name
    mxEdText->set_text( pHyperlinkItem->GetIntName() );

    // Script-button
    if (!comphelper::LibreOfficeKit::isActive())
    {
        if ( pHyperlinkItem->GetMacroEvents() == HyperDialogEvent::NONE )
            mxBtScript->set_sensitive(false);
        else
            mxBtScript->set_sensitive(true);
    }
    else
    {
        mxBtScript->hide();
    }
}

// Any action to do after apply-button is pressed
void SvxHyperlinkTabPageBase::DoApply ()
{
    // default-implementation : do nothing
}

// This method would be called from bookmark-window to set new mark-string
void SvxHyperlinkTabPageBase::SetMarkStr ( const OUString& /*aStrMark*/ )
{
    // default-implementation : do nothing
}

// Set initial focus
void SvxHyperlinkTabPageBase::SetInitFocus()
{
    xContainer->grab_focus();
}

// retrieve dispatcher
SfxDispatcher* SvxHyperlinkTabPageBase::GetDispatcher() const
{
    return mpDialog->GetDispatcher();
}

void SvxHyperlinkTabPageBase::DisableClose(bool _bDisable)
{
    mbIsCloseDisabled = _bDisable;
    if (mbIsCloseDisabled)
        maBusy.incBusy(mpDialog->getDialog());
    else
        maBusy.decBusy();
}

// Click on imagebutton : Script
IMPL_LINK_NOARG(SvxHyperlinkTabPageBase, ClickScriptHdl_Impl, weld::Button&, void)
{
    SvxHyperlinkItem *pHyperlinkItem = const_cast<SvxHyperlinkItem*>(
                                       GetItemSet().GetItem (SID_HYPERLINK_GETLINK));

    if (!pHyperlinkItem || pHyperlinkItem->GetMacroEvents() == HyperDialogEvent::NONE)
        return;

    // get macros from itemset
    const SvxMacroTableDtor* pMacroTbl = pHyperlinkItem->GetMacroTable();
    SvxMacroItem aItem ( SID_ATTR_MACROITEM );
    if( pMacroTbl )
        aItem.SetMacroTable( *pMacroTbl );

    // create empty itemset for macro-dlg
    auto xItemSet = std::make_unique<SfxItemSetFixed<SID_ATTR_MACROITEM, SID_ATTR_MACROITEM>>( SfxGetpApp()->GetPool() );
    xItemSet->Put( aItem );

    DisableClose( true );

    SfxMacroAssignDlg aDlg(mpDialog->getDialog(), mxDocumentFrame, std::move(xItemSet));

    // add events
    SfxMacroTabPage *pMacroPage = aDlg.GetTabPage();

    if ( pHyperlinkItem->GetMacroEvents() & HyperDialogEvent::MouseOverObject )
        pMacroPage->AddEvent( CuiResId(RID_CUISTR_HYPDLG_MACROACT1),
                              SvMacroItemId::OnMouseOver );
    if ( pHyperlinkItem->GetMacroEvents() & HyperDialogEvent::MouseClickObject )
        pMacroPage->AddEvent( CuiResId(RID_CUISTR_HYPDLG_MACROACT2),
                              SvMacroItemId::OnClick);
    if ( pHyperlinkItem->GetMacroEvents() & HyperDialogEvent::MouseOutObject )
        pMacroPage->AddEvent( CuiResId(RID_CUISTR_HYPDLG_MACROACT3),
                              SvMacroItemId::OnMouseOut);
    // execute dlg
    short nRet = aDlg.run();
    DisableClose( false );
    if ( RET_OK == nRet )
    {
        const SfxItemSet* pOutSet = aDlg.GetOutputItemSet();
        const SfxPoolItem* pItem;
        if( SfxItemState::SET == pOutSet->GetItemState( SID_ATTR_MACROITEM, false, &pItem ))
        {
            pHyperlinkItem->SetMacroTable( static_cast<const SvxMacroItem*>(pItem)->GetMacroTable() );
        }
    }
}

// Get Macro-Infos
HyperDialogEvent SvxHyperlinkTabPageBase::GetMacroEvents() const
{
    const SvxHyperlinkItem *pHyperlinkItem =
                                       GetItemSet().GetItem (SID_HYPERLINK_GETLINK);

    return pHyperlinkItem ? pHyperlinkItem->GetMacroEvents() : HyperDialogEvent();
}

SvxMacroTableDtor* SvxHyperlinkTabPageBase::GetMacroTable()
{
    const SvxHyperlinkItem *pHyperlinkItem =
                                       GetItemSet().GetItem (SID_HYPERLINK_GETLINK);

    return const_cast<SvxMacroTableDtor*>(pHyperlinkItem->GetMacroTable());
}

// try to detect the current protocol that is used in rStrURL
OUString SvxHyperlinkTabPageBase::GetSchemeFromURL( const OUString& rStrURL )
{
    OUString aStrScheme;

    INetURLObject aURL( rStrURL );
    INetProtocol aProtocol = aURL.GetProtocol();

    // our new INetUrlObject now has the ability
    // to detect if a Url is valid or not :-(
    if ( aProtocol == INetProtocol::NotValid )
    {
        if ( rStrURL.startsWithIgnoreAsciiCase( INET_HTTP_SCHEME ) )
        {
            aStrScheme = INET_HTTP_SCHEME;
        }
        else if ( rStrURL.startsWithIgnoreAsciiCase( INET_HTTPS_SCHEME ) )
        {
            aStrScheme = INET_HTTPS_SCHEME;
        }
        else if ( rStrURL.startsWithIgnoreAsciiCase( INET_FTP_SCHEME ) )
        {
            aStrScheme = INET_FTP_SCHEME;
        }
        else if ( rStrURL.startsWithIgnoreAsciiCase( INET_MAILTO_SCHEME ) )
        {
            aStrScheme = INET_MAILTO_SCHEME;
        }
    }
    else
        aStrScheme = INetURLObject::GetScheme( aProtocol );
    return aStrScheme;
}

void SvxHyperlinkTabPageBase::GetDataFromCommonFields( OUString& aStrName,
                                             OUString& aStrIntName, OUString& aStrFrame,
                                             SvxLinkInsertMode& eMode )
{
    aStrIntName = mxEdText->get_text();
    aStrName    = mxEdIndication->get_text();
    aStrFrame   = mxCbbFrame->get_active_text();

    sal_Int32 nPos = mxLbForm->get_active();
    if (nPos == -1)
        // This happens when FillStandardDlgFields() hides mpLbForm.
        nPos = 0;
    eMode = static_cast<SvxLinkInsertMode>(nPos + 1);

    // Ask dialog whether the current doc is a HTML-doc
    if (mpDialog->IsHTMLDoc())
        eMode = static_cast<SvxLinkInsertMode>( sal_uInt16(eMode) | HLINK_HTMLMODE );
}

// reset dialog-fields
void SvxHyperlinkTabPageBase::Reset( const SfxItemSet& rItemSet )
{

    // Set dialog-fields from create-itemset
    maStrInitURL.clear();

    const SvxHyperlinkItem *pHyperlinkItem =
                                       rItemSet.GetItem (SID_HYPERLINK_GETLINK);

    if ( pHyperlinkItem )
    {
        // set dialog-fields
        FillStandardDlgFields (pHyperlinkItem);

        // set all other fields
        FillDlgFields(pHyperlinkItem->GetURL());

        // Store initial URL
        maStrInitURL = pHyperlinkItem->GetURL();
    }
}

// Fill output-ItemSet
bool SvxHyperlinkTabPageBase::FillItemSet( SfxItemSet* rOut)
{
    OUString aStrURL, aStrName, aStrIntName, aStrFrame;
    SvxLinkInsertMode eMode;

    GetCurrentItemData ( aStrURL, aStrName, aStrIntName, aStrFrame, eMode);
    if ( aStrName.isEmpty() ) //automatically create a visible name if the link is created without name
        aStrName = CreateUiNameFromURL(aStrURL);

    HyperDialogEvent nEvents = GetMacroEvents();
    SvxMacroTableDtor* pTable = GetMacroTable();

    SvxHyperlinkItem aItem( SID_HYPERLINK_SETLINK, aStrName, aStrURL, aStrFrame,
                            aStrIntName, eMode, nEvents, pTable );
    rOut->Put (aItem);

    return true;
}

// Activate / Deactivate Tabpage
void SvxHyperlinkTabPageBase::ActivatePage( const SfxItemSet& rItemSet )
{

    // Set dialog-fields from input-itemset
    const SvxHyperlinkItem *pHyperlinkItem =
                                       rItemSet.GetItem (SID_HYPERLINK_GETLINK);

    if ( pHyperlinkItem )
    {
        // standard-fields
        FillStandardDlgFields (pHyperlinkItem);
    }

    // show mark-window if it was open before
    if ( ShouldOpenMarkWnd () )
        ShowMarkWnd ();
}

DeactivateRC SvxHyperlinkTabPageBase::DeactivatePage( SfxItemSet* _pSet)
{
    // hide mark-wnd
    SetMarkWndShouldOpen( IsMarkWndVisible () );
    HideMarkWnd ();

    // retrieve data of dialog
    OUString aStrURL, aStrName, aStrIntName, aStrFrame;
    SvxLinkInsertMode eMode;

    GetCurrentItemData ( aStrURL, aStrName, aStrIntName, aStrFrame, eMode);

    HyperDialogEvent nEvents = GetMacroEvents();
    SvxMacroTableDtor* pTable = GetMacroTable();

    if( _pSet )
    {
        SvxHyperlinkItem aItem( SID_HYPERLINK_GETLINK, aStrName, aStrURL, aStrFrame,
                                aStrIntName, eMode, nEvents, pTable );
        _pSet->Put( aItem );
    }

    return DeactivateRC::LeavePage;
}

bool SvxHyperlinkTabPageBase::ShouldOpenMarkWnd()
{
    return false;
}

void SvxHyperlinkTabPageBase::SetMarkWndShouldOpen(bool)
{
}

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