/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko;

import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UIAsyncTask;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.database.Cursor;
import android.support.design.widget.Snackbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;

/**
 * A dialog that allows editing a bookmarks url, title, or keywords
 * <p>
 * Invoked by calling one of the {@link org.mozilla.gecko.EditBookmarkDialog#show(String)}
 * methods.
 */
public class EditBookmarkDialog {
    private final Context mContext;

    public EditBookmarkDialog(Context context) {
        mContext = context;
    }

    /**
     * A private struct to make it easier to pass bookmark data across threads
     */
    private class Bookmark {
        final int id;
        final String title;
        final String url;
        final String keyword;

        public Bookmark(int aId, String aTitle, String aUrl, String aKeyword) {
            id = aId;
            title = aTitle;
            url = aUrl;
            keyword = aKeyword;
        }
    }

    /**
     * This text watcher to enable or disable the OK button if the dialog contains
     * valid information. This class is overridden to do data checking on different fields.
     * By itself, it always enables the button.
     *
     * Callers can also assign a paired partner to the TextWatcher, and callers will check
     * that both are enabled before enabling the ok button.
     */
    private class EditBookmarkTextWatcher implements TextWatcher {
        // A stored reference to the dialog containing the text field being watched
        protected AlertDialog mDialog;

        // A stored text watcher to do the real verification of a field
        protected EditBookmarkTextWatcher mPairedTextWatcher;

        // Whether or not the ok button should be enabled.
        protected boolean mEnabled = true;

        public EditBookmarkTextWatcher(AlertDialog aDialog) {
            mDialog = aDialog;
        }

        public void setPairedTextWatcher(EditBookmarkTextWatcher aTextWatcher) {
            mPairedTextWatcher = aTextWatcher;
        }

        public boolean isEnabled() {
            return mEnabled;
        }

        // Textwatcher interface
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // Disable if the we're disabled or the paired partner is disabled
            boolean enabled = mEnabled && (mPairedTextWatcher == null || mPairedTextWatcher.isEnabled());
            mDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled);
        }

        @Override
        public void afterTextChanged(Editable s) {}
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
    }

    /**
     * A version of the EditBookmarkTextWatcher for the url field of the dialog.
     * Only checks if the field is empty or not.
     */
    private class LocationTextWatcher extends EditBookmarkTextWatcher {
        public LocationTextWatcher(AlertDialog aDialog) {
            super(aDialog);
        }

        // Disables the ok button if the location field is empty.
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            mEnabled = (s.toString().trim().length() > 0);
            super.onTextChanged(s, start, before, count);
        }
    }

    /**
     * A version of the EditBookmarkTextWatcher for the keyword field of the dialog.
     * Checks if the field has any (non leading or trailing) spaces.
     */
    private class KeywordTextWatcher extends EditBookmarkTextWatcher {
        public KeywordTextWatcher(AlertDialog aDialog) {
            super(aDialog);
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // Disable if the keyword contains spaces
            mEnabled = (s.toString().trim().indexOf(' ') == -1);
            super.onTextChanged(s, start, before, count);
       }
    }

    /**
     * Show the Edit bookmark dialog for a particular url. If the url is bookmarked multiple times
     * this will just edit the first instance it finds.
     *
     * @param url The url of the bookmark to edit. The dialog will look up other information like the id,
     *            current title, or keywords associated with this url. If the url isn't bookmarked, the
     *            dialog will fail silently. If the url is bookmarked multiple times, this will only show
     *            information about the first it finds.
     */
    public void show(final String url) {
        final ContentResolver cr = mContext.getContentResolver();
        final BrowserDB db = GeckoProfile.get(mContext).getDB();
        (new UIAsyncTask.WithoutParams<Bookmark>(ThreadUtils.getBackgroundHandler()) {
            @Override
            public Bookmark doInBackground() {
                final Cursor cursor = db.getBookmarkForUrl(cr, url);
                if (cursor == null) {
                    return null;
                }

                Bookmark bookmark = null;
                try {
                    cursor.moveToFirst();
                    bookmark = new Bookmark(cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID)),
                                                          cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.TITLE)),
                                                          cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.URL)),
                                                          cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.KEYWORD)));
                } finally {
                    cursor.close();
                }
                return bookmark;
            }

            @Override
            public void onPostExecute(Bookmark bookmark) {
                if (bookmark == null) {
                    return;
                }

                show(bookmark.id, bookmark.title, bookmark.url, bookmark.keyword);
            }
        }).execute();
    }

    /**
     * Show the Edit bookmark dialog for a set of data. This will show the dialog whether
     * a bookmark with this url exists or not, but the results will NOT be saved if the id
     * is not a valid bookmark id.
     *
     * @param id The id of the bookmark to change. If there is no bookmark with this ID, the dialog
     *           will fail silently.
     * @param title The initial title to show in the dialog
     * @param url The initial url to show in the dialog
     * @param keyword The initial keyword to show in the dialog
     */
    public void show(final int id, final String title, final String url, final String keyword) {
        final Context context = mContext;

        AlertDialog.Builder editPrompt = new AlertDialog.Builder(context);
        final View editView = LayoutInflater.from(context).inflate(R.layout.bookmark_edit, null);
        editPrompt.setTitle(R.string.bookmark_edit_title);
        editPrompt.setView(editView);

        final EditText nameText = ((EditText) editView.findViewById(R.id.edit_bookmark_name));
        final EditText locationText = ((EditText) editView.findViewById(R.id.edit_bookmark_location));
        final EditText keywordText = ((EditText) editView.findViewById(R.id.edit_bookmark_keyword));
        nameText.setText(title);
        locationText.setText(url);
        keywordText.setText(keyword);

        final BrowserDB db = GeckoProfile.get(mContext).getDB();
        editPrompt.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int whichButton) {
                (new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
                    @Override
                    public Void doInBackground() {
                        String newUrl = locationText.getText().toString().trim();
                        String newKeyword = keywordText.getText().toString().trim();

                        db.updateBookmark(context.getContentResolver(), id, newUrl, nameText.getText().toString(), newKeyword);
                        return null;
                    }

                    @Override
                    public void onPostExecute(Void result) {
                        SnackbarHelper.showSnackbar((Activity) context,
                                context.getString(R.string.bookmark_updated),
                                Snackbar.LENGTH_LONG);
                    }
                }).execute();
            }
        });

        editPrompt.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int whichButton) {
                  // do nothing
              }
        });

        final AlertDialog dialog = editPrompt.create();

        // Create our TextWatchers
        LocationTextWatcher locationTextWatcher = new LocationTextWatcher(dialog);
        KeywordTextWatcher keywordTextWatcher = new KeywordTextWatcher(dialog);

        // Cross reference the TextWatchers
        locationTextWatcher.setPairedTextWatcher(keywordTextWatcher);
        keywordTextWatcher.setPairedTextWatcher(locationTextWatcher);

        // Add the TextWatcher Listeners
        locationText.addTextChangedListener(locationTextWatcher);
        keywordText.addTextChangedListener(keywordTextWatcher);

        dialog.show();
    }
}
