package com.atid.app.atx.ble.deviceoption;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.atid.app.atx.ble.deviceoption.data.DeviceItem;
import com.atid.app.atx.ble.deviceoption.data.GlobalData;
import com.atid.app.atx.ble.deviceoption.device.ReaderManager;
import com.atid.app.atx.ble.deviceoption.dialog.BaseDialog;
import com.atid.app.atx.ble.deviceoption.dialog.DateTimeDialog;
import com.atid.app.atx.ble.deviceoption.dialog.EnumListDialog;
import com.atid.app.atx.ble.deviceoption.dialog.MessageBox;
import com.atid.app.atx.ble.deviceoption.dialog.NotifyMethodDialog;
import com.atid.app.atx.ble.deviceoption.dialog.NumberUnitDialog;
import com.atid.app.atx.ble.deviceoption.dialog.WaitDialog;
import com.atid.lib.atx88.ATn88Reader;
import com.atid.lib.atx88.ATx88Reader;
import com.atid.lib.diagnostics.ATException;
import com.atid.lib.reader.ATEAReader;
import com.atid.lib.reader.event.IATEAReaderEventListener;
import com.atid.lib.reader.params.NotifyMethod;
import com.atid.lib.reader.types.KeyState;
import com.atid.lib.reader.types.KeyType;
import com.atid.lib.reader.types.NotifyTimeType;
import com.atid.lib.reader.types.OperationMode;
import com.atid.lib.reader.types.UsbChargerState;
import com.atid.lib.reader.types.UsbChargerType;
import com.atid.lib.transport.types.ConnectState;
import com.atid.lib.transport.types.ConnectType;
import com.atid.lib.types.ActionState;
import com.atid.lib.types.DeviceType;
import com.atid.lib.types.ResultCode;
import com.atid.lib.util.SysUtil;
import com.atid.lib.util.diagnotics.ATLog;

import java.util.ArrayList;
import java.util.Locale;

public class MainActivity extends Activity implements OnClickListener, IATEAReaderEventListener {

	private static final String TAG = MainActivity.class.getSimpleName();
	private static final int INFO = ATLog.L1;
	
	private static final int REQUEST_PERMISSION_CODE = 1000;
	private static final int REQUEST_ENABLE_BLUETOOTH = 1;
	
	private static final int LOADING_STATE_DEVICE_DISABLE_ACTION_KEY = 1;
	private static final int LOADING_STATE_DEVICE_FIRMWARE_VERSION = 2;
	private static final int LOADING_STATE_DEVICE_LOAD_DEVICE_TIME = 3;
	private static final int LOADING_STATE_DEVICE_LOAD_DISPLAY_OFF_TIME = 4;
	private static final int LOADING_STATE_DEVICE_LOAD_AUTO_OFF_TIME = 5;
	private static final int LOADING_STATE_DEVICE_BUTTON_MODE = 6;
	private static final int LOADING_STATE_DEVICE_BUTTON_NOTIFY = 7;
	private static final int LOADING_STATE_DEVICE_ALERT_NOTIFY = 8;			
	
	// ------------------------------------------------------------------------
	// Member Variable
	// ------------------------------------------------------------------------
	private TextView txtVersion;
	private TextView txtDevice;
	private TextView txtAddress;

	private LinearLayout linearFirmwareVersion;
	private LinearLayout linearSerialNo;
	private LinearLayout linearDeviceTime;
	private LinearLayout linearDisplayOffTime;
	private LinearLayout linearAutoOffTime;
	private LinearLayout linearButtonMode;
	private LinearLayout linearButtonNotify;
	private LinearLayout linearAlertNotify;
	
	private TextView txtFirmwareVersion;
	private TextView txtSerialNo;
	private TextView txtDisplayOffTime;
	private TextView txtAutoOffTime;
	private TextView txtButtonMode;
	private TextView txtButtonNotify;
	private TextView txtAlertNotify;
	
	private Button btnDefault;
	
	private String mFirmwareVersion;
	private String mSerialNo;

	private DateTimeDialog dlgTime;
	private NumberUnitDialog dlgDisplayOffTime;
	private NumberUnitDialog dlgAutoOffTime;
	private EnumListDialog dlgButtonMode;
	private NotifyMethodDialog dlgButtonNotify;
	private NotifyMethodDialog dlgAlertNotify;

	private ATEAReader mReader;
	
	private ActionState mAction;
	private volatile boolean mIsInitialize;
	private volatile boolean mIsConnected;
	
	private int mLoadingState;
	private ATException mLoadingError;

	private boolean mIsRunAppHomeButton = true;

	private PowerManager.WakeLock mWakeLock;
	private ActivityLifecycleManager mActivityLifecycleCallbacks;
	
	private BluetoothAdapter mBluetooth;
	private volatile boolean mIsCheckEnableBluetooth;
	private volatile boolean mIsSelectBluetooth;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		
		mActivityLifecycleCallbacks = new ActivityLifecycleManager();
		getApplication().registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
		
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		SysUtil.setContext(this);		// must used the ble device

		checkPermission();
		
		ATLog.i(TAG, INFO, "INFO. onCreate()");
	}

	@SuppressLint("NewApi")
	private void checkPermission() {
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
			
			final String[] permissionList = new String[] {
					Manifest.permission.WAKE_LOCK,
					Manifest.permission.BLUETOOTH_ADMIN,
					Manifest.permission.BLUETOOTH,
					Manifest.permission.ACCESS_FINE_LOCATION,
					Manifest.permission.ACCESS_COARSE_LOCATION,
					Manifest.permission.BLUETOOTH_SCAN,
					Manifest.permission.BLUETOOTH_CONNECT,
			};
			
			ArrayList<String> permissions = new ArrayList<String>();
			int grantResult = PackageManager.PERMISSION_GRANTED;
			for (int i = 0 ; i < permissionList.length ; i++) {
				grantResult = ContextCompat.checkSelfPermission(MainActivity.this, permissionList[i]);
				if (grantResult == PackageManager.PERMISSION_DENIED) {
					permissions.add(permissionList[i]);
				}
			}
			
			if (permissions.size() <= 0) {
				onPermissionResult(true);
			} else {
				String[] requestPermissions = new String[permissions.size()];
				//permissions.toArray(requestPermissions);
				ActivityCompat.requestPermissions(this, permissions.toArray(requestPermissions), REQUEST_PERMISSION_CODE);
				                                         
			}
		} else {
			onPermissionResult(true);
		}
	}
	
	@Override
	protected void onDestroy() {
		
		closeActivity();
		
		if (mReader != null) {
			mReader.removeListener(this);
			mReader.destroy();
		}
		super.onDestroy();
		getApplication().unregisterActivityLifecycleCallbacks(mActivityLifecycleCallbacks);		
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override 
	public boolean onPrepareOptionsMenu(Menu menu) {
		ATLog.i(TAG, INFO, "INFO. onPrepareOptionsMenu()");

		MenuItem menuAction = menu.findItem(R.id.menu_action);
		MenuItem menuSearh = menu.findItem(R.id.menu_search);
		
		if (mReader != null) {
			if (mReader.getState() == ConnectState.Connected) {
				menuAction.setTitle(R.string.action_disconnect);
			} else {
				menuAction.setTitle(R.string.action_connect);
			}
			
		}
			
		if (isAvailableBluetoothState()) {
			if (mIsSelectBluetooth) 
				menuAction.setEnabled(true);
			else
				menuAction.setEnabled(false);
			
			if (mReader != null) {
				if (mReader.getState() == ConnectState.Connected) {
					menuSearh.setEnabled(false);	
				} else {
					menuSearh.setEnabled(true);	
				}
			} else {
				menuSearh.setEnabled(true);
			}

		} else {
			Toast.makeText(this, "Current state of the local Bluetooth adapter is not ON", Toast.LENGTH_SHORT).show();
			menuAction.setEnabled(false);
			menuSearh.setEnabled(false);
		}
		
		return super.onPrepareOptionsMenu(menu);
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		int id = item.getItemId();
		switch(id) {
		case R.id.menu_action:
			if (null != mReader) {
				if(mReader.getState() == ConnectState.Connected) {
					mReader.disconnect();
				} else {
					if (!mReader.connect() ) {
						ATLog.e(TAG, "ERROR. onOptionsItemSelected - Failed to connect reader");
						MessageBox.show( this, getString(R.string.msg_not_connect_ble),
								getString(R.string.title_error));
					} else {
						DeviceItem deviceItem = new DeviceItem(ConnectType.BluetoothLe, mReader.getDeviceName(), mReader.getAddress());
						GlobalData.saveDeviceInfo(this, deviceItem);
					}
				}
			}
			return true;

		case R.id.menu_search:
			selectDevice();
			return true;
		default:
			break;
		}
		
		return super.onOptionsItemSelected(item);
	}
	
	@Override
	public void onUserLeaveHint() {
		
		if (!mIsRunAppHomeButton) {
			if (mReader != null) {
				closeActivity();
			}
		}
		ATLog.i(TAG, INFO, "INFO. onUserLeaveHint()");
		super.onUserLeaveHint();
	}	
	
	@Override
	public void onClick(View v) {

		if (!mIsConnected) {
			ATLog.e(TAG, "ERROR - onClick(%d) - Device is not connected !!",v.getId());
			return;
		}
		v.setEnabled(false);
		switch(v.getId()) {
		case R.id.default_option:
			mReader.defaultParameter();
			break;
		case R.id.linear_firmware_version:
			break;
		case R.id.linear_serial_no:
			break;
		case R.id.linear_device_time:
			if (mReader.getDeviceType() != DeviceType.ATS100 &&
					mReader.getDeviceType() != DeviceType.ATS200 &&
					mReader.getDeviceType() != DeviceType.ATD100) {
				onReaderSystemTime();			
			}
			break;
		case R.id.linear_display_off_time:
			if (mReader.getDeviceType() != DeviceType.ATS100 &&
					mReader.getDeviceType() != DeviceType.ATS200 &&
					mReader.getDeviceType() != DeviceType.ATD100) {
				onReaderDisplayOffTime();	
			}
			break;
		case R.id.linear_auto_off_time:
			onReaderAutoOffTime();			
			break;
		case R.id.linear_button_mode:
			onReaderButtonMode();			
			break;
		case R.id.linear_button_notify:
			onReaderButtonNotify();			
			break;
		case R.id.linear_alert_notify:
			onReaderAlertNotify();			
			break;
		}
		v.setEnabled(true);
	}


	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == SelectDeviceActivity.ID) {
			if (resultCode == Activity.RESULT_OK) {

				DeviceItem item = data.getParcelableExtra(SelectDeviceActivity.ITEM);
				
				if (null != mReader) 
					mReader.removeListener(this);
					
				mReader = ReaderManager.getReader(item.getConnectType(), item.getName(), item.getAddress(), false);
				if (mReader == null) {
					ATLog.e(TAG, "ERROR. onActivityResult(SelectDeviceActivity, [%s]) - %s",
							getString(R.string.msg_fail_load_reader_instance));
					MessageBox.show(this, R.string.msg_fail_load_reader_instance, 
							R.string.title_error, android.R.drawable.ic_dialog_alert,
							new DialogInterface.OnClickListener() {
						
						@Override
						public void onClick(DialogInterface dialog, int which) {
							MainActivity.this.finish();
							return;
						}
					});
					return;
				}
				// Set Reader Event Listener
				mReader.addListener(this);
				
				mIsSelectBluetooth = true;
				
				txtDevice.setText(item.getType().toString());
				txtAddress.setText(item.getAddress());
				
				ATLog.i(TAG, INFO, "INFO. onActivityResult(SelectDeviceActivity, [%s])", item.toString());
			}
		} else if (requestCode == REQUEST_ENABLE_BLUETOOTH) {
			if (resultCode == Activity.RESULT_OK) {

				checkSystem();

				ATLog.i(TAG, INFO, "INFO. onActivityResult(REQUEST_ENABLE_BLUETOOTH)");
				return;
			}
			GlobalData.isEnableBluetooth = false;
			return;
		}
		
		super.onActivityResult(requestCode, resultCode, data);
	}
	
	@Override
	public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
		if (requestCode != REQUEST_PERMISSION_CODE) {
			return;
		}
		
		boolean result = true;
		for (int i = 0 ; i < grantResults.length ; i++) {
			result &= ( grantResults[i] != PackageManager.PERMISSION_DENIED );
		}
		onPermissionResult(result);
	}	
	
	private void onPermissionResult(boolean result) {
		
		if (!result) {
			MessageBox.show(this, R.string.msg_fail_permission, R.string.title_error,
					android.R.drawable.ic_dialog_alert, new DialogInterface.OnClickListener() {
						
						@Override
						public void onClick(DialogInterface dialog, int which) {
							MainActivity.this.finish();
							return;
						}
					});
		}
		
		mReader = null;
		
		mFirmwareVersion = "";
		mSerialNo = "";

		dlgTime = null;
		dlgDisplayOffTime = null;
		dlgAutoOffTime = null;
		dlgButtonMode = null;
		dlgButtonNotify = null;
		dlgAlertNotify = null;
		
		mIsInitialize = false;
		mIsConnected = false;
		
		mAction = ActionState.Stop;
		
		mIsCheckEnableBluetooth = false;
		mIsSelectBluetooth = false;
		
		initActivity();

		Resources res = getResources();
		String unitSec = res.getString(R.string.unit_sec);

		dlgTime = new DateTimeDialog();
		dlgDisplayOffTime = new NumberUnitDialog(txtDisplayOffTime, unitSec);
		dlgAutoOffTime = new NumberUnitDialog(txtAutoOffTime, unitSec);
		dlgButtonMode = new EnumListDialog(txtButtonMode, NotifyTimeType.values());
		dlgButtonNotify = new NotifyMethodDialog(txtButtonNotify);
		dlgAlertNotify = new NotifyMethodDialog(txtAlertNotify);

		enableWidgets(false);
		
		// Initialize Bluetooth
		mBluetooth = BluetoothAdapter.getDefaultAdapter();

		checkSystem();

		DeviceItem item = GlobalData.loadDeviceInfo(this, ConnectType.BluetoothLe);
		if (null != item) {
			mReader = ReaderManager.getReader(item.getConnectType(), item.getName(), item.getAddress(), false);
			if (mReader == null) {
				ATLog.e(TAG, "ERROR. onActivityResult(SelectDeviceActivity, [%s]) - %s",
						getString(R.string.msg_fail_load_reader_instance));
				MessageBox.show(this, R.string.msg_fail_load_reader_instance, 
						R.string.title_error, android.R.drawable.ic_dialog_alert,
						new DialogInterface.OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						MainActivity.this.finish();
						return;
					}
				});
				return;
			}
			// Set Reader Event Listener
			mReader.addListener(this);
			
			mIsSelectBluetooth = true;

			txtDevice.setText(item.getType().toString());
			txtAddress.setText(item.getAddress());
		}		
	}	
	
	// ------------------------------------------------------------------------
	// Reader Event Handler Methods
	// ------------------------------------------------------------------------	

	@Override
	public void onReaderStateChanged(ATEAReader reader, ConnectState state, Object params) {

		switch(state) {
		case Connected:
			WaitDialog.hide();
			mIsConnected = true;
			loadProperties();
			break;
		case Connecting:
			WaitDialog.show(this, R.string.msg_connecting_ble);
			break;				
		case Disconnected:
			WaitDialog.hide();
			mIsConnected = false;
			enableWidgets(false);
			closeActivity();
			break;
		default:
			break;
		}
		ATLog.i(TAG, INFO, "EVENT. onReaderStateChanged([%s], %s)", reader, state);
	}
	
	@Override
	public void onReaderActionChanged(ATEAReader reader, ResultCode code, ActionState action, Object params) {
		
		if (mAction == ActionState.DefaultParameter && action == ActionState.Stop) {
			loadProperties();
		}
		mAction = action;

		ATLog.i(TAG, INFO, "EVENT. onReaderActionChanged([%s], %s, %s)", reader, code, action);
	}

	@Override
	public void onReaderOperationModeChanged(ATEAReader reader, OperationMode mode, Object params) {
		
		ATLog.i(TAG, INFO, "EVENT. onReaderOperationModeChanged([%s], %s)", reader, mode);
	}	
	
	@Override
	public void onReaderBatteryState(ATEAReader reader, int batteryState, Object params) {
		
		ATLog.i(TAG, INFO, "EVENT. onReaderBatteryState([%s], %d)", reader, batteryState);
	}

	@Override
	public void onReaderKeyChanged(ATEAReader reader, KeyType type, KeyState state, Object params) {
		
		ATLog.i(TAG, INFO, "EVENT. onReaderKeyChanged([%s], %s, %s)", reader, type, state);
	}
	
	@Override
	public void onReaderUsbChargerChanged(ATEAReader reader, UsbChargerType type, UsbChargerState state, Object params) {
		
		ATLog.i(TAG, INFO, "EVENT. onReaderUsbChargerChanged([%s], %s, %s)", reader, type, state);
	}

	// ------------------------------------------------------------------------
	// Internal Widget Control Methods
	// ------------------------------------------------------------------------
	private void initActivity() {
		txtVersion = (TextView) findViewById(R.id.app_version);
		txtVersion.setText(SysUtil.getVersion(this));
		txtDevice = (TextView) findViewById(R.id.bluetooth_device);
		txtAddress = (TextView) findViewById(R.id.bluetooth_address);
		
		linearFirmwareVersion = (LinearLayout) findViewById(R.id.linear_firmware_version);
		linearFirmwareVersion.setOnClickListener(this);
		linearSerialNo = (LinearLayout) findViewById(R.id.linear_serial_no);
		linearSerialNo.setOnClickListener(this);
		linearDeviceTime = (LinearLayout) findViewById(R.id.linear_device_time);
		linearDeviceTime.setOnClickListener(this);
		linearDisplayOffTime = (LinearLayout) findViewById(R.id.linear_display_off_time);
		linearDisplayOffTime.setOnClickListener(this);
		
		linearAutoOffTime = (LinearLayout) findViewById(R.id.linear_auto_off_time);
		linearAutoOffTime.setOnClickListener(this);
		linearButtonMode = (LinearLayout) findViewById(R.id.linear_button_mode);
		linearButtonMode.setOnClickListener(this);
		linearButtonNotify = (LinearLayout) findViewById(R.id.linear_button_notify);
		linearButtonNotify.setOnClickListener(this);

		linearAlertNotify = (LinearLayout) findViewById(R.id.linear_alert_notify);
		linearAlertNotify.setOnClickListener(this);
		
		txtFirmwareVersion = (TextView) findViewById(R.id.value_firmware_version);
		txtSerialNo = (TextView) findViewById(R.id.value_serial_no);       
		txtDisplayOffTime = (TextView) findViewById(R.id.value_display_off_time);
		txtAutoOffTime = (TextView) findViewById(R.id.value_auto_off_time);
		txtButtonMode = (TextView) findViewById(R.id.value_button_mode);
		txtButtonNotify = (TextView) findViewById(R.id.value_button_notify);
		txtAlertNotify = (TextView) findViewById(R.id.value_alert_notify);

		btnDefault = (Button) findViewById(R.id.default_option);
		btnDefault.setOnClickListener(this);
	}
	
	private void closeActivity() {
		WaitDialog.hide();
		
		if (mReader != null) {
			if (mReader.getAction() != ActionState.Stop) {

				while (mReader.getAction() != ActionState.Stop &&
						mReader.getState() == ConnectState.Connected) {
					
					try {
						ATLog.i(TAG, INFO, "closeActivity() - wait for action stop.");
						Thread.sleep(50);
					} catch (InterruptedException e) {
						ATLog.e(TAG, e, "closeActivity() - Falied to sleep for thread");
					}
				}
			}
		}

		ATLog.i(TAG, INFO, "INFO. closeActivity()");
	}	
	
	private void enableWidgets(boolean enabled) {
		
		linearFirmwareVersion.setEnabled(enabled);
		linearSerialNo.setEnabled(enabled);
		linearDeviceTime.setEnabled(enabled);
		linearDisplayOffTime.setEnabled(enabled);
		linearAutoOffTime.setEnabled(enabled);
		linearButtonMode.setEnabled(enabled);
		linearButtonNotify.setEnabled(enabled);
		linearAlertNotify.setEnabled(enabled);
		
		btnDefault.setEnabled(enabled);
		
		txtDevice.setEnabled(enabled || mIsConnected);
		txtAddress.setEnabled(enabled || mIsConnected);

		ATLog.i(TAG, INFO, "INFO. enableWidgets(%s)", enabled);
	}	
	
	// ------------------------------------------------------------------------
	// Internal Helper Methods
	// ------------------------------------------------------------------------
	
	private void onReaderSystemTime() {
		enableWidgets(false);
		dlgTime.showDialog(this, R.string.system_time, new BaseDialog.IValueChangedListener() {

			@Override
			public void onValueChanged(BaseDialog dialog) {
				try {
					mReader.setTime(dlgTime.getDateTime());
				} catch (ATException e) {
					ATLog.e(TAG, e, "ERROR. onReaderSystemTime() - Failed to set time [%s]",
							dlgTime.getDateTime().toString());

					showMessage(String.format(Locale.US, "%s\r\nError[%s]",
							getResources().getString(R.string.msg_fail_save_device_time), e.getCode()));
					
				}
				enableWidgets(true);
			}
		}, new BaseDialog.ICancelListener() {
			
			@Override
			public void onCanceled(BaseDialog dialog) {
				enableWidgets(true);
			}
		});
		ATLog.i(TAG, INFO, "INFO. onReaderSystemTime()");
	}
	
	private void onReaderDisplayOffTime() {
		enableWidgets(false);
		dlgDisplayOffTime.showDialog(this, R.string.display_off_time, new BaseDialog.IValueChangedListener() {

			@Override
			public void onValueChanged(BaseDialog dialog) {
				txtDisplayOffTime.setText(String.format(Locale.US, "%d %s",
						dlgDisplayOffTime.getValue(), getResources().getString(R.string.unit_sec)));
				
				try {
					((ATx88Reader) mReader).setDisplayOffTime(dlgDisplayOffTime.getValue());
				} catch (ATException e) {
					ATLog.e(TAG, e, "ERROR. onReaderDisplayOffTime() - Failed to set display off time [%d]",
							dlgDisplayOffTime.getValue());

					showMessage(String.format(Locale.US, "%s\r\nError[%s]",
							getResources().getString(R.string.msg_fail_save_device_display_off_time), e.getCode()));
					
				}
				enableWidgets(true);
			}
		}, new BaseDialog.ICancelListener() {
			
			@Override
			public void onCanceled(BaseDialog dialog) {
				enableWidgets(true);
			}
		});
		ATLog.i(TAG, INFO, "INFO. onReaderDisplayOffTime()");
	}
	
	private void onReaderAutoOffTime() {
		enableWidgets(false);
		dlgAutoOffTime.showDialog(this, R.string.auto_off_time, new BaseDialog.IValueChangedListener() {

			@Override
			public void onValueChanged(BaseDialog dialog) {
				txtAutoOffTime.setText(String.format(Locale.US, "%d %s",
						dlgAutoOffTime.getValue(), getResources().getString(R.string.unit_sec)));
				
				try {
					mReader.setAutoOffTime(dlgAutoOffTime.getValue());
				} catch (ATException e) {
					ATLog.e(TAG, e, "ERROR. onReaderAutoOffTime() - Failed to set auto off time [%d]",
							dlgAutoOffTime.getValue());

					showMessage(String.format(Locale.US, "%s\r\nError[%s]",
							getResources().getString(R.string.msg_fail_save_device_auto_off_time), e.getCode()));
					
				}
				enableWidgets(true);
			}
		}, new BaseDialog.ICancelListener() {
			
			@Override
			public void onCanceled(BaseDialog dialog) {
				enableWidgets(true);
			}
		});
		ATLog.i(TAG, INFO, "INFO. onReaderAutoOffTime()");
	}
	
	private void onReaderButtonMode() {
		enableWidgets(false);
		dlgButtonMode.showDialog(this, R.string.button_mode, new BaseDialog.IValueChangedListener() {

			@Override
			public void onValueChanged(BaseDialog dialog) {
				txtButtonMode.setText(dlgButtonMode.getValue().toString());
				
				try {
					((ATn88Reader) mReader).setButtonNotifyTime((NotifyTimeType) dlgButtonMode.getValue());
				} catch (ATException e) {
					ATLog.e(TAG, e, "ERROR. onReaderButtonMode() - Failed to set notify term [%s]",
							dlgButtonMode.getValue());

					showMessage(String.format(Locale.US, "%s\r\nError[%s]",
							getResources().getString(R.string.msg_fail_save_device_button_mode), e.getCode()));
					
					dlgButtonMode.restoreValue();
				}
				enableWidgets(true);
			}
		}, new BaseDialog.ICancelListener() {
			
			@Override
			public void onCanceled(BaseDialog dialog) {
				enableWidgets(true);
			}
		});
		ATLog.i(TAG, INFO, "INFO. onReaderButtonMode()");
	}
	
	private void onReaderButtonNotify() {
		enableWidgets(false);
		dlgButtonNotify.showDialog(this, R.string.button_notify, new BaseDialog.IValueChangedListener() {

			@Override
			public void onValueChanged(BaseDialog dialog) {
				
				try {
					((ATn88Reader) mReader).setButtonNotify((NotifyMethod) dlgButtonNotify.getMethod());
				} catch (ATException e) {
					ATLog.e(TAG, e, "ERROR. onReaderButtonNotify() - Failed to set button notify [%s]",
							dlgButtonNotify.getMethod());

					showMessage(String.format(Locale.US, "%s\r\nError[%s]",
							getResources().getString(R.string.msg_fail_save_device_button_notify), e.getCode()));
					
					dlgButtonNotify.restoreMethod();
				}
				enableWidgets(true);
			}
		}, new BaseDialog.ICancelListener() {
			
			@Override
			public void onCanceled(BaseDialog dialog) {
				enableWidgets(true);
			}
		});
		ATLog.i(TAG, INFO, "INFO. onReaderButtonNotify()");
	}
	
	private void onReaderAlertNotify() {
		enableWidgets(false);
		dlgAlertNotify.showDialog(this, R.string.alert_notify, new BaseDialog.IValueChangedListener() {

			@Override
			public void onValueChanged(BaseDialog dialog) {
				txtAlertNotify.setText(dlgAlertNotify.getMethod().toString());
				
				try {
					((ATn88Reader) mReader).setAlertNotify((NotifyMethod) dlgAlertNotify.getMethod());
				} catch (ATException e) {
					ATLog.e(TAG, e, "ERROR. onReaderAlertNotify() - Failed to set alert notify [%s]",
							dlgAlertNotify.getMethod());

					showMessage(String.format(Locale.US, "%s\r\nError[%s]",
							getResources().getString(R.string.msg_fail_save_device_alert_notify), e.getCode()));
					
					dlgAlertNotify.restoreMethod();
				}
				enableWidgets(true);
			}
		}, new BaseDialog.ICancelListener() {
			
			@Override
			public void onCanceled(BaseDialog dialog) {
				enableWidgets(true);
			}
		});
		ATLog.i(TAG, INFO, "INFO. onReaderAlertNotify()");
	}
	
	@SuppressWarnings("unused")
	private void showMessage(final int message) {
		runOnUiThread( new Runnable() {
			@Override
			public void run() {
				MessageBox.show(MainActivity.this, message);
			}
		});
		
	}

	private void showMessage(final String message) {
		runOnUiThread( new Runnable() {
			@Override
			public void run() {
				MessageBox.show(MainActivity.this, message);
			}
		});
		
	}
	
	private void selectDevice() {
		Intent intent = new Intent(this, SelectDeviceActivity.class);
		intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

		startActivityForResult(intent, SelectDeviceActivity.ID);
		
		ATLog.i(TAG, INFO, "INFO. setting()");
	}

	private boolean isAvailableBluetoothState() {
		boolean available = false;
		
		if(mBluetooth == null)
			return available;
		
		int state = BluetoothAdapter.STATE_TURNING_ON;
		
		while(state == BluetoothAdapter.STATE_TURNING_OFF || state == BluetoothAdapter.STATE_TURNING_ON) {
			state = mBluetooth.getState();
		}
		
		if(state == BluetoothAdapter.STATE_ON) {
			available = true;
		} else {
			ATLog.i(TAG, INFO, "INFO. current state of the local Bluetooth adapter : " + state);
		}
		
		return available;
	}	

	private synchronized void checkSystem() {

		// Check Bluetooth
		if (mBluetooth == null) {
			// Not Support Bluetooth...
			GlobalData.isSupportBluetooth = false;
			ATLog.e(TAG, "INFO. checkSystem() - Not suppored bluetooth");
		} else {
			GlobalData.isSupportBluetooth = true;
			// Suppoted Bluetooth
			if (!mBluetooth.isEnabled() && !mIsCheckEnableBluetooth) {
				mIsCheckEnableBluetooth = true;
				// Disabled Bluetotoh
				GlobalData.isEnableBluetooth = false;
				Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
				startActivityForResult(intent, REQUEST_ENABLE_BLUETOOTH);
				ATLog.i(TAG, INFO, "INFO. checkSystem() - Request Enable Bluetooth");
				return;
			} else {
				GlobalData.isEnableBluetooth = true;
			}
		}

		
		if (!GlobalData.isSupportBluetooth) {
			MessageBox.show(this, R.string.msg_not_support_ble, R.string.title_bt);
		}

		ATLog.i(TAG, INFO, "INFO. checkSystem()");
	}

	// ------------------------------------------------------------------------
	// Load RFID Reader Properties
	// ------------------------------------------------------------------------
	private void loadProperties() {
		WaitDialog.show(this, R.string.msg_initialize_view);
		
		Thread thread = new Thread(mLoadingProperties);
		thread.start();
	}
	
	private Runnable mLoadingProperties = new Runnable() {
		@Override
		public void run() {
			mIsInitialize = loadingProperties();
			runOnUiThread(mLoadedProperties);
		}
	};
	
	private Runnable mLoadedProperties = new Runnable() {
		@Override
		public void run() {
			loadedProperties(mIsInitialize);
			WaitDialog.hide();
		}
	};
	
	private boolean loadingProperties() {

		if (mReader.getDeviceType() != DeviceType.ATD100) {
			if (mIsConnected) {
				// Disabled Use Key Action
				mLoadingState = LOADING_STATE_DEVICE_DISABLE_ACTION_KEY;
				try {
					mReader.setUseActionKey(false);
				} catch (ATException e) {
					mLoadingError = new ATException(e.getCode());
					ATLog.e(TAG, "ERROR. loadingProperties() - Failed to disabled key action");
					return false;
				}
			} else {
				ATLog.e(TAG, "ERROR. loadingProperties() - disconnected the bluetooth ");
				return false;
			}			
		}

		if (mIsConnected) {
			// Load Firmware Version
			mLoadingState = LOADING_STATE_DEVICE_FIRMWARE_VERSION;
			mFirmwareVersion = mReader.getVersion();
			// Load Serial No
			try {
				mSerialNo = mReader.getSerialNo();
			} catch (ATException e) {
				mLoadingError = new ATException(e.getCode());
				ATLog.e(TAG, e, "ERROR. loadingProperties() - Failed to load serial no");
				return false;
			}
		} else {
			ATLog.e(TAG, "ERROR. loadingProperties() - disconnected the bluetooth ");
			return false;
		}

		if (mIsConnected) {
			// Load Device Time
			if (mReader.getDeviceType() != DeviceType.ATS100 &&
					mReader.getDeviceType() != DeviceType.ATS200 &&
					mReader.getDeviceType() != DeviceType.ATD100) {
				mLoadingState = LOADING_STATE_DEVICE_LOAD_DEVICE_TIME;
				try {
					dlgTime.setDateTime(mReader.getTime());
				} catch (ATException e) {
					mLoadingError = new ATException(e.getCode());
					ATLog.e(TAG, e, "ERROR. loadingProperties() - Failed to load time");
					return false;
				}				
			}

		} else {
			ATLog.e(TAG, "ERROR. loadingProperties() - disconnected the bluetooth ");
			return false;
		}

		if (mIsConnected) {
			// Load Display Off Time
			if (mReader.getDeviceType() != DeviceType.ATS100 &&
					mReader.getDeviceType() != DeviceType.ATS200 &&
					mReader.getDeviceType() != DeviceType.ATD100) {
				mLoadingState = LOADING_STATE_DEVICE_LOAD_DISPLAY_OFF_TIME;
				try {
					dlgDisplayOffTime.setValue(((ATx88Reader) mReader).getDisplayOffTime());
				} catch (ATException e) {
					mLoadingError = new ATException(e.getCode());
					ATLog.e(TAG, e, "ERROR. loadingProperties() - Failed to load display off time");
					return false;
				}				
			}

		} else {
			ATLog.e(TAG, "ERROR. loadingProperties() - disconnected the bluetooth ");
			return false;
		}

		if (mIsConnected) {
			// Load Auto Off Time
			mLoadingState = LOADING_STATE_DEVICE_LOAD_AUTO_OFF_TIME;
			try {
				dlgAutoOffTime.setValue(mReader.getAutoOffTime());
			} catch (ATException e) {
				mLoadingError = new ATException(e.getCode());
				ATLog.e(TAG, e, "ERROR. loadingProperties() - Failed to load auto off time");
				return false;
			}
		} else {
			ATLog.e(TAG, "ERROR. loadingProperties() - disconnected the bluetooth ");
			return false;
		}

		if (mIsConnected) {
			// Button Mode
			mLoadingState = LOADING_STATE_DEVICE_BUTTON_MODE;
			try {
				dlgButtonMode.setValue(((ATn88Reader) mReader).getButtonNotifyTime());
			} catch (ATException e) {
				mLoadingError = new ATException(e.getCode());
				ATLog.e(TAG, e, "ERROR. loadingProperties() - Failed to load notify term");
				return false;
			}
		} else {
			ATLog.e(TAG, "ERROR. loadingProperties() - disconnected the bluetooth ");
			return false;
		}

		if (mIsConnected) {
			// Button Notify
			mLoadingState = LOADING_STATE_DEVICE_BUTTON_NOTIFY;
			try {
				dlgButtonNotify.setMethod(((ATn88Reader) mReader).getButtonNotify());
				dlgButtonNotify.setDeviceType(mReader.getDeviceType());
			} catch (ATException e) {
				mLoadingError = new ATException(e.getCode());
				ATLog.e(TAG, e, "ERROR. loadingProperties() - Failed to load button notify");
				return false;
			}
		} else {
			ATLog.e(TAG, "ERROR. loadingProperties() - disconnected the bluetooth ");
			return false;
		}

		if (mIsConnected) {
			// Alert Notify
			mLoadingState = LOADING_STATE_DEVICE_ALERT_NOTIFY;
			try {
				dlgAlertNotify.setMethod(((ATn88Reader) mReader).getAlertNotify());
				dlgAlertNotify.setDeviceType(mReader.getDeviceType());
			} catch (ATException e) {
				mLoadingError = new ATException(e.getCode());
				ATLog.e(TAG, e, "ERROR. loadingProperties() - Failed to load alert notify");
				return false;
			}
		} else {
			ATLog.e(TAG, "ERROR. loadingProperties() - disconnected the bluetooth ");
			return false;
		}
		
		ATLog.i(TAG, INFO, "INFO. loadingProperties()");
		return true;
	}
	
	private void loadedProperties(boolean isInitialize) {
		
		if (mReader.getDeviceType() == DeviceType.ATD100)
			btnDefault.setVisibility(View.INVISIBLE);
		else 
			btnDefault.setVisibility(View.VISIBLE);

		if (isInitialize) {
			
			txtFirmwareVersion.setText(String.format(Locale.US, "%s",mFirmwareVersion));
			txtSerialNo.setText(String.format(Locale.US, "%s",mSerialNo));
			dlgTime.display();
			
			if (mReader.getDeviceType() != DeviceType.ATS100 &&
					mReader.getDeviceType() != DeviceType.ATS200 &&
					mReader.getDeviceType() != DeviceType.ATD100) {
				linearDeviceTime.setVisibility(View.VISIBLE);
				linearDisplayOffTime.setVisibility(View.VISIBLE);
			} else {
				linearDeviceTime.setVisibility(View.GONE);
				linearDisplayOffTime.setVisibility(View.GONE);
			}
			
			if (mReader.getDeviceType() != DeviceType.ATS100 &&
					mReader.getDeviceType() != DeviceType.ATS200 &&
					mReader.getDeviceType() != DeviceType.ATD100) {
				dlgDisplayOffTime.display();	
			}
			
			dlgAutoOffTime.display();
			dlgButtonMode.display();
			dlgButtonNotify.display();
			dlgAlertNotify.display();
			
			enableWidgets(true);
		} else {
			enableWidgets(false);
			runOnUiThread(mFailedLoadProc);			
		}
		
		ATLog.i(TAG, INFO, "INFO. loadedProperties(%s)", isInitialize);
	}

	private Runnable mFailedLoadProc = new Runnable() {
		int failMessage;
		String message;
		
		@Override
		public void run() {
			switch(mLoadingState) {
			case LOADING_STATE_DEVICE_DISABLE_ACTION_KEY:
				failMessage = R.string.msg_fail_load_disabled_action_key;
				break;
			case LOADING_STATE_DEVICE_FIRMWARE_VERSION:
				failMessage = R.string.msg_fail_load_device_firmware_version;
				break;
			case LOADING_STATE_DEVICE_LOAD_DEVICE_TIME:
				failMessage = R.string.msg_fail_load_device_time;
				break;
			case LOADING_STATE_DEVICE_LOAD_DISPLAY_OFF_TIME:
				failMessage = R.string.msg_fail_load_device_display_off_time;
				break;
			case LOADING_STATE_DEVICE_LOAD_AUTO_OFF_TIME:
				failMessage = R.string.msg_fail_load_device_auto_off_time;
				break;
			case LOADING_STATE_DEVICE_BUTTON_MODE:
				failMessage = R.string.msg_fail_load_device_button_mode;
				break;
			case LOADING_STATE_DEVICE_BUTTON_NOTIFY:
				failMessage = R.string.msg_fail_load_device_button_notify;
				break;
			case LOADING_STATE_DEVICE_ALERT_NOTIFY:
				failMessage = R.string.msg_fail_load_device_alert_notify;
				break;
			}
			
			message = String.format(Locale.US, "%s\r\nError[%s]",
					getResources().getString(failMessage), mLoadingError.getCode());
			
			MessageBox.show(MainActivity.this, message, R.string.title_error, android.R.drawable.ic_menu_info_details 
					, new DialogInterface.OnClickListener() {
						
						@Override
						public void onClick(DialogInterface dialog, int which) {
							
						}
					});
		}
	};
	
	// ------------------------------------------------------------------------
	// Activity Life cycle
	// ------------------------------------------------------------------------
	public class ActivityLifecycleManager implements ActivityLifecycleCallbacks {

		private int mRefCount = 0;
		private String _tag = ActivityLifecycleManager.class.getSimpleName();
		
		@SuppressWarnings("deprecation")
		@Override
		public void onActivityStarted(Activity activity) {
			mRefCount++;
			ATLog.i(TAG, INFO, "INFO. ActivityLifecycleManager.onActivityStarted : " + mRefCount);
			
			if(mRefCount == 1) {
				// Setup always wake up
				Context context = activity.getApplicationContext();
				//SysUtil.wakeLock(activity.getApplicationContext(), SysUtil.getAppName(context));
				
				if(mWakeLock != null) {
					ATLog.i(TAG, INFO, "INFO. Already exist wake lock");
				}
				
				PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
				mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, _tag);
				//mWakeLock = powerManager.newWakeLock(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, TAG);
				mWakeLock.acquire();
				ATLog.i(TAG, INFO, "INFO. Acquires the wake lock.");
			}
		}
		
		@Override
		public void onActivityStopped(Activity activity) {
			mRefCount--;
			ATLog.i(TAG, INFO, "INFO. ActivityLifecycleManager.onActivityStopped : " + mRefCount);
			
			if(mRefCount == 0) {
				// release WakeLock.
				//SysUtil.wakeUnlock();
				
				if (mWakeLock == null)
					return;

				mWakeLock.release();
				mWakeLock = null;
				ATLog.i(TAG, INFO, "INFO. Releases the wake lock.");
			}
		}

		@Override
		public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
		@Override
		public void onActivityResumed(Activity activity) {}
		@Override
		public void onActivityPaused(Activity activity) {}
		@Override
		public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
		@Override
		public void onActivityDestroyed(Activity activity) {}
		
	}		
	
}
