/*
 *    Copyright 2013 TOYAMA Sumio <jun.nama@gmail.com>
 *
 * Licensed 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.
 */
package io.appium.android.ime;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;

import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharacterCodingException;

import android.inputmethodservice.InputMethodService;
import android.text.method.MetaKeyKeyListener;
import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;


public class UnicodeIME extends InputMethodService {
    private static final String TAG = "AppiumUnicodeIME";

    // encodings
    private static final Charset M_UTF7 = Charset.forName("x-IMAP-mailbox-name");
    private static final Charset ASCII  = Charset.forName("US-ASCII");

    private static final CharsetDecoder decoder = M_UTF7.newDecoder();

    // markers of UTF-7 content
    private static final char M_UTF7_SHIFT   = '&';
    private static final char M_UTF7_UNSHIFT = '-';

    // local state
    private boolean isShifted;
    private long metaState;
    private StringBuilder unicodeString;


    @Override
    public void onStartInput(EditorInfo attribute, boolean restarting) {
        Log.i(TAG, "onStartInput");
        super.onStartInput(attribute, restarting);

        if (!restarting) {
            metaState = 0;
            isShifted = false;
        }
        unicodeString = null;
    }

    @Override
    public void onFinishInput() {
        Log.i(TAG, "onFinishInput");
        super.onFinishInput();
        unicodeString = null;
    }

    @Override
    public boolean onEvaluateFullscreenMode() {
        return false;
    }

    @Override
    public boolean onEvaluateInputViewShown() {
        return false;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.i(TAG, "onKeyDown (keyCode='" + keyCode + "', event.keyCode='" + event.getKeyCode() + "', metaState='" + event.getMetaState() + "')");
        int c = getUnicodeChar(keyCode, event);

        if (c == 0) {
            return super.onKeyDown(keyCode, event);
        }

        if (!isShifted) {
            if (c == M_UTF7_SHIFT) {
                shift();
                return true;
            } else if (isAsciiPrintable(c)) {
                commitChar(c);
                return true;
            } else {
                return super.onKeyDown(keyCode, event);
            }
        } else {
            if (c == M_UTF7_UNSHIFT) {
                unshift();
            } else {
                appendChar(c);
            }
            return true;
        }
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        Log.i(TAG, "onKeyUp (keyCode='" + keyCode + "', event.keyCode='" + event.getKeyCode() + "', metaState='" + event.getMetaState() + "')");
        metaState = MetaKeyKeyListener.handleKeyUp(metaState, keyCode, event);
        return super.onKeyUp(keyCode, event);
    }

    private void shift() {
        isShifted = true;
        unicodeString = new StringBuilder();
        appendChar(M_UTF7_SHIFT);
    }

    private void unshift() {
        isShifted = false;
        unicodeString.append(M_UTF7_UNSHIFT);
        String decoded = decodeUtf7(unicodeString.toString());
        getCurrentInputConnection().commitText(decoded, 1);
        unicodeString = null;
    }

    private int getUnicodeChar(int keyCode, KeyEvent event) {
        metaState = MetaKeyKeyListener.handleKeyDown(metaState, keyCode, event);
        int c = event.getUnicodeChar(event.getMetaState());
        metaState = MetaKeyKeyListener.adjustMetaAfterKeypress(metaState);
        return c;
    }

    private void commitChar(int c) {
        getCurrentInputConnection().commitText(String.valueOf((char) c), 1);
    }

    private void appendChar(int c) {
        unicodeString.append((char) c);
    }

    private String decodeUtf7(String encStr) {
        ByteBuffer encoded = ByteBuffer.wrap(encStr.getBytes(ASCII));
        String decoded;
        try {
            CharBuffer buf = decoder.decode(encoded);
            decoded = buf.toString();
        } catch (CharacterCodingException e) {
            decoded = encStr;
        }
        return decoded;
    }

    private boolean isAsciiPrintable(int c) {
        return c >= 0x20 && c <= 0x7E;
    }
}

@@ -1568,17 +1568,68 @@
  = '-';%0A
+    %0A    private static String strInputString = %22%22;
 %0A
-
     // l
@@ -1915,17 +1915,45 @@
 rting);%0A
+        strInputString = %22%22;
 %0A
-
         
@@ -2153,25 +2153,51 @@
 nFinishInput
-%22
+: string%22 + strInputString 
 );%0A        s
@@ -2515,155 +2515,8 @@
 ) %7B%0A
-        Log.i(TAG, %22onKeyDown (keyCode='%22 + keyCode + %22', event.keyCode='%22 + event.getKeyCode() + %22', metaState='%22 + event.getMetaState() + %22')%22);%0A
     
@@ -2559,17 +2559,16 @@
 event);%0A
-%0A
         
@@ -2643,25 +2643,26 @@
       %7D%0A
+    
 %0A    
-    
+%09
 if (!isS
@@ -3226,153 +3226,8 @@
 ) %7B%0A
-        Log.i(TAG, %22onKeyUp (keyCode='%22 + keyCode + %22', event.keyCode='%22 + event.getKeyCode() + %22', metaState='%22 + event.getMetaState() + %22')%22);%0A
     
@@ -4168,24 +4168,92 @@
 ar) c), 1);%0A
+        strInputString = strInputString + String.valueOf((char) c);%0A
     %7D%0A%0A    p
@@ -4688,32 +4688,32 @@
 decoded;%0A    %7D%0A%0A
-
     private bool
@@ -4734,32 +4734,69 @@
 ntable(int c) %7B%0A
+    %09if (c == 10)%0A    %09%09return true;%0A
         return c

make carriage returns behave normally