/*
 * Copyright (C) 2008 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.settings.quicklaunch;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.provider.Settings.Bookmarks;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.widget.AdapterView;

import com.android.settings.R;

import java.net.URISyntaxException;

/**
 * Settings activity for quick launch.
 * <p>
 * Shows a list of possible shortcuts, the current application each is bound to,
 * and allows choosing a new bookmark for a shortcut.
 */
public class QuickLaunchSettings extends PreferenceActivity implements
        AdapterView.OnItemLongClickListener, DialogInterface.OnClickListener {

    private static final String TAG = "QuickLaunchSettings";

    private static final String KEY_SHORTCUT_CATEGORY = "shortcut_category";

    private static final int DIALOG_CLEAR_SHORTCUT = 0;

    private static final int REQUEST_PICK_BOOKMARK = 1;

    private static final int COLUMN_SHORTCUT = 0;
    private static final int COLUMN_TITLE = 1;
    private static final int COLUMN_INTENT = 2;
    private static final String[] sProjection = new String[] {
            Bookmarks.SHORTCUT, Bookmarks.TITLE, Bookmarks.INTENT
    };
    private static final String sShortcutSelection = Bookmarks.SHORTCUT + "=?";
    
    private Handler mUiHandler = new Handler();
    
    private static final String DEFAULT_BOOKMARK_FOLDER = "@quicklaunch";
    /** Cursor for Bookmarks provider. */
    private Cursor mBookmarksCursor;
    /** Listens for changes to Bookmarks provider. */
    private BookmarksObserver mBookmarksObserver;
    /** Used to keep track of which shortcuts have bookmarks. */
    private SparseBooleanArray mBookmarkedShortcuts;
    
    /** Preference category to hold the shortcut preferences. */
    private PreferenceGroup mShortcutGroup;
    /** Mapping of a shortcut to its preference. */
    private SparseArray<ShortcutPreference> mShortcutToPreference;

    /** The bookmark title of the shortcut that is being cleared. */
    private CharSequence mClearDialogBookmarkTitle;
    private static final String CLEAR_DIALOG_BOOKMARK_TITLE = "CLEAR_DIALOG_BOOKMARK_TITLE";
    /** The shortcut that is being cleared. */
    private char mClearDialogShortcut;
    private static final String CLEAR_DIALOG_SHORTCUT = "CLEAR_DIALOG_SHORTCUT";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        addPreferencesFromResource(R.xml.quick_launch_settings);
        
        mShortcutGroup = (PreferenceGroup) findPreference(KEY_SHORTCUT_CATEGORY);
        mShortcutToPreference = new SparseArray<ShortcutPreference>();
        mBookmarksObserver = new BookmarksObserver(mUiHandler);
        initShortcutPreferences();
        mBookmarksCursor = managedQuery(Bookmarks.CONTENT_URI, sProjection, null, null);
        getListView().setOnItemLongClickListener(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        getContentResolver().registerContentObserver(Bookmarks.CONTENT_URI, true,
                mBookmarksObserver);
        refreshShortcuts();
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        getContentResolver().unregisterContentObserver(mBookmarksObserver);
    }

    @Override
    protected void onRestoreInstanceState(Bundle state) {
        super.onRestoreInstanceState(state);
        
        // Restore the clear dialog's info
        mClearDialogBookmarkTitle = state.getString(CLEAR_DIALOG_BOOKMARK_TITLE);
        mClearDialogShortcut = (char) state.getInt(CLEAR_DIALOG_SHORTCUT, 0);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        
        // Save the clear dialog's info
        outState.putCharSequence(CLEAR_DIALOG_BOOKMARK_TITLE, mClearDialogBookmarkTitle);
        outState.putInt(CLEAR_DIALOG_SHORTCUT, mClearDialogShortcut);
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
            
            case DIALOG_CLEAR_SHORTCUT: {
                // Create the dialog for clearing a shortcut
                return new AlertDialog.Builder(this)
                        .setTitle(getString(R.string.quick_launch_clear_dialog_title))
                        .setIcon(android.R.drawable.ic_dialog_alert)
                        .setMessage(getString(R.string.quick_launch_clear_dialog_message,
                                mClearDialogShortcut, mClearDialogBookmarkTitle))
                        .setPositiveButton(R.string.quick_launch_clear_ok_button, this)
                        .setNegativeButton(R.string.quick_launch_clear_cancel_button, this)
                        .create();
            }
        }
        
        return super.onCreateDialog(id);
    }
    
    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        switch (id) {
            
            case DIALOG_CLEAR_SHORTCUT: {
                AlertDialog alertDialog = (AlertDialog) dialog;
                alertDialog.setMessage(getString(R.string.quick_launch_clear_dialog_message,
                        mClearDialogShortcut, mClearDialogBookmarkTitle));
            }
        }
    }

    private void showClearDialog(ShortcutPreference pref) {

        if (!pref.hasBookmark()) return;
        
        mClearDialogBookmarkTitle = pref.getTitle();
        mClearDialogShortcut = pref.getShortcut();
        showDialog(DIALOG_CLEAR_SHORTCUT);
    }
    
    public void onClick(DialogInterface dialog, int which) {
        if (mClearDialogShortcut > 0 && which == AlertDialog.BUTTON1) {
            // Clear the shortcut
            clearShortcut(mClearDialogShortcut);
        }
        mClearDialogBookmarkTitle = null;
        mClearDialogShortcut = 0;
    }

    private void clearShortcut(char shortcut) {
        getContentResolver().delete(Bookmarks.CONTENT_URI, sShortcutSelection,
                new String[] { String.valueOf((int) shortcut) });
    }
    
    @Override
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
        if (!(preference instanceof ShortcutPreference)) return false;

        // Open the screen to pick a bookmark for this shortcut
        ShortcutPreference pref = (ShortcutPreference) preference;
        Intent intent = new Intent(this, BookmarkPicker.class);
        intent.putExtra(BookmarkPicker.EXTRA_SHORTCUT, pref.getShortcut());
        startActivityForResult(intent, REQUEST_PICK_BOOKMARK);
        
        return true;
    }

    public boolean onItemLongClick(AdapterView parent, View view, int position, long id) {
        
        // Open the clear shortcut dialog
        Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem(position);
        if (!(pref instanceof ShortcutPreference)) return false;
        showClearDialog((ShortcutPreference) pref);
        return true;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != RESULT_OK) {
            return;
        }

        if (requestCode == REQUEST_PICK_BOOKMARK) {
            
            // Returned from the 'pick bookmark for this shortcut' screen
            if (data == null) {
                Log.w(TAG, "Result from bookmark picker does not have an intent.");
                return;
            }
            
            String title = data.getStringExtra(BookmarkPicker.EXTRA_TITLE);
            char shortcut = data.getCharExtra(BookmarkPicker.EXTRA_SHORTCUT, (char) 0);
            updateShortcut(shortcut, data);
            
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    private void updateShortcut(char shortcut, Intent intent) {
        // Update the bookmark for a shortcut
        // Pass an empty title so it gets resolved each time this bookmark is
        // displayed (since the locale could change after we insert into the provider).
        Bookmarks.add(getContentResolver(), intent, "", DEFAULT_BOOKMARK_FOLDER, shortcut, 0);
    }
    
    private ShortcutPreference getOrCreatePreference(char shortcut) {
        ShortcutPreference pref = mShortcutToPreference.get(shortcut);
        if (pref != null) {
            return pref;
        } else {
            Log.w(TAG, "Unknown shortcut '" + shortcut + "', creating preference anyway");
            return createPreference(shortcut);
        }
    }
    
    private ShortcutPreference createPreference(char shortcut) {
        ShortcutPreference pref = new ShortcutPreference(QuickLaunchSettings.this, shortcut);
        mShortcutGroup.addPreference(pref);
        mShortcutToPreference.put(shortcut, pref);
        return pref;
    }

    private void initShortcutPreferences() {
        
        /** Whether the shortcut has been seen already.  The array index is the shortcut. */
        SparseBooleanArray shortcutSeen = new SparseBooleanArray();
        KeyCharacterMap keyMap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);

        // Go through all the key codes and create a preference for the appropriate keys
        for (int keyCode = KeyEvent.getMaxKeyCode() - 1; keyCode >= 0; keyCode--) {
            // Get the label for the primary char on the key that produces this key code
            char shortcut = (char) Character.toLowerCase(keyMap.getDisplayLabel(keyCode));
            if (shortcut == 0 || shortcutSeen.get(shortcut, false)) continue;
            // TODO: need a to tell if the current keyboard can produce this key code, for now
            // only allow the letter or digits
            if (!Character.isLetterOrDigit(shortcut)) continue;
            shortcutSeen.put(shortcut, true);
            
            createPreference(shortcut);
        }
    }
    
    private synchronized void refreshShortcuts() {
        Cursor c = mBookmarksCursor;
        if (c == null) {
            // Haven't finished querying yet
            return;
        }
        
        if (!c.requery()) {
            Log.e(TAG, "Could not requery cursor when refreshing shortcuts.");
            return;
        }
        
        /**
         * We use the previous bookmarked shortcuts array to filter out those
         * shortcuts that had bookmarks before this method call, and don't after
         * (so we can set the preferences to be without bookmarks).
         */
        SparseBooleanArray noLongerBookmarkedShortcuts = mBookmarkedShortcuts;
        SparseBooleanArray newBookmarkedShortcuts = new SparseBooleanArray(); 
        while (c.moveToNext()) {
            char shortcut = Character.toLowerCase((char) c.getInt(COLUMN_SHORTCUT));
            if (shortcut == 0) continue;
            
            ShortcutPreference pref = getOrCreatePreference(shortcut);
            CharSequence title = Bookmarks.getTitle(this, c);

            /*
             * The title retrieved from Bookmarks.getTitle() will be in
             * the original boot locale, not the current locale.
             * Try to look up a localized title from the PackageManager.
             */
            int intentColumn = c.getColumnIndex(Bookmarks.INTENT);
            String intentUri = c.getString(intentColumn);
            PackageManager packageManager = getPackageManager();
            try {
                Intent intent = Intent.getIntent(intentUri);
                ResolveInfo info = packageManager.resolveActivity(intent, 0);
                if (info != null) {
                    title = info.loadLabel(packageManager);
                }
            } catch (URISyntaxException e) {
                // Just use the non-localized title, then.
            }

            pref.setTitle(title);
            pref.setSummary(getString(R.string.quick_launch_shortcut,
                    String.valueOf(shortcut)));
            pref.setHasBookmark(true);
            
            newBookmarkedShortcuts.put(shortcut, true);
            if (noLongerBookmarkedShortcuts != null) {
                // After this loop, the shortcuts with value true in this array
                // will no longer have bookmarks
                noLongerBookmarkedShortcuts.put(shortcut, false);
            }
        }
        
        if (noLongerBookmarkedShortcuts != null) {
            for (int i = noLongerBookmarkedShortcuts.size() - 1; i >= 0; i--) {
                if (noLongerBookmarkedShortcuts.valueAt(i)) {
                    // True, so there is no longer a bookmark for this shortcut
                    char shortcut = (char) noLongerBookmarkedShortcuts.keyAt(i);
                    ShortcutPreference pref = mShortcutToPreference.get(shortcut);
                    if (pref != null) {
                        pref.setHasBookmark(false);
                    }
                }
            }
        }
        
        mBookmarkedShortcuts = newBookmarkedShortcuts;
        
        c.deactivate();
    }

    private class BookmarksObserver extends ContentObserver {

        public BookmarksObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            
            refreshShortcuts();
        }
    }
}
