package kr.co.icontrols.iotplatform;

import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import kr.co.icontrols.iotplatform.AWS.AmazonBLEinfo;
import kr.co.icontrols.iotplatform.Protocol.bleProtocol;
import kr.co.icontrols.iotplatform.Protocol.bleProtocolState;
import kr.co.icontrols.iotplatform.utils.hdcUUID;

import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;
import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT;
import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_BUZZER_PROTOCOL_TIMEOUT;
import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT;
import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT;
import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_INIT_GROUP_LIGHT_TIMEOUT;
import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT;
import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.SCAN_RESULT_PROTOCOL_FAIL;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.IsRunningProgress;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.appendByteArray;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.byteArrayToHex;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.getProgressDialog;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.isBlueToothEnable;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.isPasswordValid;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_ENABLE_BT_MAIN;
import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_LIGHT_BLINK_SUCCESS;
import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RETRY_BUT_NO_DEVICE;
import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RETRY_SCAN_DEVICE;
import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_SWITCH_MAPPER_BUZZER_SUCCESS;

public class SwitchMapperRegisterActivity extends AppCompatActivity implements View.OnClickListener {

    private final String TAG = "SwitchMapperRegister";

    private final int  SCAN_TYPE_NOTHING = -1;
    private final int  SCAN_TYPE_SWITCH = 0;
    private final int  SCAN_TYPE_LIGHT = 1;
    private int scanType = SCAN_TYPE_SWITCH;

    // Switch scan ui
    private LinearLayout layoutSwitchList;
    private TextView txtSwitch;
    private ListView listViewSwitch;
    private DeviceArrayAdapter switchArrayAdapter;
    private List<AmazonBLEinfo> swithList = new ArrayList<>();


    // Light scan ui
    private LinearLayout layoutLightList;
    private TextView txtLight;
    private ListView listViewLight;
    private DeviceArrayAdapter lightArrayAdapter;
    private List<AmazonBLEinfo> lightList = new ArrayList<>();
    private List<String> lightListSelected = new ArrayList<>();
    private List<String> lightScanList = new ArrayList<>();

    // completed ui
    private LinearLayout layoutCompleted;
    private TextView txtCompleted;

    // Bluetooth
    private static final int REQUEST_ENABLE_BT = 1;
    private static final int PERMISSION_REQUEST_FINE_LOCATION = 1;

    // ble
    private Handler handler;
    //private BluetoothGatt bluetoothGatt;
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothGattCharacteristic bluetoothGattCharacteristic;
    private Map<String, BluetoothGatt> connectedDeviceMap;

    //status
    private final int UI_STATUS_LOADING = 0;
    private final int UI_STATUS_SHOW_SWITCH_LIST = 1;
    private final int UI_STATUS_SHOW_LIGHT_LIST = 3;
    private final int UI_STATUS_SHOW_FAIL = 4;
    private final int UI_STATUS_SHOW_COMPLETED = 5;
    private final int UI_STATUS_SHOW_ETC = 6;

    private final int PROCESS_STATUS_NOTHING = 0;
    private final int PROCESS_STATUS_BUZZER = 1;
    private final int PROCESS_STATUS_BLINK = 2;
    private final int PROCESS_STATUS_SCAN_LIGHT = 3;
    private final int PROCESS_STATUS_GROUPING_SWITCH = 4;
    private final int PROCESS_STATUS_GROUPING_LIGHT = 5;
    private final int PROCESS_STATUS_INIT_SWITCH = 6;
    private final int PROCESS_STATUS_INIT_LIGHT = 7;

    //retry to scan device
    private boolean isRetry = false;

    private int status_process = PROCESS_STATUS_NOTHING;
    private int selectedSwitchPosition = 0;
    private int selectedLightPosition = 0;
    private int sendIndexLight = 0;
    private String selectedSwitchMac = "";
    private String selectedLightMac = "";
    private Handler switchRegisterHandler;
    private static final long SCAN_PERIOD = 5000;
    private int FIRST_SCAN_PERIOD = 500;
    private int SCAN_COUNT = 3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_switch_mapper_register);

        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_FINE_LOCATION);

        final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        if (bluetoothManager != null) {
            bluetoothAdapter = bluetoothManager.getAdapter();
        }

        setUI();

        // Switch Device Selected & alarm buzzer
        listViewSwitch.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                final AmazonBLEinfo item = (AmazonBLEinfo) parent.getItemAtPosition(position);
                selectedSwitchMac = item.getMacAddr();
                selectedSwitchPosition = position;
                startSwitchBuzzer();
            }
        });

        listViewLight.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                final AmazonBLEinfo item = (AmazonBLEinfo) parent.getItemAtPosition(position);
                selectedLightMac = item.getMacAddr();
                selectedLightPosition = position;

                if(lightListSelected.contains(selectedLightMac)) {
                    lightListSelected.remove(selectedLightMac);
                    lightArrayAdapter.notifyDataSetChanged();
                    selectedLightPosition = -1;
                } else {
                    startBlinkLight();
                }
            }
        });

        switchRegisterHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                DebugLogOutput(TAG, "[handleMessage what] : " + msg.what);
                switch (msg.what) {
                    case bleProtocolState.BLUETOOTH_CONNECTED:
                        //Toast.makeText(SwitchMapperRegisterActivity.this, "BLE Connected", Toast.LENGTH_SHORT).show();
                        DebugLogOutput(TAG, "[Switch Register Protocol] Switch Device Connect");
                        break;

                    case bleProtocolState.BLUETOOTH_DISCONNECTED:
                        //Toast.makeText(SwitchMapperRegisterActivity.this, "Switch Device Disconnect", Toast.LENGTH_SHORT).show();
                        DebugLogOutput(TAG, "[Buzzer Protocol] Switch Device Disconnect");
                        break;

                    case bleProtocolState.BLUETOOTH_NOT_CONNECTED:
                        //Toast.makeText(SwitchMapperRegisterActivity.this, "BLE Connect Fail", Toast.LENGTH_SHORT).show();
                        showNotiMsg(SwitchMapperRegisterActivity.this,"블루투스 연결을 실패하였습니다.");
                        DebugLogOutput(TAG, "[Switch Register Protocol] BLE Connect Fail");
                        if(layoutLightList.getVisibility() != View.VISIBLE) {
                            retryScanDevice();
                        } else {
                            if(IsRunningProgress()){
                                DismissConnectDialog();
                            }
                        }
                        break;

                    case bleProtocolState.BLUETOOTH_GET_NOTIFICATION:
                        switchRegisterHandler.removeMessages(bleProtocolState.BLUETOOTH_GET_NOTIFICATION_FAIL);
                        //Toast.makeText(SwitchMapperRegisterActivity.this, "Mac : " + msg.obj, Toast.LENGTH_SHORT).show();
                        DebugLogOutput(TAG, "[Switch Register Protocol] Get MAC Success");
                        bleDisconnect(selectedSwitchMac);
                        break;

                    case bleProtocolState.BLUETOOTH_GET_NOTIFICATION_FAIL:
                        //Toast.makeText(SwitchMapperRegisterActivity.this, "Time Over 5' Sec", Toast.LENGTH_SHORT).show();
                        DebugLogOutput(TAG, "[Switch Register Protocol] BLUETOOTH_GET_NOTIFICATION_FAIL");
                        bleDisconnect(selectedSwitchMac);
                        break;
                    case bleProtocolState.RSP_BUZZER_PROTOCOL_SUCCESS:{
                        switchRegisterHandler.removeMessages(RSP_BUZZER_PROTOCOL_TIMEOUT);
                        //bleDisconnect(selectedSwitchMac);
                        changeUI(UI_STATUS_SHOW_SWITCH_LIST);
                        Intent intent = new Intent(SwitchMapperRegisterActivity.this, PopupActivity.class);
                        intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
                        intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_confirm_register_switch_mapper_title));
                        intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_confirm_register_switch_mapper_description));
                        intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.yes));
                        intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.re_select));
                        startActivityForResult(intent, RESULT_SWITCH_MAPPER_BUZZER_SUCCESS);
                        }
                        break;
                    case bleProtocolState.RSP_BUZZER_PROTOCOL_FAIL:
                        switchRegisterHandler.removeMessages(RSP_BUZZER_PROTOCOL_TIMEOUT);
                        //bleDisconnect(selectedSwitchMac);
                        changeUI(UI_STATUS_SHOW_SWITCH_LIST,"스마트 스위치 연결음 송신에 실패하였습니다.");
                        break;
                    case RSP_BUZZER_PROTOCOL_TIMEOUT:
                        //bleDisconnect(selectedSwitchMac);
                        changeUI(UI_STATUS_SHOW_SWITCH_LIST,"스마트 스위치 연결시간이 초과되었습니다.");
                        break;
                    case bleProtocolState.RSP_BLINK_PROTOCOL_SUCCESS:{
                        switchRegisterHandler.removeMessages(RSP_BLINK_PROTOCOL_TIMEOUT);
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST);
                        Intent intent = new Intent(SwitchMapperRegisterActivity.this, PopupActivity.class);
                        intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
                        intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_confirm_register_light_title));
                        intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_confirm_register_light_description));
                        intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.yes));
                        intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.re_select));
                        startActivityForResult(intent, RESULT_LIGHT_BLINK_SUCCESS);
                        }
                        break;
                    case bleProtocolState.RSP_BLINK_PROTOCOL_FAIL:
                        switchRegisterHandler.removeMessages(RSP_BLINK_PROTOCOL_TIMEOUT);
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 깜빡임 요청 중에 오류가 발생하였습니다.");
                        //TODO 스마트 조명 깜빡임 실패시 로직 구현해야함.
                        break;
                    case RSP_BLINK_PROTOCOL_TIMEOUT:
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 깜빡임 요청시간을 초과되었습니다.");
                        //TODO 스마트 조명 깜빡임 Timeout 로직 구현해야함.
                        break;
                    case bleProtocolState.SCAN_RESULT_PROTOCOL_SUCCESS:
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"등록하실 스마트 조명를 선택해 주세요\n선택하신 스마트 조명이 깜빡입니다.");
                        switchRegisterHandler.removeMessages(bleProtocolState.SCAN_RESULT_PROTOCOL_FAIL);
                        //TODO - 현재 스마트 스위치에서 스캔된 모듈 정보와 단말기에서 스캔한 모듈 정보 둘 다 존재할 경우로 작업했음.(차후 스마트 스위치에서 스캔된 모듈로 표시해야 될수도 있음)
                        for(int i = 0 ; i < lightList.size() ; i++) {
                            boolean isExist = false;
                            for(String s : lightScanList) {
                                if(lightList.get(i).getMacAddr().equals(s)){
                                    isExist = true;
                                }
                            }
                            if(!isExist) {
                                lightList.remove(i--);
                            }
                        }
                        lightArrayAdapter.notifyDataSetChanged();
                        break;
                    case bleProtocolState.SCAN_RESULT_PROTOCOL_FAIL:
                        changeUI(UI_STATUS_SHOW_SWITCH_LIST,"스마트 스위치의 검색 요청이 실패하였습니다.");
                        //TODO 스마트 스위치에서 스마트 조명 스캔 실패시.
                        break;
                    case bleProtocolState.RSP_GROUP_SWITCH_SUCCESS:
                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT);
                        sendIndexLight = 0;
                        startGroupForLight(sendIndexLight++);
                        //initGroupDataForLight(sendIndexLight++);  //스마트 조명 그루핑 초기화 과정 제외.
                        break;
                    case bleProtocolState.RSP_GROUP_SWITCH_FAIL:
                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT);
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 스위치의 그룹 요청이 실패하였습니다.");
                        break;
                    case bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT:
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 스위치의 그룹 요청시간을 초과하였습니다.");
                        break;
                    case bleProtocolState.RSP_GROUP_LIGHT_SUCCESS:
                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT);
                        if(lightListSelected.size() > sendIndexLight) {
                            startGroupForLight(sendIndexLight++);
                        } else {
                            RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_END, null);
                        }
                        break;
                    case bleProtocolState.RSP_GROUP_LIGHT_FAIL:
                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT);
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 그룹 요청이 실패하였습니다.");
                        break;
                    case bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT:
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 그룹 요청시간을 초과하였습니다.");
                        break;
                    case bleProtocolState.RSP_GROUP_END:
                        changeUI(UI_STATUS_SHOW_COMPLETED,"스마트 스위치 등록이 완료되었습니다.");
                        for(String key : connectedDeviceMap.keySet()){
                            DebugLogOutput(TAG, "onSuccessed] connectedDeviceMap bluetoothGatt disconnect / MAC :"+key);
                            connectedDeviceMap.get(key).disconnect();
                            connectedDeviceMap.get(key).close();
                        }
                        connectedDeviceMap.clear();
                        break;
                    case bleProtocolState.RSP_INIT_GROUP_SWITCH_SUCCES:
                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT);
                        startGroupForSwitch();
                        break;
                    case bleProtocolState.RSP_INIT_GROUP_SWITCH_FAIL:
                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT);
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 스위치 그룹 초기화 요청이 실패하였습니다.");
                        break;
                    case bleProtocolState.RSP_INIT_GROUP_SWITCH_FAIL_OVER_LIGHT:
                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT);
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 스위치 초기화 버튼(독서버튼 5초)을 눌러 초기화 하세요.");
                        break;
                    case RSP_INIT_GROUP_SWITCH_TIMEOUT:
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 스위치 그룹 초기화 시간을 초과하였습니다.");
                        break;
                    case bleProtocolState.RSP_INIT_GROUP_LIGHT_SUCCES:
                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_INIT_GROUP_LIGHT_TIMEOUT);
                        if(lightListSelected.size() > sendIndexLight) {
                            initGroupDataForLight(sendIndexLight++);
                        } else {
                            sendIndexLight = 0;
                            startGroupForLight(sendIndexLight++);
                        }
                        break;
                    case bleProtocolState.RSP_INIT_GROUP_LIGHT_FAIL:
                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_INIT_GROUP_LIGHT_TIMEOUT);
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 그룹 초기화 요청이 실패하였습니다.");
                        break;
                    case RSP_INIT_GROUP_LIGHT_TIMEOUT:
                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 그룹 초기화 시간을 초과하였습니다.");
                        break;
                    default:
                        throw new IllegalStateException("Unexpected value: " + msg.what);
                }
            }
        };
        checkEnableBluetooth();
    }

    private void startSwitchBuzzer() {
        changeUI(UI_STATUS_LOADING,"스마트 스위치로 연결음 요청 중입니다...");
        status_process = PROCESS_STATUS_BUZZER;
        if (!connectedDeviceMap.containsKey(selectedSwitchMac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedSwitchMac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback, TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback));
            }
        } else if (connectedDeviceMap.containsKey(selectedSwitchMac)) {
            RequestHandlerSendMessage(RSP_BUZZER_PROTOCOL_TIMEOUT,null);
            switchBLEGattWriteRequestCommand(selectedSwitchMac,bleProtocol.buzzerSwitchProtocol);
            DebugLogOutput(TAG, "buzzerSwitchProtocol : " + byteArrayToHex(bleProtocol.buzzerSwitchProtocol));
        }
    }

    private void startSwitchScanLight() {
        changeUI(UI_STATUS_LOADING,"스마트 스위치의 검색 범위 내 스마트 조명을 찾고 있습니다...");
        status_process = PROCESS_STATUS_SCAN_LIGHT;
        lightScanList.clear();
        if (!connectedDeviceMap.containsKey(selectedSwitchMac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedSwitchMac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback, TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback));
            }
        } else if (connectedDeviceMap.containsKey(selectedSwitchMac)) {
            RequestHandlerSendMessage(SCAN_RESULT_PROTOCOL_FAIL,null);
            DebugLogOutput(TAG, String.format("Before scanRequestSwitchProtocol : %s", byteArrayToHex(bleProtocol.scanRequestSwitchProtocol)));
            bleProtocol.scanRequestSwitchProtocol[3] = (byte) (SCAN_COUNT & 0xff);
            DebugLogOutput(TAG, String.format("After scanRequestSwitchProtocol : %s", byteArrayToHex(bleProtocol.scanRequestSwitchProtocol)));
            switchBLEGattWriteRequestCommand(selectedSwitchMac,bleProtocol.scanRequestSwitchProtocol);
            DebugLogOutput(TAG, "scanRequestSwitchProtocol : " + byteArrayToHex(bleProtocol.scanRequestSwitchProtocol));
        }
    }

    private void startBlinkLight() {
        changeUI(UI_STATUS_LOADING,"선택된 스마트 조명에 깜빡임을 요청중입니다...");
        status_process = PROCESS_STATUS_BLINK;
        if (!connectedDeviceMap.containsKey(selectedLightMac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedLightMac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(selectedLightMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback, TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(selectedLightMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback));
            }
        } else if (connectedDeviceMap.containsKey(selectedLightMac)) {
            RequestHandlerSendMessage(RSP_BLINK_PROTOCOL_TIMEOUT, null);
            lightBLEGattWriteRequestCommand(selectedLightMac,bleProtocol.blinkLightProtocol);
            DebugLogOutput(TAG, "blinkLightProtocol : " + byteArrayToHex(bleProtocol.blinkLightProtocol));
        }
    }

    private void initGroupDataForSwitch() {
        changeUI(UI_STATUS_LOADING,"스마트 스위치 그룹 초기화 요청 중입니다.");
        status_process = PROCESS_STATUS_INIT_SWITCH;
        if (!connectedDeviceMap.containsKey(selectedSwitchMac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedSwitchMac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback, TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback));
            }
        } else if (connectedDeviceMap.containsKey(selectedSwitchMac)) {
            RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT, null);
            switchBLEGattWriteRequestCommand(selectedSwitchMac,bleProtocol.initGroupDateSwitchProtocol);
            DebugLogOutput(TAG, "initGroupDateSwitchProtocol : " + byteArrayToHex(bleProtocol.initGroupDateSwitchProtocol));
        }
    }

    private void initGroupDataForLight(final int index) {
        changeUI(UI_STATUS_LOADING,"스마트 조명 그룹 초기화 요청 중입니다.");
        status_process = PROCESS_STATUS_INIT_LIGHT;
        final String mac = lightListSelected.get(index);
        if (!connectedDeviceMap.containsKey(mac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(mac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(mac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback, TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(mac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback));
            }
        } else if (connectedDeviceMap.containsKey(mac)) {
            RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_LIGHT_TIMEOUT, null);
             lightBLEGattWriteRequestCommand(mac,bleProtocol.initGroupDateLightProtocol);
            DebugLogOutput(TAG, "initGroupDateLightProtocol : " + byteArrayToHex(bleProtocol.initGroupDateLightProtocol));
        }
    }

    private void startGroupForSwitch() {
        changeUI(UI_STATUS_LOADING,"스마트 스위치 그룹 요청 중입니다.");
        status_process = PROCESS_STATUS_GROUPING_SWITCH;
        if (!connectedDeviceMap.containsKey(selectedSwitchMac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedSwitchMac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback, TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback));
            }
        } else if (connectedDeviceMap.containsKey(selectedSwitchMac)) {
            RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT, null);
            switchBLEGattWriteRequestCommand(selectedSwitchMac,getGroupProtocolForSwitch());
            DebugLogOutput(TAG, "groupSwitchProtocol : " + byteArrayToHex(getGroupProtocolForSwitch()));
        }
    }

    private void startGroupForLight(final int index) {
        status_process = PROCESS_STATUS_GROUPING_LIGHT;
        final String mac = lightListSelected.get(index);
        changeUI(UI_STATUS_LOADING,"스마트 조명 그룹 요청 중입니다.("+index+"/"+lightListSelected.size()+")");
        if (!connectedDeviceMap.containsKey(mac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(mac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(mac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback, TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(mac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback));
            }
        } else if (connectedDeviceMap.containsKey(mac)) {
            RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT, null);
            lightBLEGattWriteRequestCommand(mac,getGroupProtocolForLight());
            DebugLogOutput(TAG, "groupLightProtocol : " + byteArrayToHex(getGroupProtocolForLight()));
        }
    }

    private byte[] getGroupProtocolForSwitch() {
        int switchSize = (lightListSelected.size() * 6) + 4;
        byte[] switchProtocol = new byte[switchSize];
        switchProtocol[0] = (byte) (0xF2 & 0xff);
        switchProtocol[1] = (byte) (0x62 & 0xff);
        switchProtocol[2] = (byte) (((lightListSelected.size() * 6) + 1) & 0xff);
        switchProtocol[3] = (byte) (lightListSelected.size() & 0xff);
        for (int i = 0; i < lightListSelected.size(); i++) {
            String lightMac = lightListSelected.get(i);
            String[] lightMacParts = lightMac.split(":");
            byte[] toMacPartsAddress = new byte[6];
            for (int b = 0; b < 6; b++) {
                Integer hex = Integer.parseInt(lightMacParts[b], 16);
                toMacPartsAddress[b] = hex.byteValue();
            }
            switchProtocol = appendByteArray(switchProtocol, toMacPartsAddress, 4 + (i * 6));
        }
        return switchProtocol;
    }

    private byte[] getGroupProtocolForLight() {
        int switchCount = 1;
        int lightSize = (switchCount * 6) + 4;
        byte[] lightProtocol = new byte[lightSize];

        lightProtocol[0] = (byte) (0xF2 & 0xff);
        lightProtocol[1] = (byte) (0x60 & 0xff);
        lightProtocol[2] = (byte) (((switchCount * 6) + 1) & 0xff); //Length(Header, Command, Length 는 제외)
        lightProtocol[3] = (byte) (0x01 & 0xff); //그루핑 스마트 조명 스위치 개수.

        for (int i = 0; i < switchCount; i++) {
            String lightMac = selectedSwitchMac;
            String[] lightMacParts = lightMac.split(":");
            byte[] toMacPartsAddress = new byte[6];
            for (int b = 0; b < 6; b++) {
                Integer hex = Integer.parseInt(lightMacParts[b], 16);
                toMacPartsAddress[b] = hex.byteValue();
            }
            lightProtocol = appendByteArray(lightProtocol, toMacPartsAddress, 4 + (i * 6));
        }
        return lightProtocol;
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    public void onClick(View v) {
        /*
        final View fv = v;
        fv.setEnabled(false);
        Timer buttonTimer = new Timer();
        buttonTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            fv.setEnabled(true);
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {

                        }
                    }
                });
            }
        }, 500);
        */

        switch (v.getId()) {
            case R.id.btnBack:
                onBackPressed();
                break;
            case R.id.btnGroup:
                if(lightListSelected.size() == 0) {
                    showNotiMsg(SwitchMapperRegisterActivity.this,"스마트 조명을 하나 이상 선택해주세요.");
                } else {
                    startGroupForSwitch();
                    //initGroupDataForSwitch();
                }
                break;
            case R.id.btnCompleted:
                actvityExit();
                break;
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case RESULT_ENABLE_BT_MAIN:
                checkEnableBluetooth2();
                break;
            case RESULT_RETRY_BUT_NO_DEVICE:
                finish();
                break;
            case RESULT_RETRY_SCAN_DEVICE:
                if (resultCode == Activity.RESULT_OK) {
                    isRetry= true;
                    startScanningDevice();
                } else {
                    finish();
                }
                break;
            case RESULT_SWITCH_MAPPER_BUZZER_SUCCESS:
                if (resultCode == Activity.RESULT_OK) {
                    startSwitchScanLight();
                } else {
                    selectedSwitchPosition = -1;
                    changeUI(UI_STATUS_SHOW_SWITCH_LIST);
                }
                break;
            case RESULT_LIGHT_BLINK_SUCCESS:
                if (resultCode == Activity.RESULT_OK) {
                    if(!lightListSelected.contains(selectedLightMac)) {
                        lightListSelected.add(selectedLightMac);
                        lightArrayAdapter.notifyDataSetChanged();
                    }
                } else {
                    selectedLightPosition = -1;
                    changeUI(UI_STATUS_SHOW_LIGHT_LIST);
                }
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        
        DebugLogOutput(TAG, "bleDisconnect: Call");
        for(String key : connectedDeviceMap.keySet()){
            DebugLogOutput(TAG, "onDestroy] connectedDeviceMap bluetoothGatt disconnect / MAC :"+key);
            connectedDeviceMap.get(key).disconnect();
            connectedDeviceMap.get(key).close();
        }
        connectedDeviceMap.clear();

        if(swithList != null) {
            swithList.clear();
        }

        if(lightList != null) {
            lightList.clear();
        }

        if(lightListSelected != null) {
            lightListSelected.clear();
        }

        if(lightScanList != null) {
            lightScanList.clear();
        }
    }

    private void activityExit() {
        Intent intent = new Intent(getApplicationContext(), MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
                | Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }

    private void setUI() {

        connectedDeviceMap = new HashMap<String, BluetoothGatt>();

        // Scan Device
        listViewSwitch = findViewById(R.id.listViewSwitch);
        switchArrayAdapter = new DeviceArrayAdapter(swithList,SCAN_TYPE_SWITCH);
        listViewSwitch.setAdapter(switchArrayAdapter);
        listViewSwitch.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);

        // Scan Wifi
        listViewLight = findViewById(R.id.listViewLight);
        lightArrayAdapter = new DeviceArrayAdapter(lightList,SCAN_TYPE_LIGHT);
        listViewLight.setAdapter(lightArrayAdapter);
        listViewLight.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);

        // ui
        layoutSwitchList = findViewById(R.id.layoutSwitchList);
        layoutLightList = findViewById(R.id.layoutLightList);
        layoutCompleted = findViewById(R.id.layoutCompleted);
        txtSwitch = findViewById(R.id.txtSwitch);
        txtLight = findViewById(R.id.txtLight);
        txtCompleted = findViewById(R.id.txtCompleted);
    }

    private void changeUI(final int status) {
        changeUI(status, null, -1);
    }

    private void changeUI(final int status,final String msg) {
        changeUI(status, msg, -1);
    }

    private void changeUI(final int status,final String msg, final long hideTime) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                switch (status) {
                    case UI_STATUS_LOADING:
                        if(IsRunningProgress()){
                            getProgressDialog().showTitle(msg);
                        } else {
                            if(hideTime > 0) {
                                displayProgress(SwitchMapperRegisterActivity.this, msg, null, hideTime);
                            } else {
                                displayProgress(SwitchMapperRegisterActivity.this, msg, null, 10000);
                            }
                        }
                        break;
                    case UI_STATUS_SHOW_SWITCH_LIST:
                        layoutSwitchList.setVisibility(View.VISIBLE);
                        layoutLightList.setVisibility(View.GONE);
                        layoutCompleted.setVisibility(View.GONE);
                        if(msg!=null) {
                            txtSwitch.setText(msg);
                        }
                        if(IsRunningProgress()) {
                            DismissConnectDialog();
                        }
                        break;
                    case UI_STATUS_SHOW_LIGHT_LIST:
                        layoutSwitchList.setVisibility(View.GONE);
                        layoutLightList.setVisibility(View.VISIBLE);
                        layoutCompleted.setVisibility(View.GONE);
                        if(msg!=null) {
                            txtLight.setText(msg);
                        }
                        if(IsRunningProgress()) {
                            DismissConnectDialog();
                        }
                        break;
                    case UI_STATUS_SHOW_COMPLETED:
                        findViewById(R.id.btnBack).setVisibility(View.INVISIBLE);
                        layoutSwitchList.setVisibility(View.GONE);
                        layoutLightList.setVisibility(View.GONE);
                        layoutCompleted.setVisibility(View.VISIBLE);
                        if(msg!=null) {
                            txtCompleted.setText(msg);
                        }
                        if(IsRunningProgress()) {
                            DismissConnectDialog();
                        }
                        break;
                    case UI_STATUS_SHOW_FAIL:
                        showNotiMsg(SwitchMapperRegisterActivity.this,msg);
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                actvityExit();
                            }
                        },3000);
                        if(IsRunningProgress()) {
                            DismissConnectDialog();
                        }
                        break;
                }
            }
        });
    }

    private void actvityExit() {
        Intent intent = new Intent(getApplicationContext(), ProfileActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
                | Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }

    private void bleDisconnect(String mac) {
        if(connectedDeviceMap.containsKey(mac)) {
            if(connectedDeviceMap.get(mac) !=null) {
                connectedDeviceMap.get(mac).disconnect();
                connectedDeviceMap.get(mac).close();
            }
            connectedDeviceMap.remove(mac);
        }
    }

    /**
     * Ble 검색
     */
    private void startScanningDevice() {
        changeUI(UI_STATUS_LOADING,"등록할 스마트 조명을 찾고 있습니다.");
        scanLeDevice(true);
    }

    private void scanLeDevice(final boolean enable) {
        handler = new Handler();
        if (enable) {
            swithList.clear();
            lightList.clear();
            lightListSelected.clear();
            // Stops scanning after a pre-defined scan period.
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    bluetoothAdapter.stopLeScan(leScanCallback);

                    if(switchArrayAdapter == null || lightArrayAdapter == null || (switchArrayAdapter.getCount() == 0) || (lightArrayAdapter.getCount() == 0)) {
                        changeUI(UI_STATUS_SHOW_SWITCH_LIST,"검색된 장치가 없습니다.");
                        retryScanDevice();
                    } else {
                        switchArrayAdapter.notifyDataSetChanged();
                        lightArrayAdapter.notifyDataSetChanged();
                        changeUI(UI_STATUS_SHOW_SWITCH_LIST,"등록하실 스마트 스위치를 \n" +
                                "선택해 주세요.\n" +
                                "선택하신 스마트 스위치의 \n" +
                                "진동(소리)이 울립니다.\n");
                    }
                }
            }, SCAN_PERIOD);
            bluetoothAdapter.startLeScan(leScanCallback);
        } else {
            bluetoothAdapter.stopLeScan(leScanCallback);
        }
    }

    private final BluetoothAdapter.LeScanCallback leScanCallback =
            new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, int rssi,
                                     final byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (device.getName() != null && device.getName().toUpperCase().contains("ICON_LS")) {
                                DebugLogOutput(TAG, "Scan Switch Register Record :" + byteArrayToHex(scanRecord));
                                AmazonBLEinfo thisDevice = new AmazonBLEinfo(device.getName(),
                                        device.getAddress(), device);
                                if (!swithList.contains(thisDevice)) {
                                    swithList.add(thisDevice);
                                }
                            } else if (device.getName() != null && device.getName().toUpperCase().contains("ICON_LM")) {
                                DebugLogOutput(TAG, "Scan Light Register Record :" + byteArrayToHex(scanRecord));
                                AmazonBLEinfo thisDevice = new AmazonBLEinfo(device.getName(),
                                        device.getAddress(), device);
                                if (!lightList.contains(thisDevice)) {
                                    lightList.add(thisDevice);
                                }
                            }
                        }
                    });
                }
            };

    private final BluetoothGattCallback swithGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);

            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
                DebugLogOutput(TAG, String.format(Locale.getDefault(),
                        "BluetoothGatt.STATE_CONNECTED State from %s (%s) - status %d - state %d",
                        gatt.getDevice().getName(),
                        gatt.getDevice().getAddress(),
                        status,
                        newState
                ));

                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_CONNECTED, null);

                gatt.discoverServices();

            } else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) {
                DebugLogOutput(TAG, String.format(Locale.getDefault(),
                        "bluetoothGatt.STATE_DISCONNECTED State from %s (%s) - status %d - state %d",
                        gatt.getDevice().getName(),
                        gatt.getDevice().getAddress(),
                        status,
                        newState
                ));

                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_DISCONNECTED, null);

            } else if (status != BluetoothGatt.GATT_SUCCESS) {
                DebugLogOutput(TAG, String.format(Locale.getDefault(),
                        "bluetoothGatt.GATT_FAIL from %s (%s) - status %d - state %d",
                        gatt.getDevice().getName(),
                        gatt.getDevice().getAddress(),
                        status,
                        newState
                ));
                bleDisconnect(gatt.getDevice().getAddress());
                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_NOT_CONNECTED, null);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);

            DebugLogOutput(TAG, String.format(Locale.getDefault(),
                    "onServicesDiscovered State from %s (%s) - status %d",
                    gatt.getDevice().getName(),
                    gatt.getDevice().getAddress(),
                    status
            ));

            BluetoothGattService deviceGattService = gatt.getService(hdcUUID.SWITCH_GATT_SERVICE_UUID);
            if (deviceGattService == null) {
                DebugLogOutput(TAG, "Switch # device No Gatt Service !!");
                return;
            }

            bluetoothGattCharacteristic = deviceGattService.getCharacteristic(hdcUUID.SWITCH_NOTIFICATION_CHARACTERISTIC_UUID);
            if (bluetoothGattCharacteristic == null) {
                DebugLogOutput(TAG, "Switch # device No Gatt Notification Service !!");
                return;
            }

            DebugLogOutput(TAG, "Switch/Set on Notification");
            gatt.setCharacteristicNotification(bluetoothGattCharacteristic, true);

            if (hdcUUID.SWITCH_NOTIFICATION_CHARACTERISTIC_UUID.equals(bluetoothGattCharacteristic.getUuid())) {
                DebugLogOutput(TAG, "Switch/Write Descriptor");
                BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptor(hdcUUID.SWITCH_GATT_DESCRIPTORS_UUID);
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                gatt.writeDescriptor(descriptor);
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            byte[] data = characteristic.getValue();

            DebugLogOutput(TAG, String.format(Locale.getDefault(),
                    "onCharacteristicWrite State from %s (%s) - status %d - data %s",
                    gatt.getDevice().getName(),
                    gatt.getDevice().getAddress(),
                    status,
                    byteArrayToHex(data)
            ));
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            byte[] data = characteristic.getValue();

            // non use notification
            DebugLogOutput(TAG, String.format(Locale.getDefault(),
                    "onCharacteristicChanged State from %s (%s) - data %s",
                    gatt.getDevice().getName(),
                    gatt.getDevice().getAddress(),
                    byteArrayToHex(data)
            ));

            if (data[0] == (byte) (bleProtocol.BLE_HEADER) && data[1] == (byte) (bleProtocol.BLE_COMMAND_STOA_MAC_ADDR_RSP_TO_APP)) {
                DebugLogOutput(TAG, "SWITCH # get Notification");

                try {
                    String mac = "";
                    mac = String.format("%02x", data[3] & 0xff).toUpperCase() + ":" +
                            String.format("%02x", data[4] & 0xff).toUpperCase() + ":" +
                            String.format("%02x", data[5] & 0xff).toUpperCase() + ":" +
                            String.format("%02x", data[6] & 0xff).toUpperCase() + ":" +
                            String.format("%02x", data[7] & 0xff).toUpperCase() + ":" +
                            String.format("%02x", data[8] & 0xff).toUpperCase();

                    DebugLogOutput(TAG, "onCharacteristicChanged: MAC : " + mac);

                    RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_GET_NOTIFICATION, mac);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else if (data[0] == (byte) (bleProtocol.BLE_HEADER) && data[1] == (byte) (bleProtocol.BLE_COMMAND_STOA_BUZZER_PLAY_RSP_TO_APP)) {
                if(data[3] == (byte)0x01) {
                    DebugLogOutput(TAG, "get RSP_BUZZER_PROTOCOL_SUCCESS");
                    RequestHandlerSendMessage(bleProtocolState.RSP_BUZZER_PROTOCOL_SUCCESS, null);
                } else {
                    DebugLogOutput(TAG, "get RSP_BUZZER_PROTOCOL_FAIL");
                    RequestHandlerSendMessage(bleProtocolState.RSP_BUZZER_PROTOCOL_FAIL, null);
                }
            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_STOA_SWITCH_SCANRSP_TO_APP) {
                int num = data[3];
                int len = num * 8;
                for (int i = 4; i < len; i += 8) {
                    String scanResultInfo = String.format("ICON_LM_%02X%02X%02X", data[i + 3], data[i + 4], data[i + 5]);
                    DebugLogOutput(TAG, "scanResultInfo[" + scanResultInfo + "]");
                    lightScanList.add(String.format("%02X:%02X:%02X:%02X:%02X:%02X", data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5]));
                }
                RequestHandlerSendMessage(bleProtocolState.SCAN_RESULT_PROTOCOL_SUCCESS,null);
            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_STOA_SWITCH_GROUPING_RSP_TO_APP) {
                DebugLogOutput(TAG, "Switch/Write Protocol(GROUP) : " + byteArrayToHex(characteristic.getValue()));
                if(data[4] == (byte)0x01) {
                    DebugLogOutput(TAG, "get RSP_GROUP_SWITCH_SUCCESS");
                    RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_SWITCH_SUCCESS, null);
                } else {
                    DebugLogOutput(TAG, "get RSP_GROUP_SWITCH_FAIL");
                    RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_SWITCH_FAIL, null);
                }
            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_STOA_INIT_SWITCH_GROUPING_RSP_TO_APP) {
                if(data[3] == (byte)0x01) {
                    DebugLogOutput(TAG, "get RSP_INIT_GROUP_SWITCH_SUCCES");
                    RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_SWITCH_SUCCES, null);
                } else if(data[3] == (byte)0x03) {
                    DebugLogOutput(TAG, "get RSP_INIT_GROUP_SWITCH_FAIL_OVER_LIGHT");
                    RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_SWITCH_FAIL_OVER_LIGHT, null);
                } else {
                    DebugLogOutput(TAG, "get RSP_INIT_GROUP_SWITCH_FAIL");
                    RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_SWITCH_FAIL, null);
                }
            }
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            byte[] data = descriptor.getValue();

            DebugLogOutput(TAG, String.format(Locale.getDefault(),
                    "onDescriptorWrite State from %s (%s) - status %d - data %s",
                    gatt.getDevice().getName(),
                    gatt.getDevice().getAddress(),
                    status,
                    byteArrayToHex(data)
            ));
            if(status_process == PROCESS_STATUS_BUZZER) {
                DebugLogOutput(TAG, "스마트 스위치 소리 요청\nonDescriptorWrite: status_process = " + status_process);
                RequestHandlerSendMessage(RSP_BUZZER_PROTOCOL_TIMEOUT, null);
                switchBLEGattWriteRequestCommand(gatt.getDevice().getAddress(), bleProtocol.buzzerSwitchProtocol);
            } else if(status_process == PROCESS_STATUS_SCAN_LIGHT) {
                DebugLogOutput(TAG, String.format("Before scanRequestSwitchProtocol : %s", byteArrayToHex(bleProtocol.scanRequestSwitchProtocol)));
                bleProtocol.scanRequestSwitchProtocol[3] = (byte) (SCAN_COUNT & 0xff);
                DebugLogOutput(TAG, String.format("After scanRequestSwitchProtocol : %s", byteArrayToHex(bleProtocol.scanRequestSwitchProtocol)));
                switchBLEGattWriteRequestCommand(gatt.getDevice().getAddress(),bleProtocol.scanRequestSwitchProtocol);
                RequestHandlerSendMessage(SCAN_RESULT_PROTOCOL_FAIL,null);
            } else if(status_process == PROCESS_STATUS_GROUPING_SWITCH) {
                DebugLogOutput(TAG, "스마트 스위치 그룹 요청\nonDescriptorWrite: status_process = " + status_process);
                RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT, null);
                switchBLEGattWriteRequestCommand(selectedSwitchMac,getGroupProtocolForSwitch());
            } else if(status_process == PROCESS_STATUS_INIT_SWITCH) {
                DebugLogOutput(TAG, "스마트 스위치 그룹 정보 초기화\nonDescriptorWrite: status_process = " + status_process);
                RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT, null);
                switchBLEGattWriteRequestCommand(selectedSwitchMac,bleProtocol.initGroupDateSwitchProtocol);
            } else {
                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_GET_NOTIFICATION_FAIL, null);
                switchBLEGattWriteRequestCommand(gatt.getDevice().getAddress(), bleProtocol.getMacSwitchProtocol);
            }

        }
    };

    private final BluetoothGattCallback lightGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);

            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
                DebugLogOutput(TAG, String.format(Locale.getDefault(),
                        "BluetoothGatt.STATE_CONNECTED State from %s (%s) - status %d - state %d",
                        gatt.getDevice().getName(),
                        gatt.getDevice().getAddress(),
                        status,
                        newState
                ));

                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_CONNECTED, null);

                gatt.discoverServices();

            } else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) {
                DebugLogOutput(TAG, String.format(Locale.getDefault(),
                        "bluetoothGatt.STATE_DISCONNECTED State from %s (%s) - status %d - state %d",
                        gatt.getDevice().getName(),
                        gatt.getDevice().getAddress(),
                        status,
                        newState
                ));

                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_DISCONNECTED, null);

            } else if (status != BluetoothGatt.GATT_SUCCESS) {
                DebugLogOutput(TAG, String.format(Locale.getDefault(),
                        "bluetoothGatt.GATT_FAIL from %s (%s) - status %d - state %d",
                        gatt.getDevice().getName(),
                        gatt.getDevice().getAddress(),
                        status,
                        newState
                ));
                bleDisconnect(gatt.getDevice().getAddress());
                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_NOT_CONNECTED, null);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);

            DebugLogOutput(TAG, String.format(Locale.getDefault(),
                    "onServicesDiscovered State from %s (%s) - status %d",
                    gatt.getDevice().getName(),
                    gatt.getDevice().getAddress(),
                    status
            ));

            BluetoothGattService deviceGattService = gatt.getService(hdcUUID.LIGHT_GATT_SERVICE_UUID);
            if (deviceGattService == null) {
                DebugLogOutput(TAG, "Light # device No Gatt Service !!");
                return;
            }

            bluetoothGattCharacteristic = deviceGattService.getCharacteristic(hdcUUID.LIGHT_GATT_NOTIFICATION_CHARACTERISTIC_UUID);
            if (bluetoothGattCharacteristic == null) {
                DebugLogOutput(TAG, "Light # device No Gatt Notification Service !!");
                return;
            }

            DebugLogOutput(TAG, "Light/Set on Notification");
            gatt.setCharacteristicNotification(bluetoothGattCharacteristic, true);

            if (hdcUUID.LIGHT_GATT_NOTIFICATION_CHARACTERISTIC_UUID.equals(bluetoothGattCharacteristic.getUuid())) {
                DebugLogOutput(TAG, "Light/Write Descriptor");
                BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptor(hdcUUID.LIGHT_GATT_DESCRIPTORS_UUID);
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                gatt.writeDescriptor(descriptor);
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            byte[] data = characteristic.getValue();

            DebugLogOutput(TAG, String.format(Locale.getDefault(),
                    "onCharacteristicWrite State from %s (%s) - status %d - data %s",
                    gatt.getDevice().getName(),
                    gatt.getDevice().getAddress(),
                    status,
                    byteArrayToHex(data)
            ));
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            byte[] data = characteristic.getValue();

            // non use notification
            DebugLogOutput(TAG, String.format(Locale.getDefault(),
                    "onCharacteristicChanged State from %s (%s) - data %s",
                    gatt.getDevice().getName(),
                    gatt.getDevice().getAddress(),
                    byteArrayToHex(data)
            ));

            if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_LTOA_BLINK_LIGHT_RSP_TO_APP && data[2] == (byte)0x01) {
                if (data[3] == (byte) 0x01) {
                    DebugLogOutput(TAG, "get RSP_BLINK_PROTOCOL_SUCCESS");
                    RequestHandlerSendMessage(bleProtocolState.RSP_BLINK_PROTOCOL_SUCCESS, null);
                } else if (data[3] == (byte) 0x02) {
                    DebugLogOutput(TAG, "get RSP_BLINK_PROTOCOL_FAIL");
                    RequestHandlerSendMessage(bleProtocolState.RSP_BLINK_PROTOCOL_FAIL, null);
                }
            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_LTOA_LIGHT_GROUPING_RSP_TO_APP && data[2] == (byte)0x03) {
                DebugLogOutput(TAG, "Light/Write Protocol(GROUP) : " + byteArrayToHex(characteristic.getValue()));
                /*
                Error Code
                0x01: Success, 1 번째 그룹스위치 설정 (최종 1 개)
                0x02: Success, 2 번째 그룹스위치 설정 (최종 2 개)
                0x03: Error, 3 번째 그룹스위치 설정 설정 (이미 2 개 설정되어 있음)
                0x0A: Sucess, 이미 등록된 그룹스위치 설정 (재설정됨)
                */
                if(data[4] == (byte)0x01 || data[4] == (byte)0x02 || data[4] == (byte)0x0A) {
                    DebugLogOutput(TAG, "get RSP_GROUP_LIGHT_SUCCESS");
                    RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_LIGHT_SUCCESS, null);
                } else {
                    DebugLogOutput(TAG, "get RSP_GROUP_LIGHT_FAIL");
                    RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_LIGHT_FAIL, null);
                }
            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_LTOA_INIT_LIGHT_GROUPING_RSP_TO_APP) {
                if(data[3] == (byte)0x01) {
                    DebugLogOutput(TAG, "get RSP_BUZZER_PROTOCOL_SUCCESS");
                    RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_LIGHT_SUCCES, null);
                } else {
                    DebugLogOutput(TAG, "get RSP_BUZZER_PROTOCOL_FAIL");
                    RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_LIGHT_FAIL, null);
                }
            }
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            byte[] data = descriptor.getValue();

            DebugLogOutput(TAG, String.format(Locale.getDefault(),
                    "onDescriptorWrite State from %s (%s) - status %d - data %s",
                    gatt.getDevice().getName(),
                    gatt.getDevice().getAddress(),
                    status,
                    byteArrayToHex(data)
            ));
            if(status_process == PROCESS_STATUS_BLINK) {
                DebugLogOutput(TAG, "조명 깜빡임 요청\nonDescriptorWrite: status_process = " + status_process);
                RequestHandlerSendMessage(RSP_BLINK_PROTOCOL_TIMEOUT, null);
                lightBLEGattWriteRequestCommand(gatt.getDevice().getAddress(), bleProtocol.blinkLightProtocol);
            } else if(status_process == PROCESS_STATUS_GROUPING_LIGHT) {
                DebugLogOutput(TAG, "조명 그룹 요청\nonDescriptorWrite: status_process = " + status_process);
                RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT, null);
                lightBLEGattWriteRequestCommand(gatt.getDevice().getAddress(),getGroupProtocolForLight());
                DebugLogOutput(TAG, "groupLightProtocol : " + byteArrayToHex(getGroupProtocolForLight()));
            } else if(status_process == PROCESS_STATUS_INIT_LIGHT) {
                DebugLogOutput(TAG, "조명 그룹 초기화 요청\nonDescriptorWrite: status_process = " + status_process);
                RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_LIGHT_TIMEOUT, null);
                switchBLEGattWriteRequestCommand(gatt.getDevice().getAddress(),bleProtocol.initGroupDateLightProtocol);
                DebugLogOutput(TAG, "initGroupDateLightProtocol : " + byteArrayToHex(bleProtocol.initGroupDateLightProtocol));
            }
        }
    };

    private void RequestHandlerSendMessage(int deviceState, String mac) {
        Message msg = new Message();
        msg.what = deviceState;
        if (deviceState == bleProtocolState.BLUETOOTH_GET_NOTIFICATION_FAIL) {
            switchRegisterHandler.sendMessageDelayed(msg, 5000);
        } else if (deviceState == bleProtocolState.BLUETOOTH_GET_NOTIFICATION) {
            msg.obj = mac;
            switchRegisterHandler.sendMessage(msg);
        } else if(deviceState == RSP_BUZZER_PROTOCOL_TIMEOUT) {
            switchRegisterHandler.sendMessageDelayed(msg, 5000);
        } else if (deviceState == SCAN_RESULT_PROTOCOL_FAIL) {
            switchRegisterHandler.sendMessageDelayed(msg, (SCAN_COUNT + 3) * 1000);
        } else if (deviceState == RSP_GROUP_SWITCH_TIMEOUT) {
            switchRegisterHandler.sendMessageDelayed(msg, 10000);
        } else if (deviceState == RSP_GROUP_LIGHT_TIMEOUT) {
            switchRegisterHandler.sendMessageDelayed(msg, 10000);
        } else if(deviceState == RSP_INIT_GROUP_SWITCH_TIMEOUT) {
            switchRegisterHandler.sendMessageDelayed(msg, 10000);
        } else if(deviceState == RSP_INIT_GROUP_LIGHT_TIMEOUT) {
            switchRegisterHandler.sendMessageDelayed(msg, 10000);
        } else if(deviceState == RSP_BLINK_PROTOCOL_TIMEOUT) {
            switchRegisterHandler.sendMessageDelayed(msg, 10000);
        } else {
            switchRegisterHandler.sendMessage(msg);
        }
    }

    private void switchBLEGattWriteRequestCommand(String address, byte[] protocol) {
        bluetoothGattCharacteristic = connectedDeviceMap.get(address).getService(hdcUUID.SWITCH_GATT_SERVICE_UUID)
                .getCharacteristic(hdcUUID.SWITCH_GATT_WRITE_CHARACTERISTIC_UUID);
        bluetoothGattCharacteristic.setValue(protocol);
        connectedDeviceMap.get(address).writeCharacteristic(bluetoothGattCharacteristic);
    }

    private void lightBLEGattWriteRequestCommand(String address, byte[] protocol) {
        bluetoothGattCharacteristic = connectedDeviceMap.get(address).getService(hdcUUID.LIGHT_GATT_SERVICE_UUID)
                .getCharacteristic(hdcUUID.LIGHT_GATT_WRITE_CHARACTERISTIC_UUID);
        bluetoothGattCharacteristic.setValue(protocol);
        connectedDeviceMap.get(address).writeCharacteristic(bluetoothGattCharacteristic);
    }

    private void retryScanDevice() {
        if(isRetry) {
            Intent intent = new Intent(SwitchMapperRegisterActivity.this, PopupActivity.class);
            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL_WITH_ICON);
            if(swithList.size() == 0 && lightList.size() == 0) {
                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_switch_light_device_title));
            } else if(swithList.size() == 0) {
                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_switch_device_title));
            } else {
                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_light_device_title));
            }
            intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_not_found_light_device_description_2));
            intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.ok));
            startActivityForResult(intent, RESULT_RETRY_BUT_NO_DEVICE);
        } else {
            Intent intent = new Intent(SwitchMapperRegisterActivity.this, PopupActivity.class);
            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO_WITH_ICON);
            if(swithList.size() == 0 && lightList.size() == 0) {
                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_switch_light_device_title));
            } else if(swithList.size() == 0) {
                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_switch_device_title));
            } else {
                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_light_device_title));
            }
            intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_not_found_light_device_description_1));
            intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.retry));
            intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.cancel));
            startActivityForResult(intent, RESULT_RETRY_SCAN_DEVICE);
        }
    }

    private void checkEnableBluetooth() {
        if(!isBlueToothEnable(this)){
            Intent intent = new Intent(this, PopupActivity.class);
            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_ENABLE_BT);
            startActivityForResult(intent, RESULT_ENABLE_BT_MAIN);
        } else {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    startScanningDevice();
                }
            }, FIRST_SCAN_PERIOD);
        }
    }

    private void checkEnableBluetooth2() {
        if(!isBlueToothEnable(this)){
            showNotiMsg(this,"블루투스 활성화를 실패하였습니다.",true);
        } else {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    startScanningDevice();
                }
            }, FIRST_SCAN_PERIOD);
        }
    }

    /**
     * Ble 리스트 아답터
     */
    public class DeviceArrayAdapter extends BaseAdapter {
        // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
        private List<AmazonBLEinfo> listViewItemList;
        private int deviceType = 0;

        // ListViewAdapter의 생성자
        public DeviceArrayAdapter(List<AmazonBLEinfo> listItem, int type) {
            listViewItemList = listItem;
            deviceType = type;
        }

        // Adapter에 사용되는 데이터의 개수를 리턴. : 필수 구현
        @Override
        public int getCount() {
            return listViewItemList==null?0:listViewItemList.size() ;
        }

        // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴. : 필수 구현
        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            final int pos = position;
            final Context context = parent.getContext();

            // "listview_item" Layout을 inflate하여 convertView 참조 획득.
            if (convertView == null) {
                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.listview_add_device_list, parent, false);
            }

            // 화면에 표시될 View(Layout이 inflate된)으로부터 위젯에 대한 참조 획득
            LinearLayout layoutDevice = convertView.findViewById(R.id.layoutDevice);
            TextView txtName = convertView.findViewById(R.id.txtName);
            ImageView imgIcon = convertView.findViewById(R.id.imgIcon);

            // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
            if(deviceType == SCAN_TYPE_SWITCH) {
                imgIcon.setImageResource(R.drawable.icon_add_device_switch_mapper);
            } else if(deviceType == SCAN_TYPE_LIGHT) {
                imgIcon.setImageResource(R.drawable.icon_add_device_light);
                layoutDevice.setSelected(false);

                for(String s : lightListSelected) {
                    if(listViewItemList.get(position).getMacAddr().equals(s)) {
                        layoutDevice.setSelected(true);
                    }
                }
            }
            txtName.setText(listViewItemList.get(position).getName());

            return convertView;
        }

        // 지정한 위치(position)에 있는 데이터와 관계된 아이템(row)의 ID를 리턴. : 필수 구현
        @Override
        public long getItemId(int position) {
            return position ;
        }

        // 지정한 위치(position)에 있는 데이터 리턴 : 필수 구현
        @Override
        public Object getItem(int position) {
            return listViewItemList.get(position) ;
        }

        public void removeAll() {
            listViewItemList.clear();
        }
    }
}