/*
 * Jan Ochodnicky
 * Diplomova prace VUT Brno
 * 2011
 */

/*
 * TODO list:
 * 1. Export na Android Market
 * 2. Predikcny slovnik
 * 3. Odstranit pomocne cisla a premennu nieco
 * 3. Vymysliet ci vyhovuju popup nahlady pismen pri opakovanom stlacani pri velkych pismenach
 * 4. Prekreslit priesvitne ikony
 */
package com.ochodnicky.android.t12softkeyboard;


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.Keyboard.Key;
import android.inputmethodservice.KeyboardView;
import android.os.Vibrator;
import android.text.method.MetaKeyKeyListener;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.Toast;

import com.ochodnicky.android.t12softkeyboard.R;

/**
 * Example of writing an input method for a soft keyboard.  This code is
 * focused on simplicity over completeness, so it should in no way be considered
 * to be a complete soft keyboard implementation.  Its purpose is to provide
 * a basic example for how you would get started writing an input method, to
 * be fleshed out as appropriate.
 */
public class T12SoftKeyboard extends InputMethodService 
        implements KeyboardView.OnKeyboardActionListener {
    static final boolean DEBUG = false;
    
    /**
     * This boolean indicates the optional example code for performing
     * processing of hard keys in addition to regular text generation
     * from on-screen interaction.  It would be used for input methods that
     * perform language translations (such as converting text entered on 
     * a QWERTY keyboard to Chinese), but may not be used for input methods
     * that are primarily intended to be used for on-screen text entry.
     */
    static final boolean PROCESS_HARD_KEYS = true;
    
    private KeyboardView mInputView;
    private CandidateView mCandidateView;
    private CompletionInfo[] mCompletions;
    
    private StringBuilder mComposing = new StringBuilder();
    private boolean mPredictionOn;
    private boolean mCompletionOn;
    private int mLastDisplayWidth;
    private long mLastShiftTime;
    private boolean mCapsLock;
    private long mMetaState;

    private LatinKeyboard mSymbolsKeyboard;
    private LatinKeyboard mSymbolsShiftedKeyboard;
    private LatinKeyboard mQwertyKeyboard;
    
    private Vibrator mVibrator = null;
    
    private LatinKeyboard mCurKeyboard;
    private boolean mWasShifted;
    private boolean mWasLowerCase;
    private int mLastKey;
    private boolean letCompose;
    private String mComposeStore = "";
    
    private ArrayList<String> suggList;
    private ArrayList<Integer> suggPriority;
    private String mWordSeparators;
    
    public static final String ACTUAL_LANGUAGE = "T12SoftKeyboardPrefsFile";
    private int mActualLanguage;  //0- cesky, 1- english, 2- slovensky

    
    /**
     * Main initialization of the input method component.  Be sure to call
     * to super class.
     */
    @Override public void onCreate() {
        super.onCreate();
        mWordSeparators = getResources().getString(R.string.word_separators);
    }
    
    
    
    /**
     * This is the point where you can do all of your UI initialization.  It
     * is called after creation and any configuration change.
     */
    @Override public void onInitializeInterface() {
        if (mQwertyKeyboard  != null) {
            // Configuration changes can happen after the keyboard gets recreated,
            // so we need to be able to re-build the keyboards if the available
            // space has changed.
            int displayWidth = getMaxWidth();
            if (displayWidth == mLastDisplayWidth) return;
            mLastDisplayWidth = displayWidth;
            if (mComposing.length() > 0) {
            	mComposeStore = mComposing.toString();
            }
        }
        
        mQwertyKeyboard = new LatinKeyboard(this, R.xml.qwerty);
        mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols);
        mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift);
        
        mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
        
        SharedPreferences settings = getSharedPreferences(ACTUAL_LANGUAGE, 0);
        mActualLanguage = settings.getInt("aLanguage", 0);
        
        if (suggList == null) {
          suggList = new ArrayList<String>();
          suggPriority = new ArrayList<Integer>();
        }
        
    }
    
    /**
     * Called by the framework when your view for creating input needs to
     * be generated.  This will be called the first time your input method
     * is displayed, and every time it needs to be re-created such as due to
     * a configuration change.
     */
    @Override public View onCreateInputView() {
        mInputView = (KeyboardView) getLayoutInflater().inflate(
                R.layout.input, null);
        mInputView.setOnKeyboardActionListener(this);
        mInputView.setKeyboard(mQwertyKeyboard);
        setLabels();
        
        return mInputView;
    }
    
    /**
     * Called by the framework when your view for showing candidates needs to
     * be generated, like {@link #onCreateInputView}.
     */
    @Override public View onCreateCandidatesView() {
        mCandidateView = new CandidateView(this);
        mCandidateView.setService(this);
        return mCandidateView;
    }
    
    
    /**
     * This is the main point where we do our initialization of the input method
     * to begin operating on an application.  At this point we have been
     * bound to the client, and are now receiving all of the detailed information
     * about the target of our edits.
     */
    @Override public void onStartInput(EditorInfo attribute, boolean restarting) {
        super.onStartInput(attribute, restarting);
        
        //vynulovanie predikcii
        if (mComposeStore.length() == 0) {
        	letCompose = false;
        }
        
        // Reset our state.  We want to do this even if restarting, because
        // the underlying state of the text editor could have changed in any way.
        mComposing.setLength(0);
        updateCandidates();
        
        if (!restarting) {
            // Clear shift states.
            mMetaState = 0;
        }
        
        mPredictionOn = false;
        mCompletionOn = false;
        mCompletions = null;
        
        // We are now going to initialize our state based on the type of
        // text being edited.
        switch (attribute.inputType&EditorInfo.TYPE_MASK_CLASS) {
            case EditorInfo.TYPE_CLASS_NUMBER:
            case EditorInfo.TYPE_CLASS_DATETIME:
                // Numbers and dates default to the symbols keyboard, with
                // no extra features.
                mCurKeyboard = mSymbolsKeyboard;
                break;
                
            case EditorInfo.TYPE_CLASS_PHONE:
                // Phones will also default to the symbols keyboard, though
                // often you will want to have a dedicated phone keyboard.
                mCurKeyboard = mSymbolsKeyboard;
                break;
                
            case EditorInfo.TYPE_CLASS_TEXT:
                // This is general text editing.  We will default to the
                // normal alphabetic keyboard, and assume that we should
                // be doing predictive text (showing candidates as the
                // user types).
            	mCurKeyboard = mQwertyKeyboard;
                mPredictionOn = true;
                
                // We now look for a few special variations of text that will
                // modify our behavior.
                int variation = attribute.inputType &  EditorInfo.TYPE_MASK_VARIATION;
                if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD ||
                        variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
                    // Do not display predictions / what the user is typing
                    // when they are entering a password.
                    mPredictionOn = false;
                }
                
                if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS 
                        || variation == EditorInfo.TYPE_TEXT_VARIATION_URI
                        || variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
                    // Our predictions are not useful for e-mail addresses
                    // or URIs.
                    mPredictionOn = false;
                }
                
                if ((attribute.inputType&EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
                    // If this is an auto-complete text view, then our predictions
                    // will not be shown and instead we will allow the editor
                    // to supply their own.  We only show the editor's
                    // candidates when in fullscreen mode, otherwise relying
                    // own it displaying its own UI.
                    mPredictionOn = false;
                    mCompletionOn = isFullscreenMode();
                }
                
                // We also want to look at the current state of the editor
                // to decide whether our alphabetic keyboard should start out
                // shifted.
                updateShiftKeyState(attribute);
                break;
                
            default:
                // For all unknown input types, default to the alphabetic
                // keyboard with no special features.
                mCurKeyboard = mQwertyKeyboard;
                updateShiftKeyState(attribute);
        }
        
        // Update the label on the enter key, depending on what the application
        // says it will do.
        mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions);
    }

    /**
     * This is called when the user is done editing a field.  We can use
     * this to reset our state.
     */
    @Override public void onFinishInput() {
        super.onFinishInput();
        
        // Clear current composing text and candidates.
        mComposing.setLength(0);
        updateCandidates();
        
        // We only hide the candidates window when finishing input on
        // a particular editor, to avoid popping the underlying application
        // up and down if the user is entering text into the bottom of
        // its window.
        setCandidatesViewShown(false);
        
        mCurKeyboard = mQwertyKeyboard;
        if (mInputView != null) {
            mInputView.closing();
        }
    }
    
    @Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
        super.onStartInputView(attribute, restarting);
        // Apply the selected keyboard to the input view.
        mInputView.setKeyboard(mCurKeyboard);
        mInputView.closing();
        
        if (mComposeStore.length() != 0) {
        	mComposing.append(mComposeStore);
            getCurrentInputConnection().setComposingText(mComposing, 1);
            updateShiftKeyState(getCurrentInputEditorInfo());
            updateCandidates();
            mComposeStore = "";
        }
    }
    
    /**
     * This tells us about completions that the editor has determined based
     * on the current text in it.  We want to use this in fullscreen mode
     * to show the completions ourself, since the editor can not be seen
     * in that situation.
     */
    @Override public void onDisplayCompletions(CompletionInfo[] completions) {
       if (mCompletionOn) {
            mCompletions = completions;
            if (completions == null) {
                setSuggestions(null, false, false);
                return;
            }
            
            List<String> stringList = new ArrayList<String>();
            for (int i=0; i<(completions != null ? completions.length : 0); i++) {
                CompletionInfo ci = completions[i];
                if (ci != null) stringList.add(ci.getText().toString());
            }
            setSuggestions(stringList, true, true);
        }
    }
    
    /**
     * This translates incoming hard key events in to edit operations on an
     * InputConnection.  It is only needed when using the
     * PROCESS_HARD_KEYS option.
     */
    private boolean translateKeyDown(int keyCode, KeyEvent event) {
        mMetaState = MetaKeyKeyListener.handleKeyDown(mMetaState,
                keyCode, event);
        int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(mMetaState));
        mMetaState = MetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
        InputConnection ic = getCurrentInputConnection();
        if (c == 0 || ic == null) {
            return false;
        }

        if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
            c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
        }
        
        if (mComposing.length() > 0) {
            char accent = mComposing.charAt(mComposing.length() -1 );
            int composed = KeyEvent.getDeadChar(accent, c);

            if (composed != 0) {
                c = composed;
                mComposing.setLength(mComposing.length()-1);
            }
        }
        
        onKey(c, null);
        
        return true;
    }
    
    /**
     * Use this to monitor key events being delivered to the application.
     * We get first crack at them, and can either resume them or let them
     * continue to the app.
     */
    @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_BACK:
                // The InputMethodService already takes care of the back
                // key for us, to dismiss the input method if it is shown.
                // However, our keyboard could be showing a pop-up window
                // that back should dismiss, so we first allow it to do that.
                if (event.getRepeatCount() == 0 && mInputView != null) {
                    if (mInputView.handleBack()) {
                        return true;
                    }
                }
                break;
                
            case KeyEvent.KEYCODE_DEL:
                // Special handling of the delete key: if we currently are
                // composing text for the user, we want to modify that instead
                // of let the application to the delete itself.
                if (mComposing.length() > 0) {
                    onKey(Keyboard.KEYCODE_DELETE, null);
                    return true;
                }
                break;
                
            case KeyEvent.KEYCODE_ENTER:
                // Let the underlying text editor always handle these.
                return false;
                
            default:
                // For all other keys, if we want to do transformations on
                // text being entered with a hard keyboard, we need to process
                // it and do the appropriate action.
                if (PROCESS_HARD_KEYS) {
                    if (keyCode == KeyEvent.KEYCODE_SPACE
                            && (event.getMetaState()&KeyEvent.META_ALT_ON) != 0) {
                        // A silly example: in our input method, Alt+Space
                        // is a shortcut for 'android' in lower case.
                        InputConnection ic = getCurrentInputConnection();
                        if (ic != null) {
                            // First, tell the editor that it is no longer in the
                            // shift state, since we are consuming this.
                            ic.clearMetaKeyStates(KeyEvent.META_ALT_ON);
                            keyDownUp(KeyEvent.KEYCODE_A);
                            keyDownUp(KeyEvent.KEYCODE_N);
                            keyDownUp(KeyEvent.KEYCODE_D);
                            keyDownUp(KeyEvent.KEYCODE_R);
                            keyDownUp(KeyEvent.KEYCODE_O);
                            keyDownUp(KeyEvent.KEYCODE_I);
                            keyDownUp(KeyEvent.KEYCODE_D);
                            // And we consume this event.
                            return true;
                        }
                    }
                    if (mPredictionOn && translateKeyDown(keyCode, event)) {
                        return true;
                    }
                }
        }
        
        return super.onKeyDown(keyCode, event);
    }
    
    /**
     * Use this to monitor key events being delivered to the application.
     * We get first crack at them, and can either resume them or let them
     * continue to the app.
     */
    @Override public boolean onKeyUp(int keyCode, KeyEvent event) {
        // If we want to do transformations on text being entered with a hard
        // keyboard, we need to process the up events to update the meta key
        // state we are tracking.
        if (PROCESS_HARD_KEYS) {
            if (mPredictionOn) {
                mMetaState = MetaKeyKeyListener.handleKeyUp(mMetaState,
                        keyCode, event);
            }
        }
		
        return super.onKeyUp(keyCode, event);
    }

    // prida slovo do predikcneho slovnika ak tam este nie je. ak tam je tak zvysi prioritu
    private void updateSugg() {
    	if (mComposing.length() > 1) {
    		boolean jeTam = false;
        	int i =0;
    		if (!suggList.isEmpty()) {
    			for (i=0; i<suggList.size(); i++){
    				if( suggList.get(i).toLowerCase().equals(mComposing.toString().toLowerCase())){
    					jeTam=true;
    					suggPriority.set(i, suggPriority.get(i)+1);
    					break;
    				}
    			}
    		}
    		if (jeTam) {
    			if (i > 0) {	
    				while (suggPriority.get(i-1).intValue() < suggPriority.get(i).intValue()) {
    					int pomI = 0;
    					String pomS = "";
    			
    					pomI = suggPriority.get(i-1).intValue();
    					suggPriority.set(i-1, suggPriority.get(i).intValue());
    					suggPriority.set(i, pomI);
    			
    					pomS = suggList.get(i-1).toString().toLowerCase();
    					suggList.set(i-1, suggList.get(i).toString().toLowerCase());
    					suggList.set(i, pomS);
    			
    					if (i == 1) {
    						break;
    					}
    					i--;
    				}
    			}
    		} else {
    			if (mComposing.length() != 0) {
    				suggList.add(mComposing.toString().toLowerCase());
    				suggPriority.add(1);
    			}	
    		}
    	}
    }
    
    /**
     * Helper function to commit any text being composed in to the editor.
     */
    private void commitTyped(InputConnection inputConnection) {
        if (mComposing.length() > 0) {
            inputConnection.commitText(mComposing, mComposing.length());
            updateSugg();
            mComposing.setLength(0);        
            updateCandidates();
        }
    }
    
    /**
     * Helper to update the shift state of our keyboard based on the initial
     * editor state.
     */
    private void updateShiftKeyState(EditorInfo attr) {
        if (attr != null 
        		&& mInputView != null && mQwertyKeyboard == mInputView.getKeyboard()) {
            int caps = 0;
            EditorInfo ei = getCurrentInputEditorInfo();
            if (ei != null && ei.inputType != EditorInfo.TYPE_NULL) {
                caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType);
            }
            mInputView.setShifted(mCapsLock || caps != 0);
            updateKeyboardShift(mInputView.isShifted());
        }
    }
    
    /**
     * Helper to determine if a given character code is alphabetic.
     */
    private boolean isAlphabet(int code) {
        if (Character.isLetter(code)) {
            return true;
        } else {
            return false;
        }
    }
    
    /**
     * Helper to send a key down / key up pair to the current editor.
     */
    private void keyDownUp(int keyEventCode) {
        getCurrentInputConnection().sendKeyEvent(
                new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
        getCurrentInputConnection().sendKeyEvent(
                new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
    }
    
    /**
     * Helper to send a character to the editor as raw key events.
     */
    private void sendKey(int keyCode) {
        switch (keyCode) {
            case '\n':
                keyDownUp(KeyEvent.KEYCODE_ENTER);
                break;
            default:
                if (keyCode >= '0' && keyCode <= '9') {
                    keyDownUp(keyCode - '0' + KeyEvent.KEYCODE_0);
                } else {
                    getCurrentInputConnection().commitText(String.valueOf((char) keyCode), 1);
                }
                break;
        }
    }

    // Implementation of KeyboardViewListener

    public void onKey(int primaryCode, int[] keyCodes) {
        if (isWordSeparator(primaryCode)) {
            // Handle separator
            if (mComposing.length() > 0) {
                commitTyped(getCurrentInputConnection());
            }
            sendKey(primaryCode);
            updateShiftKeyState(getCurrentInputEditorInfo());
        } else if (primaryCode == Keyboard.KEYCODE_DELETE) {
            handleBackspace();
        } else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
        	handleShift();
        } else if (primaryCode == Keyboard.KEYCODE_CANCEL) {
            handleClose();
            return;
        } else if (primaryCode == LatinKeyboardView.KEYCODE_OPTIONS) {
        	if (mInputView.getKeyboard() == mQwertyKeyboard) {
        		handleOptions();
        	}
        } else if (primaryCode == LatinKeyboardView.KEYCODE_CAPS) {			// HandleCapslock CapsLock
        	if (!mCapsLock) {
        		handleCapsLock();
        	} /*else {
        		handleShift();  //z nejakeho dovodu nekorektne zobrazuje shifted kontrolku, i ked sa sprava korektne
        	}*/
        } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE
                && mInputView != null) {
            Keyboard current = mInputView.getKeyboard();
            if (current == mSymbolsKeyboard || current == mSymbolsShiftedKeyboard) {
                current = mQwertyKeyboard;
                letCompose = false;
            } else {
            	letCompose = (mComposing.length() > 0);  //je cislo sucastou slova, zostena povolene pouzivanie predikcii
                current = mSymbolsKeyboard;
            }
            mInputView.setKeyboard(current);
            if (current == mSymbolsKeyboard) {
                current.setShifted(false);
            }
        } else {
            handleCharacter(primaryCode, keyCodes);
        }
    }

	public void onText(CharSequence text) {
        InputConnection ic = getCurrentInputConnection();
        if (ic == null) return;
        ic.beginBatchEdit();
        if (mComposing.length() > 0) {
            commitTyped(ic);
        }
        ic.commitText(text, 0);
        ic.endBatchEdit();
        updateShiftKeyState(getCurrentInputEditorInfo());
    }

    /**
     * Update the list of available candidates from the current composing
     * text.  This will need to be filled in by however you are determining
     * candidates.
     */
    private void updateCandidates() {
        if (!mCompletionOn) {
            if (mComposing.length() > 0) {
                ArrayList<String> list = new ArrayList<String>();
                list.add(mComposing.toString());
                if (!suggList.isEmpty()) {
                	int compLength = mComposing.toString().length();;
                	String suggWord;
                	for (int i=0; i<suggList.size(); i++){
                		suggWord = suggList.get(i);
                		if (compLength < suggWord.length()){
                			String compWord = mComposing.toString();
                   			if (compWord.toLowerCase().equals(suggWord.substring(0, compLength).toString())) {
                   				for (int j=0; j<compWord.length(); j++) {
                   					if (Character.isUpperCase(compWord.charAt(j))){
                   					  suggWord = suggWord.substring(0,j)+suggWord.substring(j,j+1).toUpperCase()+suggWord.substring(j+1,suggWord.length());
                   					}
                   				}
                				list.add(suggWord);
                			}
                		}
                		if (list.size() > 11) {    //maximalne 12 slov ponuknutych na predikciu
                			break;
                		}
                    }	
                }
                setSuggestions(list, true, true);
            } else {
                setSuggestions(null, false, false);
            }
        }
    }
    
    public void setSuggestions(List<String> suggestions, boolean completions,
            boolean typedWordValid) {
        if (suggestions != null && suggestions.size() > 0) {
            setCandidatesViewShown(true);
        } else if (isExtractViewShown()) {
            setCandidatesViewShown(true);
        }
        if (mCandidateView != null) {
            mCandidateView.setSuggestions(suggestions, completions, typedWordValid);
        }
    }
    
    private void handleBackspace() {
        final int length = mComposing.length();
        if (length > 1) {
            mComposing.delete(length - 1, length);
            getCurrentInputConnection().setComposingText(mComposing, 1);
            updateCandidates();
        } else if (length > 0) {
            mComposing.setLength(0);
            getCurrentInputConnection().commitText("", 0);
            updateCandidates();
        } else {
            keyDownUp(KeyEvent.KEYCODE_DEL);
        }
        updateShiftKeyState(getCurrentInputEditorInfo());
    }
    
    private void handleCapsLock() {
       if (mInputView == null) {
           return;
       }
       
    	   Keyboard currentKeyboard = mInputView.getKeyboard();
    	   Key shiftKey = currentKeyboard.getKeys().get(currentKeyboard.getShiftKeyIndex());
    	   shiftKey.label= null;
    	   shiftKey.iconPreview = null;
    	   mCapsLock= !mCapsLock;
    	   shiftKey.sticky = !shiftKey.sticky;
    	   if (mCapsLock) {
    		   shiftKey.icon = getResources().getDrawable(R.drawable.sym_keyboard_capslock);
    	   } else {
    		   shiftKey.icon = getResources().getDrawable(R.drawable.sym_keyboard_shift);
    	   }
           updateShiftKeyState(getCurrentInputEditorInfo());
    }
    
    private void handleOptions() {
    	final CharSequence[] items = {"Český", "English", "Slovenský"};
    	final CharSequence[] titles = {"Zvolte rozložení klávesnice", "Choose keyboard layout", "Zvoľte rozloženie klávesnice"};
    	AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
    	builder.setTitle(titles[mActualLanguage]);
    	builder.setSingleChoiceItems(items, mActualLanguage, new DialogInterface.OnClickListener() {
    	    public void onClick(DialogInterface dialog, int item) {
    	        Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
    	        mActualLanguage = item;
    	        
    	        SharedPreferences settings = getSharedPreferences(ACTUAL_LANGUAGE, 0);
    	        SharedPreferences.Editor editor = settings.edit();
    	        editor.putInt("aLanguage", mActualLanguage);
    	        editor.commit();
    	        
    	        mInputView.setKeyboard(mCurKeyboard);		//refresh mInputView
    	        setLabels();
                updateKeyboardShift(mInputView.isShifted());
    	            	        
    	        dialog.dismiss();
    	    }
    	});
    	AlertDialog alert = builder.create();
    	
        Window window = alert.getWindow();
        WindowManager.LayoutParams lp = window.getAttributes();
        lp.token = mInputView.getWindowToken();
        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
        window.setAttributes(lp);
        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
        alert.show();
	}

    private void handleShift() {
        if (mInputView == null) {
            return;
        }
        
        Keyboard currentKeyboard = mInputView.getKeyboard();
        if (mQwertyKeyboard == currentKeyboard) {
        	// Alphabet keyboard
        	if (mCapsLock) {
        		handleCapsLock();
        	} else {
        	 // checkToggleCapsLock();
               mInputView.setShifted(mCapsLock || !mInputView.isShifted());
               updateKeyboardShift(mInputView.isShifted());
        	}
        } else if (currentKeyboard == mSymbolsKeyboard) {
            mSymbolsKeyboard.setShifted(true);
            mInputView.setKeyboard(mSymbolsShiftedKeyboard);
            mSymbolsShiftedKeyboard.setShifted(true);
        } else if (currentKeyboard == mSymbolsShiftedKeyboard) {
            mSymbolsShiftedKeyboard.setShifted(false);
            mInputView.setKeyboard(mSymbolsKeyboard);
            mSymbolsKeyboard.setShifted(false);
        }
    }
    
    private void handleCharacter(int primaryCode, int[] keyCodes) {
        long now = System.currentTimeMillis();
    	if (mLastShiftTime + 800 > now  && !mCapsLock) {
    	     boolean isIn = false;
    	     for (int i=0; i< keyCodes.length; i++) {
    	    	 if (mLastKey == keyCodes[i]) {
    	    		 isIn=true;
    	    	 }
    	     }
             if (mWasShifted && isIn) {
                	 mInputView.setShifted(true);
                     updateKeyboardShift(mInputView.isShifted());	
             }
             if (mWasLowerCase && isIn) {
            	 mInputView.setShifted(false);
            	 updateKeyboardShift(mInputView.isShifted());
             }
        }
        mLastShiftTime = now;

    	if (isInputViewShown()) {
    		if (mInputView.isShifted()) {
    			primaryCode = Character.toUpperCase(primaryCode);
    			updateKeyboardShift(true);
    			mWasShifted = true;
    			mWasLowerCase = false;
    		} else {
    			mWasShifted = false;
    			mWasLowerCase = true;
    		}
    	}
        if ((isAlphabet(primaryCode) && mPredictionOn) || (letCompose && mPredictionOn)) {
            mComposing.append((char) primaryCode);
            getCurrentInputConnection().setComposingText(mComposing, 1);
            updateShiftKeyState(getCurrentInputEditorInfo());
            updateCandidates();
        } else {
            getCurrentInputConnection().commitText(
                    String.valueOf((char) primaryCode), 1);
        }
    }

    private void handleClose() {
        commitTyped(getCurrentInputConnection());
        requestHideSelf(0);
        mInputView.closing();
    }
    
    private void setLabels() {
    	Key klavesa;
    	String[] langStrings;
    	CharSequence[] popupChars;
    	int i = 0;
    	int [] [] poleKodov = new int[12][2];
    	switch (mActualLanguage) {
    		case 1:
    			langStrings = getResources().getStringArray(R.array.lowerCase_en);	//nastavi anglicke labely
    			popupChars = getResources().getStringArray(R.array.popup_en);		//nastavi anglicku popup klavesnicu
    			poleKodov[0] = naplPole(getResources().getIntArray(R.array.a_y));	//nastavi pismena na anglickej klavesnici
    			poleKodov[1] = naplPole(getResources().getIntArray(R.array.c_x_q));
    			poleKodov[2] = naplPole(getResources().getIntArray(R.array.d_j_z));
    			poleKodov[3] = naplPole(getResources().getIntArray(R.array.e_f));
    			poleKodov[4] = naplPole(getResources().getIntArray(R.array.h_k));
    			poleKodov[5] = naplPole(getResources().getIntArray(R.array.i_w));
    			poleKodov[6] = naplPole(getResources().getIntArray(R.array.l_u));
    			poleKodov[7] = naplPole(getResources().getIntArray(R.array.n_b));
    			poleKodov[8] = naplPole(getResources().getIntArray(R.array.o_m));
    			poleKodov[9] = naplPole(getResources().getIntArray(R.array.r_g));
    			poleKodov[10] = naplPole(getResources().getIntArray(R.array.s_v));
    			poleKodov[11] = naplPole(getResources().getIntArray(R.array.t_p));
    			break;
    		case 2:
    			langStrings = getResources().getStringArray(R.array.lowerCase_sk);	//nastavi slovenske labely
    			popupChars = getResources().getStringArray(R.array.popup_sk);		//nastavi slovensku popup klavesnicu
    			poleKodov[0] = naplPole(getResources().getIntArray(R.array.a_y));	//nastavi pismena na slovenskej klavesnici
    			poleKodov[1] = naplPole(getResources().getIntArray(R.array.c_m));
    			poleKodov[2] = naplPole(getResources().getIntArray(R.array.e_f));
    			poleKodov[3] = naplPole(getResources().getIntArray(R.array.i_z));
    			poleKodov[4] = naplPole(getResources().getIntArray(R.array.k_h));
    			poleKodov[5] = naplPole(getResources().getIntArray(R.array.l_d));
    			poleKodov[6] = naplPole(getResources().getIntArray(R.array.n_b));
    			poleKodov[7] = naplPole(getResources().getIntArray(R.array.o_x_w));
    			poleKodov[8] = naplPole(getResources().getIntArray(R.array.r_g));
    			poleKodov[9] = naplPole(getResources().getIntArray(R.array.s_u));
    			poleKodov[10] = naplPole(getResources().getIntArray(R.array.t_p));
    			poleKodov[11] = naplPole(getResources().getIntArray(R.array.v_j_q));
    			break;
    		default: /*case 0*/
    			langStrings = getResources().getStringArray(R.array.lowerCase_cz);	//defaultne je nastaena cestina
    			popupChars = getResources().getStringArray(R.array.popup_cz);		//nastavi cesku popup klavesnicu
    			poleKodov[0] = naplPole(getResources().getIntArray(R.array.a_y));	//nastavi pismena na defaultnej klavesnici
    			poleKodov[1] = naplPole(getResources().getIntArray(R.array.e_f));
    			poleKodov[2] = naplPole(getResources().getIntArray(R.array.i_z));
    			poleKodov[3] = naplPole(getResources().getIntArray(R.array.k_h));
    			poleKodov[4] = naplPole(getResources().getIntArray(R.array.l_d));
    			poleKodov[5] = naplPole(getResources().getIntArray(R.array.n_b));
    			poleKodov[6] = naplPole(getResources().getIntArray(R.array.o_m));
    			poleKodov[7] = naplPole(getResources().getIntArray(R.array.r_g));
    			poleKodov[8] = naplPole(getResources().getIntArray(R.array.s_c));
    			poleKodov[9] = naplPole(getResources().getIntArray(R.array.t_p));
    			poleKodov[10] = naplPole(getResources().getIntArray(R.array.u_x_w));
    			poleKodov[11] = naplPole(getResources().getIntArray(R.array.v_j_q));
    			break;
    	}
    	for (Iterator<Key> klavesaIt = mInputView.getKeyboard().getKeys().iterator(); klavesaIt.hasNext();) {
    		klavesa = klavesaIt.next();
    		if ((klavesa.label != null) && !klavesa.label.equals("1#+") && !klavesa.label.equals("Go") 
    				&& !klavesa.label.equals("Next") && !klavesa.label.equals("Send")) {
    			klavesa.label =langStrings[i];
    			klavesa.codes = poleKodov[i];
    			klavesa.popupCharacters = popupChars[i];
    			i++;
    		}
    	}
    }
    
    private void updateKeyboardShift(boolean setShift){
    	Key klavesa;
    	String[] langStringsUpp;
    	String[] langStringsLow;
    	switch (mActualLanguage) {
    		case 0:
    			langStringsLow = getResources().getStringArray(R.array.lowerCase_cz); //nastavi ceske labely
    			langStringsUpp = getResources().getStringArray(R.array.upperCase_cz);
    			break;
    		case 1:
    			langStringsLow = getResources().getStringArray(R.array.lowerCase_en);//nastavi anglicke labely
    			langStringsUpp = getResources().getStringArray(R.array.upperCase_en);
    			break;
    		case 2:
    			langStringsLow = getResources().getStringArray(R.array.lowerCase_sk);//nastavi anglicke labely
    			langStringsUpp = getResources().getStringArray(R.array.upperCase_sk);
    			break;
    		default:
    			langStringsLow = getResources().getStringArray(R.array.lowerCase_cz);  //defaultne je nastavena cestina
    			langStringsUpp = getResources().getStringArray(R.array.upperCase_cz);
    			break;
    	}
    	for (Iterator<Key> klavesaIt = mInputView.getKeyboard().getKeys().iterator(); klavesaIt.hasNext();) {
    		klavesa = klavesaIt.next();
    		if (klavesa.label != null) {
    		  if (setShift) {
    			//tak shiftnem pismenka
    			for (int i=0; i < langStringsLow.length; i++){
    				if (klavesa.label.equals(langStringsLow[i])) {
    					klavesa.label = langStringsUpp[i];
    					break;
    				}
    			}
    		  } else {
    			//tak nastavim male
    			  for (int i=0; i < langStringsUpp.length; i++){
      				if (klavesa.label.equals(langStringsUpp[i])) {
      					klavesa.label= langStringsLow[i];
      					break;
      				}
      			  }
    		  }
    		}
    	}
    }
    
    private int[] naplPole (int[] sPole) {
    	int[] dPole = new int [sPole.length];
    	for (int i=0; i< sPole.length; i++) {
    		dPole[i] = sPole[i];
    	}
    	return dPole;
    }
   
    private String getWordSeparators() {
        return mWordSeparators;
    }
    
    public boolean isWordSeparator(int code) {
        String separators = getWordSeparators();
        return separators.contains(String.valueOf((char)code));
    }

    public void pickDefaultCandidate() {
        pickSuggestionManually(0);
    }
    
   
    public void pickSuggestionManually(int index) {
        if (mCompletionOn && mCompletions != null && index >= 0
                && index < mCompletions.length) {
            CompletionInfo ci = mCompletions[index];
            getCurrentInputConnection().commitCompletion(ci);
            if (mCandidateView != null) {
                mCandidateView.clear();
            }
            updateShiftKeyState(getCurrentInputEditorInfo());
        } else if (mComposing.length() > 0) {
        	if (mCandidateView != null) {								//zapis vybrateho slova z kandidatov
                getCurrentInputConnection().commitText(
                        mCandidateView.getSuggestion(index),
                        mCandidateView.getSuggestion(index).length());
                mComposing.setLength(0);
                mComposing.append(mCandidateView.getSuggestion(index));
            	updateSugg();
                mComposing.setLength(0);
                updateCandidates();
            }
        }
    }
    
    public void swipeRight() {
    	onKey(Keyboard.KEYCODE_MODE_CHANGE, null);
    }
    
    public void swipeLeft() {
    	handleBackspace();
    }

    public void swipeDown() {
    	handleClose();
    }

    public void swipeUp() {
    	handleShift();
    }
    
    public void onPress(int primaryCode) {
    	mVibrator.vibrate(30);				//vibrovanie po stlaceni klavesy v ms
    }
    
    public void onRelease(int primaryCode) {
    	mLastKey = primaryCode;
    }  
}
