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



#include "wrapper.hxx"

#include <comphelper/processfactory.hxx>
#include <com/sun/star/awt/XMetricField.hpp>
#include <com/sun/star/awt/XNumericField.hpp>
#include <com/sun/star/awt/XTextComponent.hpp>
#include <com/sun/star/awt/XListBox.hpp>
#include <com/sun/star/awt/XComboBox.hpp>
#include <cppuhelper/implbase1.hxx>
#include <com/sun/star/awt/XActionListener.hpp>
#include <com/sun/star/awt/XItemListener.hpp>
#include <com/sun/star/awt/XMouseListener.hpp>
#include <vcl/combobox.hxx>
#include <vcl/lstbox.hxx>

#include <toolkit/awt/vclxwindows.hxx>

using namespace ::com::sun::star;
using rtl::OUString;

#define LAYOUT_API_CALLS_HANDLER 0

namespace layout
{

class EditImpl : public ControlImpl
               , public ::cppu::WeakImplHelper1< awt::XTextListener >
{
public:
    Link maModifyHdl;

    uno::Reference< awt::XTextComponent > mxEdit;
    EditImpl( Context *context, const PeerHandle &peer, Window *window )
        : ControlImpl( context, peer, window )
        , mxEdit( peer, uno::UNO_QUERY )
    {
    }

    ~EditImpl ();

    virtual void SAL_CALL disposing( lang::EventObject const& e )
        throw (uno::RuntimeException);

    virtual void SetModifyHdl( Link const& link );

    void SAL_CALL textChanged( const awt::TextEvent& /* rEvent */ )
        throw (uno::RuntimeException)
    {
        maModifyHdl.Call( mpWindow );
    }
};

EditImpl::~EditImpl ()
{
}

void SAL_CALL EditImpl::disposing( lang::EventObject const& e )
    throw (uno::RuntimeException)
{
    ControlImpl::disposing (e);
    mxEdit.clear ();
}

void EditImpl::SetModifyHdl( Link const& link )
{
    if (!link && !!maModifyHdl)
        mxEdit->removeTextListener( this );
    else if (!!link && !maModifyHdl)
        mxEdit->addTextListener( this );
    maModifyHdl = link;
}

Edit::~Edit ()
{
    SetModifyHdl (Link ());
}

void Edit::SetSelection( Selection const& rSelection )
{
#if LAYOUT_API_CALLS_HANDLER
    if ( !getImpl().mxEdit.is() )
        getImpl().mxEdit->setSelection( awt::Selection( rSelection.Min(), rSelection.Max() ) );
#else /* !LAYOUT_API_CALLS_HANDLER */
    GetEdit ()->SetSelection (rSelection);
#endif /* !LAYOUT_API_CALLS_HANDLER */
}

void Edit::SetText( OUString const& rStr )
{
#if LAYOUT_API_CALLS_HANDLER
    if ( getImpl().mxEdit.is() )
        /// this calls handlers; endless loop in numfmt.cxx
        getImpl().mxEdit->setText( rStr );
#else /* !LAYOUT_API_CALLS_HANDLER */
    GetEdit ()->SetText (rStr);
#endif /* !LAYOUT_API_CALLS_HANDLER */
}

String Edit::GetText() const
{
    if ( !getImpl().mxEdit.is() )
        return getImpl().mxEdit->getText();
    return OUString();
}

void Edit::SetModifyHdl( const Link& link )
{
    if (&getImpl () && getImpl().mxEdit.is ())
        getImpl().SetModifyHdl( link );
}

IMPL_CONSTRUCTORS( Edit, Control, "edit" );
IMPL_GET_IMPL( Edit );
IMPL_GET_WINDOW (Edit);

class MultiLineEditImpl : public EditImpl
{
public:
    MultiLineEditImpl( Context *context, const PeerHandle &peer, Window *window )
        : EditImpl( context, peer, window )
    {
    }
};

IMPL_CONSTRUCTORS( MultiLineEdit, Edit, "multilineedit" );
IMPL_GET_IMPL( MultiLineEdit );

class SpinFieldImpl : public EditImpl
{
  public:
    SpinFieldImpl( Context *context, const PeerHandle &peer, Window *window )
        : EditImpl( context, peer, window )
    {
    }
};

IMPL_CONSTRUCTORS( SpinField, Edit, "spinfield" );

class NumericFieldImpl : public SpinFieldImpl
{
  public:
    NumericFieldImpl( Context *context, const PeerHandle &peer, Window *window )
        : SpinFieldImpl( context, peer, window )
    {
    }
};

class MetricFieldImpl : public SpinFieldImpl
{
  public:
    MetricFieldImpl( Context *context, const PeerHandle &peer, Window *window )
        : SpinFieldImpl( context, peer, window )
    {
    }
};

IMPL_GET_IMPL( SpinField );
IMPL_GET_IMPL( NumericField );
IMPL_GET_IMPL( MetricField );

class FormatterBaseImpl
{
  protected:
    PeerHandle mpeer;
  public:
    explicit FormatterBaseImpl( const PeerHandle &peer )
        : mpeer( peer )
    {
    };
};

FormatterBase::FormatterBase( FormatterBaseImpl *pFormatImpl )
    : mpFormatImpl( pFormatImpl )
{
}

class NumericFormatterImpl : public FormatterBaseImpl
{
  public:
    uno::Reference< awt::XNumericField > mxField;
    explicit NumericFormatterImpl( const PeerHandle &peer )
        : FormatterBaseImpl( peer )
        , mxField( peer, uno::UNO_QUERY )
    {
    }

    // FIXME: burn that CPU ! cut/paste from vclxwindows.cxx
    double valueToDouble( sal_Int64 nValue )
    {
        sal_Int16 nDigits = mxField->getDecimalDigits();
        double n = (double)nValue;
        for ( sal_uInt16 d = 0; d < nDigits; d++ )
            n /= 10;
        return n;
    } // FIXME: burn that CPU ! cut/paste from vclxwindows.cxx
    sal_Int64 doubleToValue( double nValue )
    {
        sal_Int16 nDigits = mxField->getDecimalDigits();
        double n = nValue;
        for ( sal_uInt16 d = 0; d < nDigits; d++ )
            n *= 10;
        return (sal_Int64) n;
    }
};

class MetricFormatterImpl : public FormatterBaseImpl
{
  public:
    uno::Reference< awt::XMetricField > mxField;
    explicit MetricFormatterImpl( const PeerHandle &peer )
        : FormatterBaseImpl( peer )
        , mxField( peer, uno::UNO_QUERY )
    {
    }
};

NumericFormatter::NumericFormatter( FormatterBaseImpl *pImpl )
    : FormatterBase( pImpl )
{
}

NumericFormatterImpl& NumericFormatter::getFormatImpl() const
{
    return *( static_cast<NumericFormatterImpl *>( mpFormatImpl ) );
}

#define SET_IMPL(vclmethod, idlmethod) \
    void NumericFormatter::vclmethod( sal_Int64 nValue ) \
    { \
        if ( !getFormatImpl().mxField.is() ) \
            return; \
        getFormatImpl().mxField->idlmethod( getFormatImpl().valueToDouble( nValue ) ); \
    }

SET_IMPL( SetMin, setMin )
SET_IMPL( SetMax, setMax )
SET_IMPL( SetLast, setLast )
SET_IMPL( SetFirst, setFirst )
SET_IMPL( SetValue, setValue )
SET_IMPL( SetSpinSize, setSpinSize )

sal_Int64 NumericFormatter::GetValue() const
{
    if ( !getFormatImpl().mxField.is() )
        return 0;
    return getFormatImpl().doubleToValue( getFormatImpl().mxField->getValue() );
}

#undef SET_IMPL

IMPL_CONSTRUCTORS_2( NumericField, SpinField, NumericFormatter, "numericfield" );

MetricFormatter::MetricFormatter( FormatterBaseImpl *pImpl )
    : FormatterBase( pImpl )
{
}
MetricFormatterImpl& MetricFormatter::getFormatImpl() const
{    return *( static_cast<MetricFormatterImpl *>( mpFormatImpl ) );   }

#define MetricUnitVclToUno(a) ((sal_uInt16)(a))

#define SET_IMPL(vclmethod, idlmethod) \
    void MetricFormatter::vclmethod( sal_Int64 nValue, FieldUnit nUnit ) \
    { \
        if ( !getFormatImpl().mxField.is() ) \
            return; \
        getFormatImpl().mxField->idlmethod( nValue, MetricUnitVclToUno( nUnit ) ); \
    }

SET_IMPL( SetMin, setMin )
SET_IMPL( SetMax, setMax )
SET_IMPL( SetLast, setLast )
SET_IMPL( SetFirst, setFirst )
SET_IMPL( SetValue, setValue )

#undef SET_IMPL

void MetricFormatter::SetSpinSize( sal_Int64 nValue )
{
    if ( !getFormatImpl().mxField.is() )
        return;
    getFormatImpl().mxField->setSpinSize( nValue );
}

sal_Int64 MetricFormatter::GetValue( FieldUnit nUnit ) const
{
    if ( !getFormatImpl().mxField.is() )
        return 0;
    return getFormatImpl().mxField->getValue( MetricUnitVclToUno( nUnit ) );
}

IMPL_CONSTRUCTORS_2( MetricField, SpinField, MetricFormatter, "metricfield" );

class ComboBoxImpl : public EditImpl
                   , public ::cppu::WeakImplHelper1< awt::XActionListener >
                   , public ::cppu::WeakImplHelper1< awt::XItemListener >
{
public:
    uno::Reference< awt::XComboBox > mxComboBox;

    Link maClickHdl;
    Link maSelectHdl;

    Window *parent;

    ComboBoxImpl( Context *context, const PeerHandle &peer, Window *window )
        : EditImpl( context, peer, window )
        , mxComboBox( peer, uno::UNO_QUERY )
    {
    }

    ~ComboBoxImpl ();

    sal_uInt16 InsertEntry( OUString const& rStr, sal_uInt16 nPos )
    {
        if ( nPos == COMBOBOX_APPEND )
            nPos = GetEntryCount();
        mxComboBox->addItem( rtl::OUString( rStr ), nPos );
        return nPos;
    }

    void RemoveEntry( sal_uInt16 nPos )
    {
        mxComboBox->removeItems( nPos, 1 );
    }

    sal_uInt16 GetEntryPos( String const& rStr ) const
    {
        uno::Sequence< rtl::OUString> aItems( mxComboBox->getItems() );
        rtl::OUString rKey( rStr );
        sal_uInt16 n = sal::static_int_cast< sal_uInt16 >(aItems.getLength());
        for (sal_uInt16 i = 0; i < n; i++)
        {
            if ( aItems[ i ] == rKey )
                return i;
        }
        return COMBOBOX_ENTRY_NOTFOUND;
    }

    OUString GetEntry( sal_uInt16 nPos ) const
    {
        return OUString( mxComboBox->getItem( nPos ) );
    }

    sal_uInt16 GetEntryCount() const
    {
        return mxComboBox->getItemCount();
    }

    void SetClickHdl( Link const& link )
    {
        if (!link && !!maClickHdl)
            mxComboBox->removeActionListener( this );
        else if (!!link && !maClickHdl)
            mxComboBox->addActionListener( this );
        maClickHdl = link;
    }

    void SetSelectHdl( Link const& link )
    {
        if (!link && !!maSelectHdl)
            mxComboBox->removeItemListener( this );
        else if (!!link && !maSelectHdl)
            mxComboBox->addItemListener( this );
        maSelectHdl = link;
    }

    void SAL_CALL disposing( lang::EventObject const& e )
        throw (uno::RuntimeException);

    void SAL_CALL actionPerformed (const awt::ActionEvent&)
        throw (uno::RuntimeException)
    {
        ComboBox* pComboBox = static_cast<ComboBox*>( mpWindow );
        if ( !pComboBox )
            return;
        maClickHdl.Call( pComboBox );
    }

    void SAL_CALL itemStateChanged( awt::ItemEvent const&)
        throw (uno::RuntimeException)
    {
        ComboBox* pComboBox = static_cast<ComboBox*>( mpWindow );
        if ( !pComboBox )
            return;
        maSelectHdl.Call( pComboBox );
    }
};

ComboBox::~ComboBox ()
{
#ifndef __SUNPRO_CC
    OSL_TRACE ("%s: deleting ComboBox for window: %p", __FUNCTION__, GetWindow ());
#endif
}

ComboBoxImpl::~ComboBoxImpl ()
{
#ifndef __SUNPRO_CC
    OSL_TRACE ("%s: deleting ComboBoxImpl for window: %p", __FUNCTION__, mpWindow ? mpWindow->GetWindow () : 0);
    OSL_TRACE ("%s: deleting ComboBoxImpl for listener: %p", __FUNCTION__, static_cast<XFocusListener*> (this));
#endif
}

void ComboBoxImpl::disposing( lang::EventObject const& e )
    throw (uno::RuntimeException)
{
    EditImpl::disposing (e);
    mxComboBox.clear ();
}

sal_uInt16 ComboBox::InsertEntry( String const& rStr, sal_uInt16 nPos )
{
    return getImpl().InsertEntry( rStr, nPos );
}

void ComboBox::RemoveEntry( String const& rStr )
{
    getImpl().RemoveEntry( GetEntryPos( rStr ) );
}

void ComboBox::RemoveEntry( sal_uInt16 nPos )
{
    getImpl().RemoveEntry( nPos );
}

void ComboBox::Clear()
{
    uno::Sequence< rtl::OUString> aNoItems;
    getImpl().setProperty( "StringItemList", uno::Any( aNoItems ) );
}

sal_uInt16 ComboBox::GetEntryPos( String const& rStr ) const
{
    return getImpl().GetEntryPos( rStr );
}

String ComboBox::GetEntry( sal_uInt16 nPos ) const
{
    rtl::OUString rItem = getImpl().mxComboBox->getItem( nPos );
    return OUString( rItem );
}

sal_uInt16 ComboBox::GetEntryCount() const
{
    return getImpl().GetEntryCount();
}

void ComboBox::SetClickHdl( const Link& link )
{
    if (&getImpl () && getImpl().mxComboBox.is ())
        getImpl().SetClickHdl( link );
}

void ComboBox::SetSelectHdl( const Link& link )
{
    if (&getImpl () && getImpl().mxComboBox.is ())
        getImpl().SetSelectHdl( link );
}

void ComboBox::EnableAutocomplete (bool enable, bool matchCase)
{
    GetComboBox ()->EnableAutocomplete (enable, matchCase);
}

IMPL_CONSTRUCTORS_BODY( ComboBox, Edit, "combobox", getImpl().parent = parent; );
IMPL_GET_WINDOW (ComboBox);
/// IMPL_GET_IMPL( ComboBox );

static ComboBoxImpl* null_combobox_impl = 0;

ComboBoxImpl &ComboBox::getImpl () const
{
    if (ComboBoxImpl* c = static_cast<ComboBoxImpl *>(mpImpl))
        return *c;
    return *null_combobox_impl;
}

class ListBoxImpl : public ControlImpl
                  , public ::cppu::WeakImplHelper1< awt::XActionListener >
                  , public ::cppu::WeakImplHelper1< awt::XItemListener >
                  , public ::cppu::WeakImplHelper1< awt::XMouseListener >
{
    Link maClickHdl;
    Link maSelectHdl;
    Link maDoubleClickHdl;

public:
    uno::Reference< awt::XListBox > mxListBox;
    ListBoxImpl( Context *context, const PeerHandle &peer, Window *window )
        : ControlImpl( context, peer, window )
        , mxListBox( peer, uno::UNO_QUERY )
    {
        SelectEntryPos (0, true);
    }

    sal_uInt16 InsertEntry (String const& rStr, sal_uInt16 nPos)
    {
        if ( nPos == LISTBOX_APPEND )
            nPos = mxListBox->getItemCount();
        mxListBox->addItem( rtl::OUString( rStr ), nPos );
        return nPos;
    }

    void RemoveEntry( sal_uInt16 nPos )
    {
        mxListBox->removeItems( nPos, 1 );
    }

    sal_uInt16 RemoveEntry( String const& rStr, sal_uInt16 nPos)
    {
        if ( nPos == LISTBOX_APPEND )
            nPos = mxListBox->getItemCount();
        mxListBox->addItem( rtl::OUString( rStr ), nPos );
        return nPos;
    }

    sal_uInt16 GetEntryPos( String const& rStr ) const
    {
        uno::Sequence< rtl::OUString> aItems( mxListBox->getItems() );
        rtl::OUString rKey( rStr );
        sal_uInt16 n = sal::static_int_cast< sal_uInt16 >(aItems.getLength());
        for (sal_uInt16 i = 0; i < n; i++)
        {
            if ( aItems[ i ] == rKey )
                return i;
        }
        return LISTBOX_ENTRY_NOTFOUND;
    }

    OUString GetEntry( sal_uInt16 nPos ) const
    {
        return mxListBox->getItem( nPos );
    }

    sal_uInt16 GetEntryCount() const
    {
        return mxListBox->getItemCount();
    }

    void SelectEntryPos( sal_uInt16 nPos, bool bSelect )
    {
        mxListBox->selectItemPos( nPos, bSelect );
    }

    sal_uInt16 GetSelectEntryCount() const
    {
        return sal::static_int_cast< sal_uInt16 >( mxListBox->getSelectedItems().getLength() );
    }

    sal_uInt16 GetSelectEntryPos( sal_uInt16 nSelIndex ) const
    {
        sal_uInt16 nSelected = 0;
        if ( mxListBox->isMutipleMode() )
        {
            uno::Sequence< short > aItems( mxListBox->getSelectedItemsPos() );
            if ( nSelIndex < aItems.getLength() )
                nSelected = aItems[ nSelIndex ];
        }
        else
            nSelected = mxListBox->getSelectedItemPos();
        return nSelected;
    }

    virtual void SAL_CALL disposing( lang::EventObject const& e )
        throw (uno::RuntimeException)
    {
        ControlImpl::disposing (e);
        mxListBox.clear ();
    }

    Link& GetClickHdl ()
    {
        return maClickHdl;
    }

    void SetClickHdl( Link const& link )
    {
        if (!link && !!maClickHdl)
            mxListBox->removeActionListener( this );
        else if (!!link && !maClickHdl)
            mxListBox->addActionListener( this );
        maClickHdl = link;
    }

    void SAL_CALL actionPerformed( const awt::ActionEvent& /* rEvent */ )
        throw (uno::RuntimeException)
    {
        maClickHdl.Call( mpWindow );
    }

    Link& GetSelectHdl ()
    {
        return maSelectHdl;
    }

    void SetSelectHdl( Link const& link )
    {
        if (!link && !!maSelectHdl)
            mxListBox->removeItemListener( this );
        else if (!!link && !maSelectHdl)
            mxListBox->addItemListener( this );
        maSelectHdl = link;
    }

    void SAL_CALL itemStateChanged (awt::ItemEvent const&)
        throw (uno::RuntimeException)
    {
        maSelectHdl.Call (static_cast <ListBox*> (mpWindow));
    }

    Link& GetDoubleClickHdl ()
    {
        return maDoubleClickHdl;
    }

    void SetDoubleClickHdl (Link const& link)
    {
        if (!link && !!maDoubleClickHdl)
            mxWindow->removeMouseListener (this);
        else if (!!link && !maSelectHdl)
            mxWindow->addMouseListener (this);
        maDoubleClickHdl = link;
    }

    void SAL_CALL mousePressed (awt::MouseEvent const&) throw (uno::RuntimeException)
    {
    }
    void SAL_CALL mouseReleased (awt::MouseEvent const& e) throw (uno::RuntimeException)
    {
        if (e.ClickCount == 2)
            maDoubleClickHdl.Call (mpWindow);
    }
    void SAL_CALL mouseEntered (awt::MouseEvent const&) throw (uno::RuntimeException)
    {
    }
    void SAL_CALL mouseExited (awt::MouseEvent const&) throw (uno::RuntimeException)
    {
    }
};

ListBox::~ListBox ()
{
}

sal_uInt16 ListBox::InsertEntry (String const& rStr, sal_uInt16 nPos)
{
    return getImpl().InsertEntry(rStr, nPos);
}

void ListBox::RemoveEntry( sal_uInt16 nPos )
{
    return getImpl().RemoveEntry( nPos );
}

void ListBox::RemoveEntry( String const& rStr )
{
    return getImpl().RemoveEntry( GetEntryPos( rStr ) );
}

void ListBox::Clear()
{
    uno::Sequence< rtl::OUString> aNoItems;
    getImpl().setProperty( "StringItemList", uno::Any( aNoItems ) );
}

sal_uInt16 ListBox::GetEntryPos( String const& rStr ) const
{
    return getImpl().GetEntryPos( rStr );
}

String ListBox::GetEntry( sal_uInt16 nPos ) const
{
    return getImpl().GetEntry( nPos );
}

sal_uInt16 ListBox::GetEntryCount() const
{
    return getImpl().GetEntryCount();
}

void ListBox::SelectEntryPos( sal_uInt16 nPos, bool bSelect )
{
#if LAYOUT_API_CALLS_HANDLER
    getImpl().SelectEntryPos( nPos, bSelect );
#else /* !LAYOUT_API_CALLS_HANDLER */
    GetListBox ()->SelectEntryPos (nPos, bSelect);
#endif /* !LAYOUT_API_CALLS_HANDLER */
}

void ListBox::SelectEntry( String const& rStr, bool bSelect )
{
    SelectEntryPos( GetEntryPos( rStr ), bSelect );
}

sal_uInt16 ListBox::GetSelectEntryCount() const
{
    return getImpl().GetSelectEntryCount();
}

sal_uInt16 ListBox::GetSelectEntryPos( sal_uInt16 nSelIndex ) const
{
    return getImpl().GetSelectEntryPos( nSelIndex );
}

String ListBox::GetSelectEntry( sal_uInt16 nSelIndex ) const
{
    return GetEntry( GetSelectEntryPos( nSelIndex ) );
}

Link& ListBox::GetSelectHdl ()
{
    return getImpl ().GetSelectHdl ();
}

void ListBox::SetSelectHdl( Link const& link )
{
    getImpl().SetSelectHdl( link );
}

Link& ListBox::GetClickHdl ()
{
    return getImpl ().GetSelectHdl ();
}

void ListBox::SetClickHdl( Link const& link )
{
    if (&getImpl () && getImpl().mxListBox.is ())
        getImpl().SetClickHdl( link );
}

Link& ListBox::GetDoubleClickHdl ()
{
    return getImpl ().GetSelectHdl ();
}

void ListBox::SetDoubleClickHdl( Link const& link )
{
    getImpl().SetDoubleClickHdl( link );
}

void ListBox::SetEntryData( sal_uInt16 pos, void* data)
{
    GetListBox ()->SetEntryData (pos, data);
}

void* ListBox::GetEntryData( sal_uInt16 pos) const
{
    return GetListBox ()->GetEntryData (pos);
}

void ListBox::SetNoSelection ()
{
    GetListBox ()->SetNoSelection ();
}

IMPL_CONSTRUCTORS (ListBox, Control, "listbox");
IMPL_GET_IMPL (ListBox);
IMPL_GET_WINDOW (ListBox);

IMPL_IMPL (MultiListBox, ListBox)
IMPL_CONSTRUCTORS_BODY( MultiListBox, ListBox, "multilistbox", GetMultiListBox()->EnableMultiSelection( true ); );
IMPL_GET_IMPL( MultiListBox );
IMPL_GET_WINDOW( MultiListBox );
} // namespace layout
