/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * 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.android.server;

import com.android.internal.content.PackageMonitor;

import android.app.AppGlobals;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.speech.RecognitionService;
import android.text.TextUtils;
import android.util.Slog;

import java.util.List;

public class RecognitionManagerService extends Binder {
    final static String TAG = "RecognitionManagerService";

    private final Context mContext;
    private final MyPackageMonitor mMonitor;
    private final IPackageManager mIPm;

    private static final boolean DEBUG = false;

    class MyPackageMonitor extends PackageMonitor {
        public void onSomePackagesChanged() {
            int userHandle = getChangingUserId();
            if (DEBUG) Slog.i(TAG, "onSomePackagesChanged user=" + userHandle);
            ComponentName comp = getCurRecognizer(userHandle);
            if (comp == null) {
                if (anyPackagesAppearing()) {
                    comp = findAvailRecognizer(null, userHandle);
                    if (comp != null) {
                        setCurRecognizer(comp, userHandle);
                    }
                }
                return;
            }

            int change = isPackageDisappearing(comp.getPackageName()); 
            if (change == PACKAGE_PERMANENT_CHANGE
                    || change == PACKAGE_TEMPORARY_CHANGE) {
                setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
                
            } else if (isPackageModified(comp.getPackageName())) {
                setCurRecognizer(findAvailRecognizer(comp.getPackageName(), userHandle),
                        userHandle);
            }
        }
    }

    RecognitionManagerService(Context context) {
        mContext = context;
        mMonitor = new MyPackageMonitor();
        mMonitor.register(context, null, UserHandle.ALL, true);
        mIPm = AppGlobals.getPackageManager();
        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
    }

    public void systemReady() {
        initForUser(UserHandle.USER_OWNER);
    }

    private void initForUser(int userHandle) {
        if (DEBUG) Slog.i(TAG, "initForUser user=" + userHandle);
        ComponentName comp = getCurRecognizer(userHandle);
        ServiceInfo info = null;
        if (comp != null) {
            // See if the current recognizer is still available.
            try {
                info = mIPm.getServiceInfo(comp, 0, userHandle);
            } catch (RemoteException e) {
            }
        }
        if (info == null) {
            comp = findAvailRecognizer(null, userHandle);
            if (comp != null) {
                setCurRecognizer(comp, userHandle);
            }
        }
    }

    ComponentName findAvailRecognizer(String prefPackage, int userHandle) {
        List<ResolveInfo> available =
                mContext.getPackageManager().queryIntentServicesAsUser(
                        new Intent(RecognitionService.SERVICE_INTERFACE), 0, userHandle);
        int numAvailable = available.size();

        if (numAvailable == 0) {
            Slog.w(TAG, "no available voice recognition services found for user " + userHandle);
            return null;
        } else {
            if (prefPackage != null) {
                for (int i=0; i<numAvailable; i++) {
                    ServiceInfo serviceInfo = available.get(i).serviceInfo;
                    if (prefPackage.equals(serviceInfo.packageName)) {
                        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
                    }
                }
            }
            if (numAvailable > 1) {
                Slog.w(TAG, "more than one voice recognition service found, picking first");
            }

            ServiceInfo serviceInfo = available.get(0).serviceInfo;
            return new ComponentName(serviceInfo.packageName, serviceInfo.name);
        }
    }

    ComponentName getCurRecognizer(int userHandle) {
        String curRecognizer = Settings.Secure.getStringForUser(
                mContext.getContentResolver(),
                Settings.Secure.VOICE_RECOGNITION_SERVICE, userHandle);
        if (TextUtils.isEmpty(curRecognizer)) {
            return null;
        }
        if (DEBUG) Slog.i(TAG, "getCurRecognizer curRecognizer=" + curRecognizer
                + " user=" + userHandle);
        return ComponentName.unflattenFromString(curRecognizer);
    }

    void setCurRecognizer(ComponentName comp, int userHandle) {
        Settings.Secure.putStringForUser(mContext.getContentResolver(),
                Settings.Secure.VOICE_RECOGNITION_SERVICE,
                comp != null ? comp.flattenToShortString() : "", userHandle);
        if (DEBUG) Slog.i(TAG, "setCurRecognizer comp=" + comp
                + " user=" + userHandle);
    }

    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (DEBUG) Slog.i(TAG, "received " + action);
            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                if (userHandle > 0) {
                    initForUser(userHandle);
                }
            }
        }
    };
}
