/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_chart2.hxx"

#include "tp_RangeChooser.hxx"
#include "tp_RangeChooser.hrc"
#include "Strings.hrc"
#include "ResId.hxx"
#include "macros.hxx"
#include "NoWarningThisInCTOR.hxx"
#include "DataSourceHelper.hxx"
#include "DiagramHelper.hxx"
#include "ChartTypeTemplateProvider.hxx"
#include "DialogModel.hxx"
#include "RangeSelectionHelper.hxx"
#include <com/sun/star/awt/XTopWindow.hpp>
#include <com/sun/star/embed/EmbedStates.hpp>
#include <com/sun/star/embed/XComponentSupplier.hpp>

namespace
{
void lcl_ShowChooserButton(
    ::chart::RangeSelectionButton & rChooserButton,
    Edit & rEditField,
    sal_Bool bShow )
{
    if( rChooserButton.IsVisible() != bShow )
    {
        rChooserButton.Show( bShow );
        sal_Int32 nWidhtDiff = 10 + 2;
        if( bShow )
            nWidhtDiff = -nWidhtDiff;
        Size aSize = rChooserButton.PixelToLogic( rEditField.GetSizePixel(), MAP_APPFONT );
        aSize.setWidth( aSize.getWidth() + nWidhtDiff );
        rEditField.SetSizePixel( rChooserButton.LogicToPixel( aSize, MAP_APPFONT ));
    }
}
void lcl_enableRangeChoosing( bool bEnable, Dialog * pDialog )
{
    if( pDialog )
    {
        pDialog->Show( bEnable ? sal_False : sal_True );
        pDialog->SetModalInputMode( bEnable ? sal_False : sal_True );
    }
}
void lcl_shiftControlY( Control & rControl, long nYOffset )
{
    Point aPos( rControl.GetPosPixel());
    aPos.setY( aPos.getY() + nYOffset );
    rControl.SetPosPixel( aPos );
}
} // anonymous namespace

//.............................................................................
namespace chart
{
//.............................................................................
using namespace ::com::sun::star;
using namespace ::com::sun::star::chart2;

using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Sequence;


RangeChooserTabPage::RangeChooserTabPage( Window* pParent
        , DialogModel & rDialogModel
        , ChartTypeTemplateProvider* pTemplateProvider
        , Dialog * pParentDialog
        , bool bHideDescription /* = false */ )

        : OWizardPage( pParent, SchResId(TP_RANGECHOOSER) )

        , m_aFT_Caption( this, SchResId( FT_CAPTION_FOR_WIZARD ) )
        , m_aFT_Range( this, SchResId( FT_RANGE ) )
        , m_aED_Range( this, SchResId( ED_RANGE ) )
        , m_aIB_Range( this, SchResId( IB_RANGE ) )
        , m_aRB_Rows( this, SchResId( RB_DATAROWS ) )
        , m_aRB_Columns( this, SchResId( RB_DATACOLS ) )
        , m_aCB_FirstRowAsLabel( this, SchResId( CB_FIRST_ROW_ASLABELS ) )
        , m_aCB_FirstColumnAsLabel( this, SchResId( CB_FIRST_COLUMN_ASLABELS ) )
        , m_nChangingControlCalls(0)
        , m_bIsDirty(false)
        , m_xDataProvider( 0 )
        , m_aLastValidRangeString()
        , m_xCurrentChartTypeTemplate(0)
        , m_pTemplateProvider(pTemplateProvider)
        , m_rDialogModel( rDialogModel )
        , m_pParentDialog( pParentDialog )
        , m_pTabPageNotifiable( dynamic_cast< TabPageNotifiable * >( pParentDialog ))
{
    FreeResource();

    if( bHideDescription )
    {
        // note: the offset should be a negative value for shifting upwards, the
        // 4 is for the offset difference between a wizard page and a tab-page
        long nYOffset = - ( m_aFT_Range.GetPosPixel().getY() - m_aFT_Caption.GetPosPixel().getY() + 4 );
        m_aFT_Caption.Hide();

        // shift all controls by the offset space saved by hiding the caption
        lcl_shiftControlY( m_aFT_Range, nYOffset );
        lcl_shiftControlY( m_aED_Range, nYOffset );
        lcl_shiftControlY( m_aIB_Range, nYOffset );
        lcl_shiftControlY( m_aRB_Rows, nYOffset );
        lcl_shiftControlY( m_aRB_Columns, nYOffset );
        lcl_shiftControlY( m_aCB_FirstRowAsLabel, nYOffset );
        lcl_shiftControlY( m_aCB_FirstColumnAsLabel, nYOffset );
    }
    else
    {
        // make font of caption bold
        Font aFont( m_aFT_Caption.GetControlFont() );
        aFont.SetWeight( WEIGHT_BOLD );
        m_aFT_Caption.SetControlFont( aFont );

        // no mnemonic
        m_aFT_Caption.SetStyle( m_aFT_Caption.GetStyle() | WB_NOLABEL );
    }

    this->SetText( String(SchResId(STR_PAGE_DATA_RANGE)) );
    m_aIB_Range.SetQuickHelpText( String(SchResId(STR_TIP_SELECT_RANGE)) );

    // set defaults as long as DetectArguments does not work
    m_aRB_Columns.Check();
    m_aCB_FirstColumnAsLabel.Check();
    m_aCB_FirstRowAsLabel.Check();

    // BM: Note, that the range selection is not available, if there is no view.
    // This happens for charts having their own embedded spreadsheet.  If you
    // force to get the range selection here, this would mean when entering this
    // page the calc view would be created in this case.  So, I enable the
    // button here, and in the worst case nothing happens when it is pressed.
    // Not nice, but I see no better solution for the moment.
    m_aIB_Range.SetClickHdl( LINK( this, RangeChooserTabPage, ChooseRangeHdl ));
    m_aED_Range.SetKeyInputHdl( LINK( this, RangeChooserTabPage, ChooseRangeHdl ));

    // #i75179# enable setting the background to a different color
    m_aED_Range.SetStyle( m_aED_Range.GetStyle() | WB_FORCECTRLBACKGROUND );

    m_aED_Range.SetUpdateDataHdl( LINK( this, RangeChooserTabPage, ControlChangedHdl ));
    m_aED_Range.SetModifyHdl( LINK( this, RangeChooserTabPage, ControlEditedHdl ));
    m_aRB_Rows.SetToggleHdl( LINK( this, RangeChooserTabPage, ControlChangedHdl ) );
    m_aCB_FirstRowAsLabel.SetToggleHdl( LINK( this, RangeChooserTabPage, ControlChangedHdl ) );
    m_aCB_FirstColumnAsLabel.SetToggleHdl( LINK( this, RangeChooserTabPage, ControlChangedHdl ) );
}

RangeChooserTabPage::~RangeChooserTabPage()
{
}

void RangeChooserTabPage::ActivatePage()
{
    OWizardPage::ActivatePage();
    initControlsFromModel();
}

void RangeChooserTabPage::initControlsFromModel()
{
    m_nChangingControlCalls++;

    if(m_pTemplateProvider)
    {
        m_xCurrentChartTypeTemplate = m_pTemplateProvider->getCurrentTemplate();
    }

    bool bUseColumns = ! m_aRB_Rows.IsChecked();
    bool bFirstCellAsLabel = bUseColumns ? m_aCB_FirstRowAsLabel.IsChecked() : m_aCB_FirstColumnAsLabel.IsChecked();
    bool bHasCategories = bUseColumns ? m_aCB_FirstColumnAsLabel.IsChecked() : m_aCB_FirstRowAsLabel.IsChecked();

    bool bIsValid = m_rDialogModel.allArgumentsForRectRangeDetected();
    if( bIsValid )
        m_rDialogModel.detectArguments(
            m_aLastValidRangeString, bUseColumns, bFirstCellAsLabel, bHasCategories );
    else
        m_aLastValidRangeString = String::EmptyString();

    m_aED_Range.SetText( m_aLastValidRangeString );

    m_aRB_Rows.Check( !bUseColumns );
    m_aRB_Columns.Check(  bUseColumns );

    m_aCB_FirstRowAsLabel.Check( m_aRB_Rows.IsChecked()?bHasCategories:bFirstCellAsLabel  );
    m_aCB_FirstColumnAsLabel.Check( m_aRB_Columns.IsChecked()?bHasCategories:bFirstCellAsLabel  );

    isValid();

    m_nChangingControlCalls--;
}

void RangeChooserTabPage::DeactivatePage()
{
    commitPage();
    svt::OWizardPage::DeactivatePage();
}

void RangeChooserTabPage::commitPage()
{
    commitPage(::svt::WizardTypes::eFinish);
}

sal_Bool RangeChooserTabPage::commitPage( ::svt::WizardTypes::CommitPageReason /*eReason*/ )
{
    //ranges may have been edited in the meanwhile (dirty is true in that case here)
    if( isValid() )
    {
        changeDialogModelAccordingToControls();
        return sal_True;//return false if this page should not be left
    }
    else
        return sal_False;
}

void RangeChooserTabPage::changeDialogModelAccordingToControls()
{
    if(m_nChangingControlCalls>0)
        return;

    if( !m_xCurrentChartTypeTemplate.is() )
    {
        if(m_pTemplateProvider)
            m_xCurrentChartTypeTemplate.set( m_pTemplateProvider->getCurrentTemplate());
        if( !m_xCurrentChartTypeTemplate.is())
        {
            OSL_ENSURE( false, "Need a template to change data source" );
            return;
        }
    }

    if( m_bIsDirty )
    {
        sal_Bool bFirstCellAsLabel = ( m_aCB_FirstColumnAsLabel.IsChecked() && !m_aRB_Columns.IsChecked() )
            || ( m_aCB_FirstRowAsLabel.IsChecked()    && !m_aRB_Rows.IsChecked() );
        sal_Bool bHasCategories = ( m_aCB_FirstColumnAsLabel.IsChecked() && m_aRB_Columns.IsChecked() )
            || ( m_aCB_FirstRowAsLabel.IsChecked()    && m_aRB_Rows.IsChecked() );

        Sequence< beans::PropertyValue > aArguments(
            DataSourceHelper::createArguments(
                m_aRB_Columns.IsChecked(), bFirstCellAsLabel, bHasCategories ) );

        // only if range is valid
        if( m_aLastValidRangeString.equals( m_aED_Range.GetText()))
        {
            m_rDialogModel.setTemplate( m_xCurrentChartTypeTemplate );
            aArguments.realloc( aArguments.getLength() + 1 );
            aArguments[aArguments.getLength() - 1] =
                beans::PropertyValue( C2U("CellRangeRepresentation"), -1,
                                      uno::makeAny( m_aLastValidRangeString ),
                                      beans::PropertyState_DIRECT_VALUE );
            m_rDialogModel.setData( aArguments );
            m_bIsDirty = false;
        }

        //@todo warn user that the selected range is not valid
        //@todo better: disable OK-Button if range is invalid
    }
}

bool RangeChooserTabPage::isValid()
{
    ::rtl::OUString aRange( m_aED_Range.GetText());
    sal_Bool bFirstCellAsLabel = ( m_aCB_FirstColumnAsLabel.IsChecked() && !m_aRB_Columns.IsChecked() )
        || ( m_aCB_FirstRowAsLabel.IsChecked()    && !m_aRB_Rows.IsChecked() );
    sal_Bool bHasCategories = ( m_aCB_FirstColumnAsLabel.IsChecked() && m_aRB_Columns.IsChecked() )
        || ( m_aCB_FirstRowAsLabel.IsChecked()    && m_aRB_Rows.IsChecked() );
    bool bIsValid = aRange.isEmpty() ||
        m_rDialogModel.getRangeSelectionHelper()->verifyArguments(
            DataSourceHelper::createArguments(
                aRange, Sequence< sal_Int32 >(), m_aRB_Columns.IsChecked(), bFirstCellAsLabel, bHasCategories ));

    if( bIsValid )
    {
        m_aED_Range.SetControlForeground();
        m_aED_Range.SetControlBackground();
        if( m_pTabPageNotifiable )
            m_pTabPageNotifiable->setValidPage( this );
        m_aLastValidRangeString = aRange;
    }
    else
    {
        m_aED_Range.SetControlBackground( RANGE_SELECTION_INVALID_RANGE_BACKGROUND_COLOR );
        m_aED_Range.SetControlForeground( RANGE_SELECTION_INVALID_RANGE_FOREGROUND_COLOR );
        if( m_pTabPageNotifiable )
            m_pTabPageNotifiable->setInvalidPage( this );
    }

    // enable/disable controls
    // #i79531# if the range is valid but an action of one of these buttons
    // would render it invalid, the button should be disabled
    if( bIsValid )
    {
        bool bDataInColumns = m_aRB_Columns.IsChecked();
        bool bIsSwappedRangeValid = m_rDialogModel.getRangeSelectionHelper()->verifyArguments(
            DataSourceHelper::createArguments(
                aRange, Sequence< sal_Int32 >(), ! bDataInColumns, bHasCategories, bFirstCellAsLabel ));
        m_aRB_Rows.Enable( bIsSwappedRangeValid );
        m_aRB_Columns.Enable( bIsSwappedRangeValid );

        m_aCB_FirstRowAsLabel.Enable(
            m_rDialogModel.getRangeSelectionHelper()->verifyArguments(
                DataSourceHelper::createArguments(
                    aRange, Sequence< sal_Int32 >(), m_aRB_Columns.IsChecked(),
                    bDataInColumns ? ! bFirstCellAsLabel : bFirstCellAsLabel,
                    bDataInColumns ? bHasCategories : ! bHasCategories )));
        m_aCB_FirstColumnAsLabel.Enable(
            m_rDialogModel.getRangeSelectionHelper()->verifyArguments(
                DataSourceHelper::createArguments(
                    aRange, Sequence< sal_Int32 >(), m_aRB_Columns.IsChecked(),
                    bDataInColumns ? bFirstCellAsLabel : ! bFirstCellAsLabel,
                    bDataInColumns ? ! bHasCategories : bHasCategories )));
    }
    else
    {
        m_aRB_Rows.Enable( bIsValid );
        m_aRB_Columns.Enable( bIsValid );
        m_aCB_FirstRowAsLabel.Enable( bIsValid );
        m_aCB_FirstColumnAsLabel.Enable( bIsValid );
}
    sal_Bool bShowIB = m_rDialogModel.getRangeSelectionHelper()->hasRangeSelection();
    lcl_ShowChooserButton( m_aIB_Range, m_aED_Range, bShowIB );

    return bIsValid;
}

IMPL_LINK( RangeChooserTabPage, ControlEditedHdl, void*, EMPTYARG )
{
    setDirty();
    isValid();
    return 0;
}

IMPL_LINK( RangeChooserTabPage, ControlChangedHdl, void*, EMPTYARG )
{
    setDirty();
    if( isValid())
        changeDialogModelAccordingToControls();
    return 0;
}

IMPL_LINK( RangeChooserTabPage, ChooseRangeHdl, void *, EMPTYARG )
{
    rtl::OUString aRange = m_aED_Range.GetText();
    // using assignment for broken gcc 3.3
    rtl::OUString aTitle = String( SchResId( STR_PAGE_DATA_RANGE ) );

    lcl_enableRangeChoosing( true, m_pParentDialog );
    m_rDialogModel.getRangeSelectionHelper()->chooseRange( aRange, aTitle, *this );

    return 0;
}


void RangeChooserTabPage::listeningFinished( const ::rtl::OUString & rNewRange )
{
    //user has selected a new range

    rtl::OUString aRange( rNewRange );

    m_rDialogModel.startControllerLockTimer();

    // stop listening
    m_rDialogModel.getRangeSelectionHelper()->stopRangeListening();

    //update dialog state
    ToTop();
    GrabFocus();
    m_aED_Range.SetText( String( aRange ) );
    m_aED_Range.GrabFocus();

    setDirty();
    if( isValid())
        changeDialogModelAccordingToControls();

    lcl_enableRangeChoosing( false, m_pParentDialog );
}
void RangeChooserTabPage::disposingRangeSelection()
{
    m_rDialogModel.getRangeSelectionHelper()->stopRangeListening( false );
}

void RangeChooserTabPage::setDirty()
{
    if( m_nChangingControlCalls == 0 )
        m_bIsDirty = true;
}

//.............................................................................
} //namespace chart
//.............................................................................
