/*
 * Copyright (C) 2012 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.mail.ui;

import com.android.mail.R;
import com.android.mail.analytics.Analytics;
import com.android.mail.providers.Account;
import com.android.mail.providers.MailAppProvider;
import com.android.mail.providers.UIProvider;
import com.android.mail.utils.LogTag;

import java.util.ArrayList;

import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.LoaderManager;
import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;

/**
 * An activity that shows the list of all the available accounts and return the
 * one selected in onResult().
 */
public class MailboxSelectionActivity extends ActionBarActivity implements OnClickListener,
        LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener {

    // Used to save our instance state
    private static final String CREATE_SHORTCUT_KEY = "createShortcut";
    private static final String CREATE_WIDGET_KEY = "createWidget";
    private static final String WIDGET_ID_KEY = "widgetId";
    private static final String WAITING_FOR_ADD_ACCOUNT_RESULT_KEY = "waitingForAddAccountResult";

    private static final String ACCOUNT = "name";
    private static final String[] COLUMN_NAMES = { ACCOUNT };
    protected static final String LOG_TAG = LogTag.getLogTag();
    private static final int RESULT_CREATE_ACCOUNT = 2;
    private static final int LOADER_ACCOUNT_CURSOR = 0;
    private static final String TAG_WAIT = "wait-fragment";
    private final int[] VIEW_IDS = { R.id.mailbox_name };
    private boolean mCreateShortcut = false;
    private boolean mConfigureWidget = false;
    private SimpleCursorAdapter mAdapter;
    private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;

    // Boolean to indicate that we are waiting for the result from an add account
    // operation.  This boolean is necessary, as there is no guarantee on whether the
    // AccountManager callback or onResume will be called first.
    boolean mWaitingForAddAccountResult = false;

    // Can only do certain actions if the Activity is resumed (e.g. setVisible)
    private boolean mResumed = false;
    private Handler mHandler = new Handler();
    private ListView mList;
    private View mContent;
    private View mWait;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.mailbox_selection_activity);
        mList = (ListView) findViewById(android.R.id.list);
        mList.setOnItemClickListener(this);
        mContent = findViewById(R.id.content);
        mWait = findViewById(R.id.wait);
        if (icicle != null) {
            restoreState(icicle);
        } else {
            if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
                mCreateShortcut = true;
            }
            mAppWidgetId = getIntent().getIntExtra(
                    AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
            if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
                mConfigureWidget = true;
            }
        }
        // We set the default title to "Gmail" or "Google Mail" for consistency
        // in Task Switcher. If this is for create shortcut or configure widget,
        // we should set the title to "Select account".
        if (mCreateShortcut || mConfigureWidget) {
            setTitle(getResources().getString(R.string.activity_mailbox_selection));
        }
        findViewById(R.id.first_button).setOnClickListener(this);

        // Initially, assume that the main view is invisible.  It will be made visible,
        // if we display the account list
        setVisible(false);
        setResult(RESULT_CANCELED);
    }

    @Override
    protected void onSaveInstanceState(Bundle icicle) {
        super.onSaveInstanceState(icicle);

        icicle.putBoolean(CREATE_SHORTCUT_KEY, mCreateShortcut);
        icicle.putBoolean(CREATE_WIDGET_KEY, mConfigureWidget);
        if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
            icicle.putInt(WIDGET_ID_KEY, mAppWidgetId);
        }
        icicle.putBoolean(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY, mWaitingForAddAccountResult);
    }

    @Override
    public void onStart() {
        super.onStart();

        Analytics.getInstance().activityStart(this);
    }

    @Override
    protected void onStop() {
        super.onStop();

        Analytics.getInstance().activityStop(this);
    }

    @Override
    public void onResume() {
        super.onResume();
        mResumed = true;
        // Only fetch the accounts, if we are not handling a response from the
        // launched child activity.
        if (!mWaitingForAddAccountResult) {
            setupWithAccounts();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        mResumed = false;
    }

    @Override
    public void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
    }

    /**
     * Restores the activity state from a bundle
     */
    private void restoreState(Bundle icicle) {
        if (icicle.containsKey(CREATE_SHORTCUT_KEY)) {
            mCreateShortcut = icicle.getBoolean(CREATE_SHORTCUT_KEY);
        }
        if (icicle.containsKey(CREATE_WIDGET_KEY)) {
            mConfigureWidget = icicle.getBoolean(CREATE_WIDGET_KEY);
        }
        if (icicle.containsKey(WIDGET_ID_KEY)) {
            mAppWidgetId = icicle.getInt(WIDGET_ID_KEY);
        }
        if (icicle.containsKey(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY)) {
            mWaitingForAddAccountResult = icicle.getBoolean(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY);
        }
    }

    private void setupWithAccounts() {
        final ContentResolver resolver = getContentResolver();
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                Cursor cursor = null;
                try {
                    cursor = resolver.query(MailAppProvider.getAccountsUri(),
                            UIProvider.ACCOUNTS_PROJECTION, null, null, null);
                    completeSetupWithAccounts(cursor);
                } finally {
                    if (cursor != null) {
                        cursor.close();
                    }
                }
                return null;
            }

        }.execute();
    }

    private void completeSetupWithAccounts(final Cursor accounts) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                updateAccountList(accounts);
            }
        });
    }

    private void updateAccountList(final Cursor accounts) {
        boolean displayAccountList = true;
        // Configuring a widget or shortcut.
        if (mConfigureWidget || mCreateShortcut) {
            if (accounts == null || accounts.getCount() == 0) {
                // No account found, show Add Account screen, for both the widget or
                // shortcut creation process
                // No account found, show Add Account screen, for both the widget or
                // shortcut creation process
                final Intent noAccountIntent = MailAppProvider.getNoAccountIntent(this);
                if (noAccountIntent != null) {
                    startActivityForResult(noAccountIntent, RESULT_CREATE_ACCOUNT);
                }
                // No reason to display the account list
                displayAccountList = false;

                // Indicate that we need to handle the response from the add account action
                // This allows us to process the results that we get in the AddAccountCallback
                mWaitingForAddAccountResult = true;
            } else if (mConfigureWidget && accounts.getCount() == 1) {
                mWait.setVisibility(View.GONE);
                // When configuring a widget, if there is only one account, automatically
                // choose that account.
                accounts.moveToFirst();
                selectAccount(Account.builder().buildFrom(accounts));
                // No reason to display the account list
                displayAccountList = false;
            }
        }

        if (displayAccountList) {
            mContent.setVisibility(View.VISIBLE);
            // We are about to display the list, make this activity visible
            // But only if the Activity is not paused!
            if (mResumed) {
                setVisible(true);
            }

            mAdapter = new SimpleCursorAdapter(this, R.layout.mailbox_item, accounts,
                    COLUMN_NAMES, VIEW_IDS, 0) {
                @Override
                public View getView(int position, View convertView, ViewGroup parent) {
                    View v = super.getView(position, convertView, parent);
                    TextView accountView = (TextView) v.findViewById(R.id.mailbox_name);
                    final Account account = Account.builder().buildFrom((Cursor) getItem(position));
                    accountView.setText(account.getDisplayName());
                    return v;
                }
            };
            mList.setAdapter(mAdapter);
        }
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        selectAccount(Account.builder().buildFrom((Cursor) mAdapter.getItem(position)));
    }

    private void selectAccount(Account account) {
        if (mCreateShortcut || mConfigureWidget) {
            // Invoked for a shortcut creation
            final Intent intent = new Intent(this, getFolderSelectionActivity());
            intent.setFlags(
                    Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
            intent.setAction(mCreateShortcut ?
                    Intent.ACTION_CREATE_SHORTCUT : AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
            if (mConfigureWidget) {
                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
            }
            intent.putExtra(FolderSelectionActivity.EXTRA_ACCOUNT_SHORTCUT, account);
            startActivity(intent);
            finish();
        } else {
            // TODO: (mindyp) handle changing the account for this shortcut.
            finish();
        }
    }

    /**
     * Return the class responsible for launching the folder selection activity.
     */
    protected Class<?> getFolderSelectionActivity() {
        return FolderSelectionActivity.class;
    }

    @Override
    public void onClick(View v) {
        final int id = v.getId();
        if (id == R.id.first_button) {
            setResult(RESULT_CANCELED);
            finish();
        }
    }

    @Override
    protected final void onActivityResult(int request, int result, Intent data) {
        if (request == RESULT_CREATE_ACCOUNT) {
            // We were waiting for the user to create an account
            if (result != RESULT_OK) {
                finish();
            } else {
                // Watch for accounts to show up!
                // restart the loader to get the updated list of accounts
                getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, null, this);
                showWaitFragment(null);
            }
        }
    }

    private void showWaitFragment(Account account) {
        WaitFragment fragment = getWaitFragment();
        if (fragment != null) {
            fragment.updateAccount(account);
        } else {
            mWait.setVisibility(View.VISIBLE);
            replaceFragment(WaitFragment.newInstance(account, false /* expectingMessages */),
                    FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_WAIT);
        }
        mContent.setVisibility(View.GONE);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        switch (id) {
            case LOADER_ACCOUNT_CURSOR:
                return new CursorLoader(this, MailAppProvider.getAccountsUri(),
                        UIProvider.ACCOUNTS_PROJECTION, null, null, null);
        }
        return null;
    }

    private WaitFragment getWaitFragment() {
        return (WaitFragment) getFragmentManager().findFragmentByTag(TAG_WAIT);
    }

    private int replaceFragment(Fragment fragment, int transition, String tag) {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.setTransition(transition);
        fragmentTransaction.replace(R.id.wait, fragment, tag);
        final int transactionId = fragmentTransaction.commitAllowingStateLoss();
        return transactionId;
    }

    @Override
    public void onLoaderReset(Loader<Cursor> arg0) {
        // Do nothing.
    }

    @Override
    public void onLoadFinished(Loader<Cursor> cursor, Cursor data) {
        if (data != null && data.moveToFirst()) {
            // there are accounts now!
            Account account;
            ArrayList<Account> accounts = new ArrayList<Account>();
            ArrayList<Account> initializedAccounts = new ArrayList<Account>();
            do {
                account = Account.builder().buildFrom(data);
                if (account.isAccountReady()) {
                    initializedAccounts.add(account);
                }
                accounts.add(account);
            } while (data.moveToNext());
            if (initializedAccounts.size() > 0) {
                mWait.setVisibility(View.GONE);
                getLoaderManager().destroyLoader(LOADER_ACCOUNT_CURSOR);
                mContent.setVisibility(View.VISIBLE);
                updateAccountList(data);
            } else {
                // Show "waiting"
                account = accounts.size() > 0 ? accounts.get(0) : null;
                showWaitFragment(account);
            }
        }
    }

    @Override
    public void onBackPressed() {
        mWaitingForAddAccountResult = false;
        // If we are showing the wait fragment, just exit.
        if (getWaitFragment() != null) {
            finish();
        } else {
            super.onBackPressed();
        }
    }
}
