/*
 * 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.bluetooth;

import com.android.settings.R;
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;

import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.text.TextUtils;
import android.util.Log;

import java.util.List;

/**
 * ConnectSpecificProfilesActivity presents the user with all of the profiles
 * for a particular device, and allows him to choose which should be connected
 * (or disconnected).
 */
public class ConnectSpecificProfilesActivity extends PreferenceActivity
        implements CachedBluetoothDevice.Callback, Preference.OnPreferenceChangeListener {
    private static final String TAG = "ConnectSpecificProfilesActivity";

    private static final String KEY_ONLINE_MODE = "online_mode";
    private static final String KEY_TITLE = "title";
    private static final String KEY_PROFILE_CONTAINER = "profile_container";

    public static final String EXTRA_DEVICE = "device";

    public static final String CLASS_NAME_OPP_PROFILE_MANAGER =
        "com.android.settings.bluetooth.LocalBluetoothProfileManager$OppProfileManager";

    private LocalBluetoothManager mManager;
    private CachedBluetoothDevice mCachedDevice;
    
    private PreferenceGroup mProfileContainer;
    private CheckBoxPreference mOnlineModePreference;

    /**
     * The current mode of this activity and its checkboxes (either online mode
     * or offline mode). In online mode, user interactions with the profile
     * checkboxes will also toggle the profile's connectivity. In offline mode,
     * they will not, and only the preferred state will be saved for the
     * profile.
     */
    private boolean mOnlineMode;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        BluetoothDevice device;
        if (savedInstanceState != null) {
            device = savedInstanceState.getParcelable(EXTRA_DEVICE);
        } else {
            Intent intent = getIntent();
            device = intent.getParcelableExtra(EXTRA_DEVICE);
        }

        if (device == null) {
            Log.w(TAG, "Activity started without a remote blueototh device");
            finish();
        }
        
        mManager = LocalBluetoothManager.getInstance(this);
        mCachedDevice = mManager.getCachedDeviceManager().findDevice(device);
        if (mCachedDevice == null) {
            Log.w(TAG, "Device not found, cannot connect to it");
            finish();
        }

        addPreferencesFromResource(R.xml.bluetooth_device_advanced);
        mProfileContainer = (PreferenceGroup) findPreference(KEY_PROFILE_CONTAINER);
        
        // Set the title of the screen
        findPreference(KEY_TITLE).setTitle(
                getString(R.string.bluetooth_device_advanced_title, mCachedDevice.getName()));

        // Listen for check/uncheck of the online mode checkbox
        mOnlineModePreference = (CheckBoxPreference) findPreference(KEY_ONLINE_MODE);
        mOnlineModePreference.setOnPreferenceChangeListener(this);
        
        // Add a preference for each profile
        addPreferencesForProfiles();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        
        outState.putParcelable(EXTRA_DEVICE, mCachedDevice.getDevice());
    }

    @Override
    protected void onResume() {
        super.onResume();
        
        mManager.setForegroundActivity(this);
        mCachedDevice.registerCallback(this);

        refresh();
    }

    @Override
    protected void onPause() {
        super.onPause();
        
        mCachedDevice.unregisterCallback(this);
        mManager.setForegroundActivity(null);
    }

    private void addPreferencesForProfiles() {
        for (Profile profile : mCachedDevice.getProfiles()) {
            Preference pref = createProfilePreference(profile);
            mProfileContainer.addPreference(pref);
        }
    }

    /**
     * Creates a checkbox preference for the particular profile. The key will be
     * the profile's name.
     * 
     * @param profile The profile for which the preference controls.
     * @return A preference that allows the user to choose whether this profile
     *         will be connected to.
     */
    private CheckBoxPreference createProfilePreference(Profile profile) {
        CheckBoxPreference pref = new CheckBoxPreference(this);
        pref.setKey(profile.toString());
        pref.setTitle(profile.localizedString);
        pref.setPersistent(false);
        pref.setOnPreferenceChangeListener(this);

        LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
                .getProfileManager(mManager, profile);

        /**
         * Gray out checkbox while connecting and disconnecting or this is OPP
         * profile
         */
        if (profileManager.getClass().getName().equals(CLASS_NAME_OPP_PROFILE_MANAGER)) {
            pref.setEnabled(false);
        } else {
            pref.setEnabled(!mCachedDevice.isBusy());
        }

        refreshProfilePreference(pref, profile);
        
        return pref;
    }

    public boolean onPreferenceChange(Preference preference, Object newValue) {
        String key = preference.getKey();
        if (TextUtils.isEmpty(key) || newValue == null) return true;
        
        if (key.equals(KEY_ONLINE_MODE)) {
            onOnlineModeCheckedStateChanged((Boolean) newValue);
            
        } else {
            Profile profile = getProfileOf(preference);
            if (profile == null) return false;
            onProfileCheckedStateChanged(profile, (Boolean) newValue);
        }

        return true;
    }

    private void onOnlineModeCheckedStateChanged(boolean checked) {
        setOnlineMode(checked, true);
    }
    
    private void onProfileCheckedStateChanged(Profile profile, boolean checked) {
        if (mOnlineMode) {
            if (checked) {
                mCachedDevice.connect(profile);
            } else {
                mCachedDevice.disconnect(profile);
            }
        }
        
        LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
                .getProfileManager(mManager, profile);
        profileManager.setPreferred(mCachedDevice.getDevice(), checked);
    }
    
    public void onDeviceAttributesChanged(CachedBluetoothDevice cachedDevice) {
        refresh();
    }

    private void refresh() {
        // We are in 'online mode' if we are connected, connecting, or disconnecting
        setOnlineMode(mCachedDevice.isConnected() || mCachedDevice.isBusy(), false);
        refreshProfiles();
    }

    /**
     * Switches between online/offline mode.
     * 
     * @param onlineMode Whether to be in online mode, or offline mode.
     * @param takeAction Whether to take action (i.e., connect or disconnect)
     *            based on the new online mode.
     */
    private void setOnlineMode(boolean onlineMode, boolean takeAction) {
        mOnlineMode = onlineMode;
            
        if (takeAction) {
            if (onlineMode) {
                mCachedDevice.connect();
            } else {
                mCachedDevice.disconnect();
            }
        }
            
        refreshOnlineModePreference();
    }
    
    private void refreshOnlineModePreference() {
        mOnlineModePreference.setChecked(mOnlineMode);

        /* Gray out checkbox while connecting and disconnecting */
        mOnlineModePreference.setEnabled(!mCachedDevice.isBusy());

        List<Profile> profiles = mCachedDevice.getProfiles();
        if ((profiles.size() == 1) && (profiles.get(0).name().equals("OPP"))) {
            Log.w(TAG, "there is only one profile: Opp, disable the connect button.");
            mOnlineModePreference.setEnabled(false);
        }

        /**
         * If the device is online, show status. Otherwise, show a summary that
         * describes what the checkbox does.
         */
        mOnlineModePreference.setSummary(mOnlineMode ? mCachedDevice.getSummary()
                : R.string.bluetooth_device_advanced_online_mode_summary);
    }
    
    private void refreshProfiles() {
        for (Profile profile : mCachedDevice.getProfiles()) {
            CheckBoxPreference profilePref =
                    (CheckBoxPreference) findPreference(profile.toString());
            if (profilePref == null) {
                profilePref = createProfilePreference(profile);
                mProfileContainer.addPreference(profilePref);
            } else {
                refreshProfilePreference(profilePref, profile);
            }
        }
    }
    
    private void refreshProfilePreference(CheckBoxPreference profilePref, Profile profile) {
        BluetoothDevice device = mCachedDevice.getDevice();
        LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
                .getProfileManager(mManager, profile);
        
        int connectionStatus = profileManager.getConnectionStatus(device);

        /*
         * Gray out checkbox while connecting and disconnecting or this is OPP
         * profile
         */
        if (profileManager.getClass().getName().equals(CLASS_NAME_OPP_PROFILE_MANAGER)) {
            Log.w(TAG, "this is Opp profile");
            profilePref.setEnabled(false);
        } else {
            profilePref.setEnabled(!mCachedDevice.isBusy());
        }
        profilePref.setSummary(getProfileSummary(profileManager, profile, device,
                connectionStatus, mOnlineMode));
        
        profilePref.setChecked(profileManager.isPreferred(device));
    }

    private Profile getProfileOf(Preference pref) {
        if (!(pref instanceof CheckBoxPreference)) return null;
        String key = pref.getKey();
        if (TextUtils.isEmpty(key)) return null;
        
        try {
            return Profile.valueOf(pref.getKey());
        } catch (IllegalArgumentException e) {
            return null;
        }
    }

    private static int getProfileSummary(LocalBluetoothProfileManager profileManager,
            Profile profile, BluetoothDevice device, int connectionStatus, boolean onlineMode) {
        if (!onlineMode || connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) {
            return getProfileSummaryForSettingPreference(profile);
        } else {
            return profileManager.getSummary(device);
        }
    }
    
    /**
     * Gets the summary that describes when checked, it will become a preferred profile.
     * 
     * @param profile The profile to get the summary for.
     * @return The summary.
     */
    private static final int getProfileSummaryForSettingPreference(Profile profile) {
        switch (profile) {
            case A2DP:
                return R.string.bluetooth_a2dp_profile_summary_use_for;
            case HEADSET:
                return R.string.bluetooth_headset_profile_summary_use_for;
            case OPP:
                return R.string.bluetooth_opp_profile_summary_use_for;
            default:
                return 0;
        }
    }
    
}
