package andraus.bluetoothhidemu.settings;

import java.util.Set;

import andraus.bluetoothhidemu.BluetoothHidEmuActivity;
import andraus.bluetoothhidemu.R;
import andraus.bluetoothhidemu.spoof.Spoof;
import andraus.bluetoothhidemu.spoof.Spoof.SpoofMode;
import andraus.bluetoothhidemu.util.DoLog;
import andraus.bluetoothhidemu.view.BluetoothDeviceView;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;

/**
 * Main Settings screen.
 * 
 * TODO: Implement mechanism to "rename" bluetooth devices.
 * 
 */
public class Settings extends PreferenceActivity {

    private static final String TAG = BluetoothHidEmuActivity.TAG;
    
    public static final int BLUETOOTH_REQUEST_OK = 1;
    public static final int BLUETOOTH_REQUEST_DISCOVERABLE_FOR_PS3_OK = 2;
    public static final int BLUETOOTH_DISCOVERABLE_DURATION_100 = 100;
    public static final int BLUETOOTH_DISCOVERABLE_DURATION_5 = 5;
    
    public static final String FILE_PREF_DEVICES = "bt_devices";
    /* package */ final static String PREF_LAST_DEVICE = "last_device";
    /* package */ final static String PREF_EMULATION_MODE = "emulation_mode";
    private final static String PREF_BT_DISCOVERABLE = "bt_discoverable";
    private final static String PREF_DEVICE_LIST = "bt_device_list";

    private CheckBoxPreference mBtDiscoverablePreference = null;
    private ListPreference mEmulationModeListPreference = null;
    private PreferenceCategory mDeviceListCategory = null;

    // Handler to update screen elements
    private Handler mUiUpdateHandler = null;
    
    // counter for bluetooth discoverability timeout
    private int mCountdown = 0;
    
    // workaround for onResume() being called twice after bluetooth dialog
    private boolean mIsResumingFromDialog = false;
    
    private BluetoothDeviceStateReceiver mBluetoothDeviceReceiver = null;
    private BluetoothAdapterStateReceiver mBluetoothAdapterStateReceiver = null;
    
    
    // Runnable used with mUiUpdateHandler to display discoverability countdown
    private final Runnable mUpdateCountdownSummaryRunnable = new Runnable() {
        public void run() {
            
            if (BluetoothAdapter.getDefaultAdapter().getScanMode() == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
            
                
                if (mCountdown != Integer.MAX_VALUE && mCountdown > 0) {
                    mBtDiscoverablePreference.setSummary(
                            getResources().getQuantityString(
                                    R.plurals.msg_pref_summary_bluetooth_discoverable_timeout, 
                                    mCountdown, 
                                    mCountdown));
                    mCountdown--;
                    
                } else if (mCountdown == Integer.MAX_VALUE) {
                    mBtDiscoverablePreference.setSummary(R.string.msg_pref_summary_bluetooth_discoverable_no_timeout);
                }
                
                mUiUpdateHandler.postDelayed(this, 1000 /* ms */);
                
            } else {
                setBluetoothDiscoverableCheck(false);
            }
            
        }
    };


    /**
     * onCreate
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DoLog.d(TAG, "onCreate()");
        addPreferencesFromResource(R.xml.main_preferences);
        
        mBtDiscoverablePreference = (CheckBoxPreference) findPreference(PREF_BT_DISCOVERABLE);
        mEmulationModeListPreference = (ListPreference) findPreference(PREF_EMULATION_MODE);
        mDeviceListCategory = (PreferenceCategory) findPreference(PREF_DEVICE_LIST);
        populateDeviceList(mDeviceListCategory);

        mBluetoothDeviceReceiver = new BluetoothDeviceStateReceiver(mDeviceListCategory);
        registerReceiver(mBluetoothDeviceReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
        
        mBluetoothAdapterStateReceiver = new BluetoothAdapterStateReceiver(this);
        registerReceiver(mBluetoothAdapterStateReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
        
        mUiUpdateHandler = new Handler();
        
        mBtDiscoverablePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
            
            @Override
            public boolean onPreferenceClick(Preference preference) {
                
                // Do not enable the preference right away, need to fire the bluetooth discoverability intent first
                setBluetoothDiscoverableCheck(!mBtDiscoverablePreference.isChecked());
                
                if (!mBtDiscoverablePreference.isChecked()) {
                
                    startActivityForResult(createBluetoothDiscoverableIntent(Settings.BLUETOOTH_DISCOVERABLE_DURATION_100), Settings.BLUETOOTH_REQUEST_OK);
                }
                
                return false;
            }
            
        });
        
        mEmulationModeListPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
        	            
        	            @Override
        	            public boolean onPreferenceChange(Preference preference, Object newValue) {
        	
        	                
        	                mEmulationModeListPreference.setSummary(getEmulationModeSummary(getApplicationContext(), Integer.valueOf((String) newValue)));
        	                
        	                return true;
        	            }
        	        });

        
    }
    
    /**
     * 
     */
    @Override
    protected void onDestroy() {

        if (mBluetoothDeviceReceiver != null) {
            unregisterReceiver(mBluetoothDeviceReceiver);
        }
        if (mBluetoothAdapterStateReceiver != null) {
            unregisterReceiver(mBluetoothAdapterStateReceiver);
        }
        
        super.onDestroy();
    }

    /**
     * onResume
     */
    @Override
    protected void onResume() {
        super.onResume();
        
        if (!mIsResumingFromDialog) {
            
            setBluetoothDiscoverableCheck(BluetoothAdapter.getDefaultAdapter().getScanMode() == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
            mEmulationModeListPreference.setSummary(
            		                    getEmulationModeSummary(this, Integer.valueOf(
            		                            PreferenceManager.getDefaultSharedPreferences(this).getString(PREF_EMULATION_MODE, "-1"))));

            if (mBtDiscoverablePreference.isChecked()) {
                mCountdown = Integer.MAX_VALUE;
                mUiUpdateHandler.post(mUpdateCountdownSummaryRunnable);
            }
        } else {
            mIsResumingFromDialog = false;
        }
        
        
    }

    /**
     * onActivityResult - used to monitor bluetooth discoverable states    
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        DoLog.d(TAG, "onActivityResult: " + requestCode + " " + resultCode);
        mIsResumingFromDialog = true;
        
        if (requestCode == BLUETOOTH_REQUEST_OK && resultCode == BLUETOOTH_DISCOVERABLE_DURATION_100) {
            setBluetoothDiscoverableCheck(true);
            
            mUiUpdateHandler.removeCallbacksAndMessages(null);
            mCountdown = BLUETOOTH_DISCOVERABLE_DURATION_100;
            mUiUpdateHandler.post(mUpdateCountdownSummaryRunnable);
            
        } else if (requestCode == BLUETOOTH_REQUEST_OK && resultCode == RESULT_CANCELED) {
            setBluetoothDiscoverableCheck(false);
        }
        
        super.onActivityResult(requestCode, resultCode, data);
    }

    /**
     * getEmulationMode
     * 
     * @param context
     * @return
     */
    public static SpoofMode getPrefEmulationMode(Context context) {
    	
    	int value = Integer.valueOf(PreferenceManager.getDefaultSharedPreferences(context).getString(PREF_EMULATION_MODE, "-1"));

        return Spoof.fromInt(value);
        
    }
    
    /**
     * getLastConnectedDevice
     * 
     * @param context
     * @return
     */
    public static String getLastConnectedDevice(Context context) {
        String value = PreferenceManager.getDefaultSharedPreferences(context).getString(PREF_LAST_DEVICE, null);
        
        return value;
    }
    
    /**
     * setLastDevice
     * 
     * @param context
     * @param value
     */
    public static void setLastDevice(Context context, String value) {
        
        savePref(context, PREF_LAST_DEVICE, value);
    }
    
    /**
     * Returns emulation mode for the specified device
     * 
     * @param context
     * @param device
     * @return
     */
    public static SpoofMode getEmulationMode(Context context, BluetoothDevice device) {
        SharedPreferences devicesPref = context.getSharedPreferences(Settings.FILE_PREF_DEVICES, Context.MODE_PRIVATE);
        SpoofMode mode = Spoof.fromInt(devicesPref.getInt(device.getAddress(), Spoof.intValue(SpoofMode.INVALID)));

       return mode;
    }
    
    /**
     * Store emulation mode for the paired device as a shared preference
     * 
     * @param context
     * @param device
     */
    public static void storeDeviceEmulationMode(Context context, BluetoothDevice device, SpoofMode spoofMode) {
        SharedPreferences devicesPref = context.getSharedPreferences(Settings.FILE_PREF_DEVICES, Context.MODE_PRIVATE);
        
        SharedPreferences.Editor editor = devicesPref.edit();
        editor.putInt(device.getAddress(), Spoof.intValue(spoofMode));
        editor.apply();
    }
    
    /**
     * savePref
     * 
     * @param context
     * @param key
     * @param value
     */
    private static void savePref(Context context, String key, String value) {
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = sharedPref.edit();
        
        editor.putString(key, value);
        editor.apply();
        
    }
    
    /**
     * getEmulationModeSummary
     * 
     * @param modeIndex
     */
    public static String getEmulationModeSummary(Context context, int modeIndex) {
        String[] modeNames = context.getResources().getStringArray(R.array.emulation_mode_names);
        
        return modeNames[modeIndex];
    }
    
    /**
     * Toggle state for Bluetooth discoverable item
     * @param state
     */
    private void setBluetoothDiscoverableCheck(boolean state) {
        mBtDiscoverablePreference.setChecked(state);
        mBtDiscoverablePreference.setEnabled(!state);
        mEmulationModeListPreference.setEnabled(!state);
        if (!state) {
            mBtDiscoverablePreference.setSummary(getResources().getString(R.string.msg_pref_summary_bluetooth_discoverable_click));
        }
    }
    
    /**
     * 
     */
    private void populateDeviceList(PreferenceCategory deviceListCategory) {
        deviceListCategory.removeAll();
        Set<BluetoothDevice> deviceSet = BluetoothAdapter.getDefaultAdapter().getBondedDevices();
        
        for (BluetoothDevice device: deviceSet) {
            Preference devicePref = new Preference(this);
            devicePref.setTitle(device.getName().equals("") ? device.getAddress() : device.getName());
            
            BluetoothDeviceView.isBluetoothDevicePs3(device);
            
            SpoofMode spoofMode = getEmulationMode(this, device);
            String emulationSummary = (spoofMode == SpoofMode.INVALID) ? 
                    getResources().getString(R.string.msg_pref_summary_device_emulation_mode_invalid) :
                    getEmulationModeSummary(this, Spoof.intValue(spoofMode)); 
            
            devicePref.setSummary(device.getAddress() + "\n" 
                        + String.format(getResources().getString(R.string.msg_pref_summary_device_emulation_mode), 
                                emulationSummary));

            deviceListCategory.addPreference(devicePref);
        }
        
    }
    
    /**
     * 
     * @param context
     */
    public static void showPs3InvalidModeDialog(Context context) {
    	
	    AlertDialog dialog =  new AlertDialog.Builder(context).create();
	    dialog.setTitle(context.getResources().getString(R.string.msg_dialog_invalid_ps3_bonding_title));
	    dialog.setMessage(String.format(
	    						context.getResources().getString(R.string.msg_dialog_invalid_ps3_bonding_text), 
	    						getEmulationModeSummary(context, Spoof.intValue(SpoofMode.HID_GENERIC)),
	    						getEmulationModeSummary(context, Spoof.intValue(SpoofMode.HID_PS3KEYPAD))
	    						));
	    dialog.setButton(DialogInterface.BUTTON_NEUTRAL, context.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				dialog.dismiss();
				
			}
		});
	    
	    dialog.show();

    }
    
    /**
     * 
     * @param duration
     * @return
     */
    public static Intent createBluetoothDiscoverableIntent(final int duration) {
        Intent bluetoothIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
        bluetoothIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, duration);
        
        return bluetoothIntent;
    }
        
}