/***************************************************************************
 *   Copyright (C) 2003-2005 by liuspider                                  *
 *   liuspider@users.sourceforge.net                                       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/
#define USE_SCIM_KDE_SETTINGS
#include "inputwindow.h"

#include "scimlookuptable.h"
#include "scimlineedit.h"
#include "scimdragableframe.h"
#include "src/skimglobalactions.h"
#include "src/extra_utils.h"

#include <qlineedit.h>
#include "scimstringlistitem.h"
#include <kdebug.h>
#include <kiconloader.h>

#define TOP_LEVEL_WFLAGS \
   Qt::WStyle_Customize|Qt::WStyle_NoBorder|Qt::WStyle_StaysOnTop|Qt::WX11BypassWM|Qt::WStyle_Tool

inputWindow::inputWindow( SkimPluginManager* is, QWidget* parent, const char* name, WFlags fl )
    : ScimDragableFrame( parent, parent, name, fl ), inTransaction(false)
{
    setKeepVisible( TRUE );
    allModules = is;
    allModules->registerSpecialProperyObject(this);

    InputServer = allModules->getInputServer();

    setFrameStyle( QFrame::NoFrame );

    ScreenRect = QApplication::desktop()->screenGeometry();
    setCaption(i18n("Input Window"));
    setName( "inputWindow" );
    setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) );
    inputWindowLayout = new QVBoxLayout( this, 0, 1, "inputWindowLayout");
    inputWindowLayout->setResizeMode( QLayout::FreeResize );

    PreEditText = new ScimLineEdit( this, "PreEditText" );
    PreEditText->hide();
    PreEditText->setBackgroundOrigin( QLineEdit::AncestorOrigin );
    PreEditText->setMouseTracking( FALSE );
    PreEditText->setAcceptDrops( FALSE );
    inputWindowLayout->addWidget( PreEditText );

    AuxText = new ScimStringListItem(this);
    AuxText->setFrameStyle( QFrame::LineEditPanel | QFrame::Raised );

    inputWindowLayout->addWidget( AuxText );
    AuxText->hide();

    LookupLbl = new ScimLookupTable( this, inputWindowLayout, "LookupLbl",
      ScimKdeSettings::lookupTable_IsVertical());

    allModules->registerSpecialProperyObject(LookupLbl);

    LookupLbl->setCaption(i18n("Candidates Window (Lookup Table)"));

    LookupLbl->setFrameStyle( QFrame::StyledPanel | QFrame::Raised );

    inputWindowLayout->addWidget( LookupLbl );

    languageChange();
    clearWState( WState_Polished );

    // signals and slots connections
    connect( InputServer, SIGNAL( updatePreeditStringReq(const QString &, const scim::AttributeList &) ),
        this, SLOT( updatePreeditString(const QString &, const scim::AttributeList & ) ) );
    connect( InputServer, 
             SIGNAL(updateLookupTableReq(const scim::LookupTable &, size_t &)),
             SLOT( updateLookupTable(const scim::LookupTable &, size_t & ) ) );
    connect( InputServer, SIGNAL( updateAuxStringReq(const QString &, const scim::AttributeList & ) ),
        this, SLOT(updateAuxString(const QString &, const scim::AttributeList & ) ) );

    connect( InputServer, SIGNAL( showAuxStringReq() ), this, SLOT( showAuxString() ) );
    connect( InputServer, SIGNAL( hideAuxStringReq() ), this, SLOT( hideAuxString() ) );
    connect( InputServer, SIGNAL( showLookupTableReq() ), this, SLOT( showLookupTable() ) );
    connect( InputServer, SIGNAL( showPreeditStringReq() ), this, SLOT( showPreeditString() ) );
    connect( InputServer, SIGNAL( hidePreeditStringReq() ), this, SLOT( hidePreeditString() ) );
    connect( InputServer, SIGNAL( hideLookupTableReq() ),
        this, SLOT( hideLookupTable() ) );
    connect( InputServer, SIGNAL( updateSpotLocationReq( int, int ) ),
        this, SLOT( updateSpotLocation( int, int ) ) );
    connect( InputServer, SIGNAL(updatePreeditCaretReq(int ) ),
        PreEditText, SLOT(setCursorPosition ( int )));

    connect( InputServer, SIGNAL( turnOffPanelReq() ), this, SLOT( hideInputWindow() ) );

    connect( InputServer, SIGNAL( transaction_start() ), this, SLOT( disableUpdates() ) );
    connect( InputServer, SIGNAL( transaction_end() ), this, SLOT( enableUpdates() ) );

    connect( LookupLbl, SIGNAL( previousPageRequested() ), InputServer, SLOT( lookupTablePageUp() ) );
    connect( LookupLbl, SIGNAL( nextPageRequested() ), InputServer, SLOT( lookupTablePageDown() ) );
    connect( LookupLbl, SIGNAL(itemSelected(int )), InputServer, SLOT(selectLookupTableItem(int )));

    StickAction =
        static_cast<KToggleAction*>(allModules->getActionCollection()->action( "stick_inputwindow" ));

    changeSetting();

    LookupLbl->hide();
}

void inputWindow::disableUpdates()
{
    inTransaction = true;
    setUpdatesEnabled( false );
//     LookupLbl->setUpdatesEnabled( false );
}

void inputWindow::enableUpdates()
{
    setUpdatesEnabled( true );
//     LookupLbl->setUpdatesEnabled( true );
    if(!LookupLbl->isAttached() && !isVisible() && !sticky)
    {
        LookupLbl->move(insertPointX, insertPointY);
        LookupLbl->adjustSize();
    }

    inTransaction = false;

    try_hide();

    adjustSize();
}

inputWindow::~inputWindow(){
    ScimKdeSettings::setIs_Sticky(sticky);
    ScimKdeSettings::setEmbedded_Lookup_Table(LookupLbl->isAttached());
}

void inputWindow::hideInputWindow ()
{
    hide();

    if(!LookupLbl->isAttached())
        LookupLbl->hide();
}

void inputWindow::toggleStick()
{
    StickAction->setIcon( StickAction->isChecked() ? "pin_down": "pin_up" );
    sticky = StickAction->isChecked();
}

void inputWindow::changeSetting()
{
//     kdDebug(18010) << k_lineinfo << " inputWindow::changeSetting()\n";
    if(!ScimKdeSettings::iW_Font().isEmpty())
    {
      QFont customFont;
      customFont.fromString(ScimKdeSettings::iW_Font());
      if(customFont != font())
        setFont(customFont);
    } else
      unsetFont();

    LookupLbl->setVertical(ScimKdeSettings::lookupTable_IsVertical());

    if(ScimKdeSettings::embedded_Lookup_Table() != LookupLbl->isAttached())
        LookupLbl->switchMode();

    if(ScimKdeSettings::lookupTable_IsVertical() &&  !ScimKdeSettings::lookupTable_MinimumWidth())
        LookupLbl->setMinimumWidth(100);
    else
        LookupLbl->setMinimumWidth(0);

    sticky = ScimKdeSettings::is_Sticky();
    StickAction->setChecked(sticky);
    toggleStick();

    UPDATE_WINDOW_OPACITY(this);
}

void inputWindow::languageChange()
{
}

void inputWindow::updateAuxString( const QString & aux, const scim::AttributeList & attrs)
{
    AuxText->setText( aux, attrs );
}

void inputWindow::showAuxString()
{
    AuxText->show();
    if( !isVisible() )
        show();
    adjustSize();
}

void inputWindow::hideAuxString()
{
    AuxText->hide();
    try_hide();
}

void inputWindow::updateSpotLocation( int x, int y )
{
    insertPointX = x; insertPointY = y;

    if(sticky)
        return;

    move(insertPointX, insertPointY);
//     adjustSize();
}
void inputWindow::updatePreeditString( const QString & newprestr, const scim::AttributeList & atl)
{
    PreEditText->setText( newprestr, atl );
//     adjustSize();
}

void inputWindow::hidePreeditString()
{
    PreEditText->hide();
    try_hide();
}

void inputWindow::showPreeditString()
{
    PreEditText->show();
    if( !isVisible() )
      show();
    adjustSize();
}

void inputWindow::updateLookupTable(const scim::LookupTable &table, size_t & exact_item_num)
{
    size_t i;
    size_t item_num = table.get_current_page_size ();

    scim::String         mbs;
    scim::WideString     wcs, label;
    scim::AttributeList  attrs;
    QValueList<scim::AttributeList> attrslist;
    QStringList    items, labels;
    for (i = 0; i < SCIM_LOOKUP_TABLE_MAX_PAGESIZE; ++ i) {
        if (i < item_num) {
            mbs = scim::String ();

            wcs = table.get_candidate_in_current_page (i);

            label =  table.get_candidate_label (i);

            labels <<  QString::fromUtf8( scim::utf8_wcstombs (label).c_str() );

            mbs = scim::utf8_wcstombs (wcs);

            // Update attributes;
            attrs = table.get_attributes_in_current_page (i);
            attrslist.push_back(attrs);
            items << QString::fromUtf8( mbs.c_str() );
        }
    }

    //FIXME: the line below should be called after updateContent, but now it's a ugly hack
    //to push_front a highlight attribute to the attrslist
    LookupLbl->updateHighlight( table.is_cursor_visible () ? table.get_cursor_pos_in_current_page () : -1 );

    exact_item_num = LookupLbl->updateContent(labels, items, attrslist, table.is_page_size_fixed ());
    LookupLbl->enablePreviousPage(table.get_current_page_start ());
    LookupLbl->enableNextPage(table.get_current_page_start () + exact_item_num < table.number_of_candidates ());

    LookupLbl->adjustSize();
//     adjustSize();
}

bool inputWindow::can_hide (void)
{
    if (PreEditText->isVisible() ||
        AuxText->isVisible() ||
        (LookupLbl->isAttached() && LookupLbl->isVisible()))
        return false;
    return true;
}

void inputWindow::try_hide()
{
    if(!inTransaction)
        if(can_hide())
            hide();
}

void inputWindow::adjustSize()
{
    if(inTransaction)
        return;

    ScimDragableFrame::adjustSize();

    if(isVisible() && !LookupLbl->isAttached() && !sticky) {
        std::vector<QRect> places;
        QRect candidate;
        {
            //find all possible locations where lookuptable can be placed
            candidate = LookupLbl->frameGeometry();
            candidate.moveTopLeft(QPoint(insertPointX, frameGeometry().bottomLeft().y() + 5)); //below
            places.push_back(candidate);
            candidate = LookupLbl->frameGeometry();
            candidate.moveTopLeft(QPoint(frameGeometry().topRight().x() + 5, insertPointY)); //right
            places.push_back(candidate);
            candidate = LookupLbl->frameGeometry();
            candidate.moveTopRight(QPoint(frameGeometry().topLeft().x() - 5, insertPointY)); //left
            places.push_back(candidate);
        }

        for(uint i=0; i<places.size(); i++)
        {
            //this will verify that candidate is in the screen, if not, try next condidate
            screenContainsRect(places[i]);
            QRect intersect = places[i] & frameGeometry();
            if ( intersect.isEmpty() )
            {
                LookupLbl->move(places[i].topLeft());
                break;
            }
        }
    }

    return;
}

void inputWindow::hideLookupTable()
{
    LookupLbl->hide();
    try_hide();
}

void inputWindow::showLookupTable()
{
    LookupLbl->show();
    if( LookupLbl->isAttached() )
    {
        show();
        adjustSize();
    } else if(!isVisible() && !sticky) {
        if(!inTransaction)
            LookupLbl->move(insertPointX, insertPointY);
    }
}

#include <kgenericfactory.h>
typedef KGenericFactory<InputWindowPlugin> InputWindowPluginFactory;
K_EXPORT_COMPONENT_FACTORY( skimplugin_inputwindow,
                              InputWindowPluginFactory( "skimplugin_inputwindow" ) )

InputWindowPlugin::InputWindowPlugin( QObject *parent, const char *name, const QStringList & /*args*/ )
 : SkimPlugin(InputWindowPluginFactory::instance(), parent, name)
{
    inputw = new inputWindow(static_cast<SkimPluginManager *>(parent), 0,0, TOP_LEVEL_WFLAGS);
    connect( this, SIGNAL(settingsChanged()), inputw, SLOT(changeSetting()));
}

void InputWindowPlugin::toggleStick() {
    inputw->toggleStick();
}

InputWindowPlugin::~InputWindowPlugin()
{
    delete inputw;
}

#include "inputwindow.moc"
