/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbPlugins module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/

#include <hbinputpredictionengine.h>
#include <hbinputmethod.h>
#include <hbinputkeymap.h>
#include <hbinputkeymapfactory.h>
#include <hbinputbutton.h>

#include "hbinputabstractbase.h"
#include "hbinputbasicqwertyhandler.h"
#include "hbinputbasichandler_p.h"

class HbInputBasicQwertyHandlerPrivate: public HbInputBasicHandlerPrivate
{
    Q_DECLARE_PUBLIC(HbInputBasicQwertyHandler)
public:
    HbInputBasicQwertyHandlerPrivate();
    ~HbInputBasicQwertyHandlerPrivate();

    void init();

    // button related operations.
    bool buttonPressed(const QKeyEvent *event);
    bool buttonReleased(const QKeyEvent *event);

public:
    HbFnState mFnState;
    int mButton;
    bool mLongPressHappened;
    QChar mPrevDeadKey;
    HbInputFocusObject *mCurrentlyFocused;
};

HbInputBasicQwertyHandlerPrivate::HbInputBasicQwertyHandlerPrivate()
:mFnState(HbFnOff),
mButton(0),
mLongPressHappened(0),
mPrevDeadKey(0),
mCurrentlyFocused(0)
{
}

HbInputBasicQwertyHandlerPrivate::~HbInputBasicQwertyHandlerPrivate()
{
}

void HbInputBasicQwertyHandlerPrivate::init()
{
}

bool HbInputBasicQwertyHandlerPrivate::buttonPressed(const QKeyEvent * event)
{
    if (!mKeymap->isDeadKey(event->key())) {
        if (event->isAutoRepeat() && mButton == event->key()) {
            if (mButton == HbInputButton::ButtonKeyCodeSymbol) {
                mInputMethod->selectSpecialCharacterTableMode();
                mLongPressHappened = true;
            }
            if (mLongPressHappened) {
                mButton = 0;
                return true;
            }
        } else {
            mLongPressHappened = false;
        }
        mButton = event->key();
    } 
    return false;
}

bool HbInputBasicQwertyHandlerPrivate::buttonReleased(const QKeyEvent *event)
{
    Q_Q(HbInputBasicQwertyHandler);
    mButton = 0;

    int buttonId = event->key();
    if (!event->text().isEmpty()) {
        buttonId = event->text().at(0).unicode();
    }

    QChar firstChar = 0;
    QChar secondChar = 0;

    if (mLongPressHappened) {
        mLongPressHappened = false;
        return false;
    }

    HbInputFocusObject *focusObject = 0;
    focusObject = mInputMethod->focusObject();
    if (!focusObject) {
        qDebug("HbInputBasicQwertyHandler::virtualButtonClicked : no focused editor widget!");
        return false;
    }
    int currentTextCase = focusObject->editorInterface().textCase();
    const HbMappedKey *mappedKey = mKeymap->keyForKeycode(mInputMethod->currentKeyboardType(), buttonId);
    QChar newChar;
    if (mFnState == HbFnNext) {
        newChar = (mappedKey->characters(HbModifierFnPressed)).at(0);
        mFnState = HbFnOff;
    } else {
        if (currentTextCase == HbTextCaseLower) {
            if (mappedKey && mappedKey->characters(HbModifierNone).size() > 0) {
                newChar = mappedKey->characters(HbModifierNone).at(0);
            } else {
                newChar = buttonId;
            }
        } else {
            if (mappedKey && mappedKey->characters(HbModifierShiftPressed).size() > 0) {
                newChar = mappedKey->characters(HbModifierShiftPressed).at(0);
            } else {
                newChar = buttonId;
            }
        }
    }

    if (mInputMethod->currentKeyboardType() != HbKeyboardSctLandscape) {
        if (!mPrevDeadKey.isNull()) {
            if (buttonId != HbInputButton::ButtonKeyCodeShift && buttonId != Qt::Key_Alt) {
                mKeymap->combineCharacter(mPrevDeadKey, newChar, firstChar, secondChar );
                mPrevDeadKey = 0;
                if (firstChar.isNull() && secondChar.isNull()) {
                    return true;
                }
            }
        } else {
            if (mKeymap->isDeadKey(newChar.unicode())) {
                mPrevDeadKey = newChar.unicode();
                return true;
            } else {
                firstChar = newChar;
            }
        }
    } else {
        firstChar = newChar;
    }

    bool ret = false;
    switch(buttonId) {
    case Qt::Key_Alt:
        if (mFnState == HbFnOff) {
            mFnState = HbFnNext;
        } else if (mFnState == HbFnNext) {
            mFnState = HbFnOn;
        } else {
            mFnState = HbFnOff;
        }
        ret = true;
        break;
    case HbInputButton::ButtonKeyCodeShift: {
            HbTextCase currentTextCase = focusObject->editorInterface().textCase();
            HbInputLanguage language = mInputMethod->inputState().language();
            
            // Update the Case Information in HbInputState, it internally updates in HbEditorInterface as well
            switch(currentTextCase) {
            case HbTextCaseLower:
                // For Case-insensitive languages, Shift Key is used to switch between character sets (i.e lower case characters and shifted characters)
                if(!language.isCaseSensitiveLanguage()){
                    currentTextCase = HbTextCaseUpper; 
                }
                else {
                    currentTextCase = HbTextCaseAutomatic;
                }                                 
                break;
            case HbTextCaseUpper:
                currentTextCase = HbTextCaseLower;                
                break;
            case HbTextCaseAutomatic:
                currentTextCase = HbTextCaseUpper;               
                break;
            default:
                break;
            } 
            HbInputState state = mInputMethod->inputState();
            state.setTextCase(currentTextCase);            
            mInputMethod->activateState(state);
            ret = true;
        }
        break;
    case HbInputButton::ButtonKeyCodeSymbol:   // Ctrl/Chr
    case HbInputButton::ButtonKeyCodeAlphabet:
        mInputMethod->switchSpecialCharacterTable();
        ret = true;
        break;
    default:

        // let's pass non deadkey event to the base class.
        if (!mKeymap->isDeadKey(firstChar.unicode()) && secondChar.isNull() && q->HbInputBasicHandler::filterEvent(event)) {
            return true;
        }

        if (event->key() == HbInputButton::ButtonKeyCodeDelete || event->key() == Qt::Key_Backspace ||
            event->key() == HbInputButton::ButtonKeyCodeCustom) {
            return false;
        }

        QList<QInputMethodEvent::Attribute> list;
        QString newText;
        // If function key is pressed, get the functionized 
        // character and commit.

        if (mFnState == HbFnNext) {
            if (!mappedKey->characters(HbModifierFnPressed).isEmpty() && focusObject && focusObject->characterAllowedInEditor(mappedKey->characters(HbModifierFnPressed).at(0))) {
                QInputMethodEvent event(QString(), list);
                event.setCommitString(QString(mappedKey->characters(HbModifierFnPressed).at(0)));
                focusObject->sendEvent(event);
            }
            mFnState = HbFnOff;
            return true;
        }
        
        if (!firstChar.isNull()){
            q->commitAndUpdate(firstChar); 
            if (!secondChar.isNull() ) {
                if (!q->HbInputBasicHandler::filterEvent(event)) {
                    q->commitAndUpdate(secondChar);
                }
            }

            // Need to refresh the autocompletion list
            refreshAutoCompleter();
            ret = true;
        }

        break;
    }
    return ret;
}

HbInputBasicQwertyHandler::HbInputBasicQwertyHandler(HbInputAbstractMethod* inputMethod)
:HbInputBasicHandler(* new HbInputBasicQwertyHandlerPrivate, inputMethod)
{
    Q_D(HbInputBasicQwertyHandler);
    d->q_ptr = this;
    d->init();
}


HbInputBasicQwertyHandler::~HbInputBasicQwertyHandler()
{
}

/*!
filterEvent function key handling.
*/
bool HbInputBasicQwertyHandler::filterEvent(const QKeyEvent* event)
{
    Q_D(HbInputBasicQwertyHandler);

    if (event->type() == QEvent::KeyRelease) {
        return d->buttonReleased(event);
    } else {
        return d->buttonPressed(event);
    }
}

/*!
returns true if in inline edit.
*/
bool HbInputBasicQwertyHandler::isComposing() const
{   
    return false;
}

/*!
Action handler
*/
bool HbInputBasicQwertyHandler::actionHandler(HbInputModeAction action)
{
    Q_D(HbInputBasicQwertyHandler);
    HbInputFocusObject *focusObject = 0;
    focusObject = d->mInputMethod->focusObject();
    bool ret = true;
    switch (action) {
    case HbInputModeActionCancelButtonPress:
    case HbInputModeActionReset:
        d->mButton = 0;
        break;
    case HbInputModeActionFocusRecieved:
        d->mButton = 0;
        // set up auto completer
        setUpAutoCompleter();
    break;
    case HbInputModeActionFocusLost:
        d->mButton = 0;
        if (d->mCurrentlyFocused != focusObject) {
            d->mCurrentlyFocused = focusObject;
            addWordInAutoCompleter();
        } 
    break;
    default: {
        ret = HbInputBasicHandler::actionHandler(action);
        }
    }
    return ret;
}

void HbInputBasicQwertyHandler::sctCharacterSelected(QString character)
{
    HbInputModeHandler::sctCharacterSelected(character);
}

