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

#include <svl/undo.hxx>

#include <rangelst.hxx>
#include <docsh.hxx>
#include <document.hxx>
#include <scresid.hxx>
#include <tabvwsh.hxx>

#include <StatisticsTwoVariableDialog.hxx>

ScStatisticsTwoVariableDialog::ScStatisticsTwoVariableDialog(
                    SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
                    weld::Window* pParent, ScViewData& rViewData, const OUString& rUIXMLDescription, const OUString& rID)
    : ScAnyRefDlgController(pSfxBindings, pChildWindow, pParent, rUIXMLDescription, rID)
    , mxVariable1RangeLabel(m_xBuilder->weld_label(u"variable1-range-label"_ustr))
    , mxVariable1RangeEdit(new formula::RefEdit(m_xBuilder->weld_entry(u"variable1-range-edit"_ustr)))
    , mxVariable1RangeButton(new formula::RefButton(m_xBuilder->weld_button(u"variable1-range-button"_ustr)))
    , mxVariable2RangeLabel(m_xBuilder->weld_label(u"variable2-range-label"_ustr))
    , mxVariable2RangeEdit(new formula::RefEdit(m_xBuilder->weld_entry(u"variable2-range-edit"_ustr)))
    , mxVariable2RangeButton(new formula::RefButton(m_xBuilder->weld_button(u"variable2-range-button"_ustr)))
    , mxOutputRangeLabel(m_xBuilder->weld_label(u"output-range-label"_ustr))
    , mxOutputRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry(u"output-range-edit"_ustr)))
    , mxOutputRangeButton(new formula::RefButton(m_xBuilder->weld_button(u"output-range-button"_ustr)))
    , mViewData(rViewData)
    , mDocument(rViewData.GetDocument())
    , mVariable1Range(ScAddress::INITIALIZE_INVALID)
    , mVariable2Range(ScAddress::INITIALIZE_INVALID)
    , mAddressDetails(mDocument.GetAddressConvention(), 0, 0 )
    , mOutputAddress(ScAddress::INITIALIZE_INVALID)
    , mGroupedBy(GroupedBy::BY_COLUMN)
    , mxButtonOk(m_xBuilder->weld_button(u"ok"_ustr))
    , mxButtonCancel(m_xBuilder->weld_button(u"cancel"_ustr))
    , mxGroupByColumnsRadio(m_xBuilder->weld_radio_button(u"groupedby-columns-radio"_ustr))
    , mxGroupByRowsRadio(m_xBuilder->weld_radio_button(u"groupedby-rows-radio"_ustr))
    , mpActiveEdit(nullptr)
    , mCurrentAddress(rViewData.GetCurX(), rViewData.GetCurY(), rViewData.CurrentTabForData() )
    , mDialogLostFocus(false)
{
    mxVariable1RangeEdit->SetReferences(this, mxVariable1RangeLabel.get());
    mxVariable1RangeButton->SetReferences(this, mxVariable1RangeEdit.get());

    mxVariable2RangeEdit->SetReferences(this, mxVariable2RangeLabel.get());
    mxVariable2RangeButton->SetReferences(this, mxVariable2RangeEdit.get());

    mxOutputRangeEdit->SetReferences(this, mxOutputRangeLabel.get());
    mxOutputRangeButton->SetReferences(this, mxOutputRangeEdit.get());

    Init();
    GetRangeFromSelection();
}

ScStatisticsTwoVariableDialog::~ScStatisticsTwoVariableDialog()
{
}

void ScStatisticsTwoVariableDialog::Init()
{
    mxButtonCancel->connect_clicked( LINK( this, ScStatisticsTwoVariableDialog, ButtonClicked ) );
    mxButtonOk->connect_clicked( LINK( this, ScStatisticsTwoVariableDialog, ButtonClicked ) );
    mxButtonOk->set_sensitive(false);

    Link<formula::RefEdit&,void> aEditLink = LINK( this, ScStatisticsTwoVariableDialog, GetEditFocusHandler );
    mxVariable1RangeEdit->SetGetFocusHdl( aEditLink );
    mxVariable2RangeEdit->SetGetFocusHdl( aEditLink );
    mxOutputRangeEdit->SetGetFocusHdl( aEditLink );

    Link<formula::RefButton&,void> aButtonLink = LINK( this, ScStatisticsTwoVariableDialog, GetButtonFocusHandler );
    mxVariable1RangeButton->SetGetFocusHdl( aButtonLink );
    mxVariable2RangeButton->SetGetFocusHdl( aButtonLink );
    mxOutputRangeButton->SetGetFocusHdl( aButtonLink );

    aEditLink = LINK( this, ScStatisticsTwoVariableDialog, LoseEditFocusHandler );
    mxVariable1RangeEdit->SetLoseFocusHdl( aEditLink );
    mxVariable2RangeEdit->SetLoseFocusHdl( aEditLink );
    mxOutputRangeEdit->SetLoseFocusHdl( aEditLink );

    aButtonLink = LINK( this, ScStatisticsTwoVariableDialog, LoseButtonFocusHandler );
    mxVariable1RangeButton->SetLoseFocusHdl( aButtonLink );
    mxVariable2RangeButton->SetLoseFocusHdl( aButtonLink );
    mxOutputRangeButton->SetLoseFocusHdl( aButtonLink );

    Link<formula::RefEdit&,void> aLink2 = LINK( this, ScStatisticsTwoVariableDialog, RefInputModifyHandler);
    mxVariable1RangeEdit->SetModifyHdl( aLink2);
    mxVariable2RangeEdit->SetModifyHdl( aLink2);
    mxOutputRangeEdit->SetModifyHdl( aLink2);

    mxOutputRangeEdit->GrabFocus();

    mxGroupByColumnsRadio->connect_toggled( LINK( this, ScStatisticsTwoVariableDialog, GroupByChanged ) );
    mxGroupByRowsRadio->connect_toggled( LINK( this, ScStatisticsTwoVariableDialog, GroupByChanged ) );

    mxGroupByColumnsRadio->set_active(true);
    mxGroupByRowsRadio->set_active(false);
}

void ScStatisticsTwoVariableDialog::GetRangeFromSelection()
{
    OUString aCurrentString;

    ScRange aCurrentRange;
    mViewData.GetSimpleArea(aCurrentRange);

    if (aCurrentRange.aEnd.Col() - aCurrentRange.aStart.Col() == 1)
    {
        mVariable1Range = aCurrentRange;
        mVariable1Range.aEnd.SetCol(mVariable1Range.aStart.Col());
        aCurrentString = mVariable1Range.Format(mDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
        mxVariable1RangeEdit->SetText(aCurrentString);

        mVariable2Range = aCurrentRange;
        mVariable2Range.aStart.SetCol(mVariable2Range.aEnd.Col());
        aCurrentString = mVariable2Range.Format(mDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
        mxVariable2RangeEdit->SetText(aCurrentString);
    }
    else
    {
        mVariable1Range = aCurrentRange;
        aCurrentString = mVariable1Range.Format(mDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
        mxVariable1RangeEdit->SetText(aCurrentString);
    }
}

void ScStatisticsTwoVariableDialog::SetActive()
{
    if ( mDialogLostFocus )
    {
        mDialogLostFocus = false;
        if( mpActiveEdit )
            mpActiveEdit->GrabFocus();
    }
    else
    {
        m_xDialog->grab_focus();
    }
    RefInputDone();
}

void ScStatisticsTwoVariableDialog::SetReference( const ScRange& rReferenceRange, ScDocument& rDocument )
{
    if ( mpActiveEdit != nullptr )
    {
        if ( rReferenceRange.aStart != rReferenceRange.aEnd )
            RefInputStart( mpActiveEdit );

        OUString aReferenceString;

        if ( mpActiveEdit == mxVariable1RangeEdit.get() )
        {
            mVariable1Range = rReferenceRange;
            aReferenceString = mVariable1Range.Format(rDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
            mxVariable1RangeEdit->SetRefString(aReferenceString);
        }
        else if ( mpActiveEdit == mxVariable2RangeEdit.get() )
        {
            mVariable2Range = rReferenceRange;
            aReferenceString = mVariable2Range.Format(rDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
            mxVariable2RangeEdit->SetRefString(aReferenceString);
        }
        else if ( mpActiveEdit == mxOutputRangeEdit.get() )
        {
            mOutputAddress = rReferenceRange.aStart;

            ScRefFlags nFormat = ( mOutputAddress.Tab() == mCurrentAddress.Tab() ) ?
                                                             ScRefFlags::ADDR_ABS :
                                                             ScRefFlags::ADDR_ABS_3D;
            aReferenceString = mOutputAddress.Format(nFormat, &rDocument, rDocument.GetAddressConvention());
            mxOutputRangeEdit->SetRefString( aReferenceString );
        }
    }

    ValidateDialogInput();
}

IMPL_LINK( ScStatisticsTwoVariableDialog, ButtonClicked, weld::Button&, rButton, void )
{
    if (&rButton == mxButtonOk.get())
    {
        CalculateInputAndWriteToOutput();
        response(RET_OK);
    }
    else
        response(RET_CANCEL);
}

IMPL_LINK(ScStatisticsTwoVariableDialog, GetEditFocusHandler, formula::RefEdit&, rCtrl, void)
{
    mpActiveEdit = nullptr;
    if (&rCtrl == mxVariable1RangeEdit.get())
    {
        mpActiveEdit = mxVariable1RangeEdit.get();
    }
    else if (&rCtrl == mxVariable2RangeEdit.get())
    {
        mpActiveEdit = mxVariable2RangeEdit.get();
    }
    else if (&rCtrl == mxOutputRangeEdit.get())
    {
        mpActiveEdit = mxOutputRangeEdit.get();
    }

    if( mpActiveEdit )
        mpActiveEdit->SelectAll();
}

IMPL_LINK( ScStatisticsTwoVariableDialog, GetButtonFocusHandler, formula::RefButton&, rCtrl, void )
{
    mpActiveEdit = nullptr;
    if (&rCtrl == mxVariable1RangeButton.get())
    {
        mpActiveEdit = mxVariable1RangeEdit.get();
    }
    else if (&rCtrl == mxVariable2RangeButton.get())
    {
        mpActiveEdit = mxVariable2RangeEdit.get();
    }
    else if (&rCtrl == mxOutputRangeButton.get())
    {
        mpActiveEdit = mxOutputRangeEdit.get();
    }

    if( mpActiveEdit )
        mpActiveEdit->SelectAll();
}

IMPL_LINK_NOARG( ScStatisticsTwoVariableDialog, LoseEditFocusHandler, formula::RefEdit&, void )
{
    mDialogLostFocus = !m_xDialog->has_toplevel_focus();
}

IMPL_LINK_NOARG( ScStatisticsTwoVariableDialog, LoseButtonFocusHandler, formula::RefButton&, void )
{
    mDialogLostFocus = !m_xDialog->has_toplevel_focus();
}

IMPL_LINK_NOARG(ScStatisticsTwoVariableDialog, GroupByChanged, weld::Toggleable&, void)
{
    if (mxGroupByColumnsRadio->get_active())
        mGroupedBy = GroupedBy::BY_COLUMN;
    else if (mxGroupByRowsRadio->get_active())
        mGroupedBy = GroupedBy::BY_ROW;

    ValidateDialogInput();
}

IMPL_LINK_NOARG( ScStatisticsTwoVariableDialog, RefInputModifyHandler, formula::RefEdit&, void )
{
    if ( mpActiveEdit )
    {
        if (mpActiveEdit == mxVariable1RangeEdit.get())
        {
            ScRangeList aRangeList;
            bool bValid = ParseWithNames( aRangeList, mxVariable1RangeEdit->GetText(), mDocument);
            const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
            if (pRange)
            {
                mVariable1Range = *pRange;
                // Highlight the resulting range.
                mxVariable1RangeEdit->StartUpdateData();
            }
            else
            {
                mVariable1Range = ScRange( ScAddress::INITIALIZE_INVALID);
            }
        }
        else if ( mpActiveEdit == mxVariable2RangeEdit.get() )
        {
            ScRangeList aRangeList;
            bool bValid = ParseWithNames( aRangeList, mxVariable2RangeEdit->GetText(), mDocument);
            const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
            if (pRange)
            {
                mVariable2Range = *pRange;
                // Highlight the resulting range.
                mxVariable2RangeEdit->StartUpdateData();
            }
            else
            {
                mVariable2Range = ScRange( ScAddress::INITIALIZE_INVALID);
            }
        }
        else if ( mpActiveEdit == mxOutputRangeEdit.get() )
        {
            ScRangeList aRangeList;
            bool bValid = ParseWithNames( aRangeList, mxOutputRangeEdit->GetText(), mDocument);
            const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
            if (pRange)
            {
                mOutputAddress = pRange->aStart;

                // Crop output range to top left address for Edit field.
                if (pRange->aStart != pRange->aEnd)
                {
                    ScRefFlags nFormat = ( mOutputAddress.Tab() == mCurrentAddress.Tab() ) ?
                                                                     ScRefFlags::ADDR_ABS :
                                                                     ScRefFlags::ADDR_ABS_3D;
                    OUString aReferenceString = mOutputAddress.Format(nFormat, &mDocument, mDocument.GetAddressConvention());
                    mxOutputRangeEdit->SetRefString( aReferenceString );
                }

                // Highlight the resulting range.
                mxOutputRangeEdit->StartUpdateData();
            }
            else
            {
                mOutputAddress = ScAddress( ScAddress::INITIALIZE_INVALID);
            }
        }
    }

    ValidateDialogInput();
}

void ScStatisticsTwoVariableDialog::CalculateInputAndWriteToOutput()
{
    OUString aUndo(ScResId(GetUndoNameId()));
    ScDocShell* pDocShell = mViewData.GetDocShell();
    SfxUndoManager* pUndoManager = pDocShell->GetUndoManager();
    pUndoManager->EnterListAction( aUndo, aUndo, 0, mViewData.GetViewShell()->GetViewShellId() );

    ScRange aOutputRange = ApplyOutput(pDocShell);

    pUndoManager->LeaveListAction();
    pDocShell->PostPaint( aOutputRange, PaintPartFlags::Grid );
}

bool ScStatisticsTwoVariableDialog::InputRangesValid()
{
    return mVariable1Range.IsValid() && mVariable2Range.IsValid() && mOutputAddress.IsValid();
}

void ScStatisticsTwoVariableDialog::ValidateDialogInput()
{
    // Enable OK button if all inputs are ok.
    mxButtonOk->set_sensitive(InputRangesValid());
}

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