/*
 * Copyright (C) 2011 Google Inc.
 *
 * 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 com.anysoftkeyboard.voice;


import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.inputmethodservice.InputMethodService;
import android.os.Handler;
import android.os.IBinder;
import android.speech.RecognizerIntent;
import android.util.Log;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.menny.android.anysoftkeyboard.AnyApplication;

/**
 * Triggers a voice recognition using the Intent api.
 */
class IntentApiTrigger implements Trigger {

    private static final String TAG = "VoiceIntentApiTrigger";

    private final InputMethodService mInputMethodService;

    private final ServiceBridge mServiceBridge;

    private String mLastRecognitionResult;

    private Set<Character> mUpperCaseChars;

    private final Handler mHandler;

    private IBinder mToken;

    public IntentApiTrigger(InputMethodService inputMethodService) {
        mInputMethodService = inputMethodService;

        mServiceBridge = new ServiceBridge(new Callback() {

            public void onRecognitionResult(String recognitionResult) {
                postResult(recognitionResult);
            }
        });

        mUpperCaseChars = new HashSet<Character>();
        mUpperCaseChars.add('.');
        mUpperCaseChars.add('!');
        mUpperCaseChars.add('?');
        mUpperCaseChars.add('\n');

        mHandler = new Handler();
    }

    public void startVoiceRecognition(String language) {
        mToken = mInputMethodService.getWindow().getWindow().getAttributes().token;

        mServiceBridge.startVoiceRecognition(mInputMethodService, language);
    }

    public static boolean isInstalled(InputMethodService inputMethodService) {
        PackageManager pm = inputMethodService.getPackageManager();
        List<ResolveInfo> activities = pm.queryIntentActivities(
                new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
        if (AnyApplication.DEBUG)
        {
        	for(ResolveInfo ri : activities)
        	{
        		Log.d(TAG, "Found IntentApiTrigger ACTION_RECOGNIZE_SPEECH handler: "+ri.activityInfo.targetActivity+" @ "+
        				ri.activityInfo.packageName);
        	}
        }
        return activities.size() > 0;
    }

    private InputMethodManager getInputMethodManager() {
        return (InputMethodManager) mInputMethodService
                .getSystemService(Context.INPUT_METHOD_SERVICE);
    }

    private void postResult(String recognitionResult) {
        mLastRecognitionResult = recognitionResult;

        // Request the system to display the IME.
        getInputMethodManager().showSoftInputFromInputMethod(mToken,
                InputMethodManager.SHOW_IMPLICIT);
    }

    public void onStartInputView() {
        Log.i(TAG, "#onStartInputView");
        if (mLastRecognitionResult != null) {
            scheduleCommit();
        }
    }

    private void scheduleCommit() {
        mHandler.post(new Runnable() {

            public void run() {
                commitResult();
            }
        });
    }

    private void commitResult() {
        if (mLastRecognitionResult == null) {
            return;
        }

        String result = mLastRecognitionResult;

        InputConnection conn = mInputMethodService.getCurrentInputConnection();

        if (conn == null) {
            Log.i(TAG, "Unable to commit recognition result, as the current input connection "
                    + "is null. Did someone kill the IME?");
            return;
        }

        if (!conn.beginBatchEdit()) {
            Log.i(TAG, "Unable to commit recognition result, as a batch edit cannot start");
            return;
        }

        try {
            ExtractedTextRequest etr = new ExtractedTextRequest();
            etr.flags = InputConnection.GET_TEXT_WITH_STYLES;

            ExtractedText et = conn.getExtractedText(etr, 0);

            if (et == null) {
                Log.i(TAG, "Unable to commit recognition result, as extracted text is null");
                return;
            }

            if (et.text != null) {

                if (et.selectionStart != et.selectionEnd) {
                    conn.deleteSurroundingText(et.selectionStart, et.selectionEnd);
                }

                result = format(et, result);
            }

            if (!conn.commitText(result, 0)) {
                Log.i(TAG, "Unable to commit recognition result");
                return;
            }

            mLastRecognitionResult = null;
        } finally {
            conn.endBatchEdit();
        }
    }

    /**
     * Formats the recognised text by adding white spaces at the beginning or at the end, and
     * by making the first char upper case if necessary.
     */
    private String format(ExtractedText et, String result) {
        int pos = et.selectionStart - 1;

        while (pos > 0 && Character.isWhitespace(et.text.charAt(pos))) {
            pos--;
        }

        if (pos == -1 || mUpperCaseChars.contains(et.text.charAt(pos))) {
            result = Character.toUpperCase(result.charAt(0)) + result.substring(1);
        }

        if (et.selectionStart - 1 > 0
                && !Character.isWhitespace(et.text.charAt(et.selectionStart - 1))) {
            result = " " + result;
        }

        if (et.selectionEnd < et.text.length()
                && !Character.isWhitespace(et.text.charAt(et.selectionEnd))) {
            result = result + " ";
        }
        return result;
    }


    interface Callback {
        void onRecognitionResult(String recognitionResult);
    }
}
