package kr.co.icontrols.iotplatform;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
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.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
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 androidx.appcompat.app.AppCompatActivity;

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.mobile.client.AWSMobileClient;
import com.google.android.material.bottomsheet.BottomSheetBehavior;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
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.AWS.AmazonFreeRTOSAgent;
import kr.co.icontrols.iotplatform.AWS.AmazonWifiInfo;
import kr.co.icontrols.iotplatform.Protocol.bleProtocol;
import kr.co.icontrols.iotplatform.Protocol.bleProtocolState;
import kr.co.icontrols.iotplatform.utils.CommonUtils;
import kr.co.icontrols.iotplatform.utils.HttpApi;
import kr.co.icontrols.iotplatform.utils.hdcUUID;
import okhttp3.Call;
import okhttp3.Response;
import software.amazon.freertos.amazonfreertossdk.AmazonFreeRTOSConstants;
import software.amazon.freertos.amazonfreertossdk.AmazonFreeRTOSDevice;
import software.amazon.freertos.amazonfreertossdk.AmazonFreeRTOSManager;
import software.amazon.freertos.amazonfreertossdk.BleConnectionStatusCallback;
import software.amazon.freertos.amazonfreertossdk.NetworkConfigCallback;
import software.amazon.freertos.amazonfreertossdk.networkconfig.ListNetworkReq;
import software.amazon.freertos.amazonfreertossdk.networkconfig.ListNetworkResp;
import software.amazon.freertos.amazonfreertossdk.networkconfig.SaveNetworkReq;
import software.amazon.freertos.amazonfreertossdk.networkconfig.SaveNetworkResp;

import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;
import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_BUNDLE;
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.ErrorLogOutput;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.IsRunningProgress;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.bssidToString;
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.restartApp;
import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_ID;
import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_ENABLE_BT_MAIN;
import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_AP_PASS;
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_MAPPING_DEVICE;
import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RETRY_SCAN_DEVICE;
import static kr.co.icontrols.iotplatform.utils.Constants.RSSI_LEVEL_1;
import static kr.co.icontrols.iotplatform.utils.Constants.RSSI_LEVEL_2;
import static kr.co.icontrols.iotplatform.utils.Constants.RSSI_LEVEL_3;
import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_UNAUTHORIZED;

public class LightRegisterDeviceActivity extends AppCompatActivity implements View.OnClickListener {

    private final String TAG = "LightRegisterDevice";

    private final long USER_REGISTER_REQUEST_TIMEOUT_TIME = 60000;
    private final long DEFAULT_WAIT_WIFI_STATUS_TIME = 20000;
    private final long DEFAULT_REG_DEVICE_LOADING_TIME = 10000;

    // device scan ui
    private LinearLayout layoutBleList;
    private TextView txtBleList;
    private ListView bleListView;
    private BleArrayAdapter bleArrayAdapter;
    private List<AmazonBLEinfo> bleList = new ArrayList<>();

    // wifi scan ui
    private LinearLayout layoutWifiList, layoutListWifi;
    private TextView txtWifiList,txtWifiDeviceName,txtCompletedDeviceName;
    //private ListView wifiListView;
    //private WifiArrayAdapter wifiArrayAdapter;
    private List<AmazonWifiInfo> wifiInfoList = new ArrayList<AmazonWifiInfo>();

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

    // FreeRTOS
    private String wifipw;
    private String wifibssid;
    private AmazonFreeRTOSManager amazonFreeRTOSManager;
    private AmazonFreeRTOSDevice amazonFreeRTOSDevice = null;
    private HashMap<String, AmazonWifiInfo> mBssid2WifiInfoMap = new HashMap<>();

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

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

    //status
    private final int UI_STATUS_LOADING = 0;
    private final int UI_STATUS_SHOW_BLE_LIST = 1;
    private final int UI_STATUS_SHOW_WIFI_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_BLINK = 1;
    private final int PROCESS_STATUS_USER_REGISTER = 2;
    private final int PROCESS_STATUS_INIT_LIGHT = 3;
    private final int PROCESS_STATUS_SEND_ACK_STATUS_SUCCESS = 4;
    private final int PROCESS_STATUS_SEND_ACK_STATUS_FAIL = 5;

    // flag
    private int status_process = PROCESS_STATUS_NOTHING;

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

    //Wait userReg
    private boolean isUserRegisteStatus = false;

    // handler
    private Handler handler;
    private Handler mHandler;
    private Handler requestHandler;
    private int SCAN_PERIOD = 5000;
    private int FIRST_SCAN_PERIOD = 500;

    private int selectedLightPosition = -1;
    private String selectedBleMac = null;
    private String macNumber = "";
    private String modelIdNumber = "";
    private String deviceIdNumber = "";
    private String userRegisterDetailState = "";
    private String awsProvisionDetailState = "";
    private String userMappingDetailState = "";

    private AmazonWifiInfo amazonWifiInfo = null;

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

        // init Activity
        {
            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);

            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

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

            setUi();
        }


        // Light Device Selected & blink led
        bleListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                final AmazonBLEinfo item = (AmazonBLEinfo) parent.getItemAtPosition(position);
                selectedBleMac = item.getMacAddr();
                selectedLightPosition = position;
                startBlinkLight();
                //selectedLight(selectedLightPosition);
            }
        });

        // send, selected wifi, smartphone -> esp32 light device
        /*
        wifiListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //String tempStr = awsWifiArrayAdapter.getItem(position).toString();
                //wifibssid = tempStr.substring(tempStr.length() - 17);
                wifibssid = bssidToString(((AmazonWifiInfo)wifiArrayAdapter.getItem(position)).getBssid());
                amazonWifiInfo = (AmazonWifiInfo)wifiArrayAdapter.getItem(position);
                inputPw(!amazonWifiInfo.getNetworkTypeName().equals("Open"));
            }
        });
        */

        requestHandler = new Handler(Looper.myLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

                switch (msg.what) {

                    // aws ble connect
                    case bleProtocolState.PROVISIONING_BLE_CONNECTED:
                        ListNetwork();
                        changeUI(UI_STATUS_LOADING,"WIFI를 검색중입니다.");
                        DebugLogOutput(TAG, "AmazonFreeRTOS BLE connection success -> request wifi list -> execute ListNetWork() Method & Progressbar Start");
                        break;

                    case bleProtocolState.PROVISIONING_BLE_DISCONNECTED:
                        changeUI(UI_STATUS_SHOW_FAIL,"Bluetooth 연결이 해제되었습니다.");
                        DebugLogOutput(TAG, "AmazonFreeRTOS BLE Disconnect -> Call gatt.Disconnect Method or Ble Status Problem");
                        break;

                    // wi-fi list renew
                    case bleProtocolState.PROVISIONING_LIST_NETWORK_SUCCESS:
                        DebugLogOutput(TAG, "Start ListNetWork() Method");
                        changeUI(UI_STATUS_SHOW_WIFI_LIST,"WIFI 목록 검색 완료\nWIFI를 선택해주세요.");


                        //bottom sheet part
                        final BottomSheetBehavior bottom_Sheet = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet));
                        bottom_Sheet.setHideable(false);
                        bottom_Sheet.setState(BottomSheetBehavior.STATE_EXPANDED);
                        bottom_Sheet.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
                            @Override
                            public void onStateChanged(View bottomSheet, int newState) {
                                if (newState == BottomSheetBehavior.STATE_EXPANDED) {

                                }
                                else if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                                    bottom_Sheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
                                }
                                else if (newState == BottomSheetBehavior.STATE_COLLAPSED) {

                                }
                            }

                            @Override
                            public void onSlide(View bottomSheet, float slideOffset) {
                            }
                        });


                        break;

                    // wi-fi list renew fail
                    case bleProtocolState.PROVISIONING_LIST_NETWORK_FAIL:
                        DebugLogOutput(TAG, "Fail ListNetWork() Method");
                        changeUI(UI_STATUS_SHOW_FAIL,"WIFI 목록 검색 실패");
                        bleAwsDisconnect();
                        break;

                    // wi-fi provisioning end
                    case bleProtocolState.PROVISIONING_SAVE_NETWORK_SUCCESS:
                        isUserRegisteStatus = true;
                        DebugLogOutput(TAG, "Start SaveNetWork() Method");
                        changeUI(UI_STATUS_LOADING,"Wi-Fi Provisioning을 저장 중입니다.",USER_REGISTER_REQUEST_TIMEOUT_TIME);
                        RequestHandlerSendMessage(bleProtocolState.RSP_GET_WIFI_STATUS_TIMEOUT);
                        break;

                    case bleProtocolState.PROVISIONING_SAVE_NETWORK_FAIL:
                        DebugLogOutput(TAG, "Fail SaveNetWork() Method");
                        changeUI(UI_STATUS_SHOW_FAIL,"조명 모듈에게 Wi-Fi 정보 저정에 대한 응답을 받지 못하였습니다.");
                        break;

                    case bleProtocolState.BLUETOOTH_NOT_CONNECTED:
                        DebugLogOutput(TAG, "BLUETOOTH_NOT_CONNECTED");
                        //changeUI(UI_STATUS_SHOW_FAIL,"조명모듈과 Bluetooth 연결에 실패하였습니다.");
                        bleGattDisconnect();
                        retryScanDevice();
                        break;

                    case bleProtocolState.USER_REGISTER_REQUEST_TIMEOUT:
                        isUserRegisteStatus = false;
                        DebugLogOutput(TAG, "User Register Timeout");
                        changeUI(UI_STATUS_SHOW_FAIL,"User Register 요청 Timeout "+(int)(USER_REGISTER_REQUEST_TIMEOUT_TIME/1000)+"'sec");
                        break;

                    case bleProtocolState.USER_REGISTER_COMPLETE_SUCCESS:
                        isUserRegisteStatus = false;
                        requestHandler.removeMessages(bleProtocolState.USER_REGISTER_REQUEST_TIMEOUT);
                        bleGattWriteRequestCommand(bleProtocol.userRegisterCompleteSuccessProtocol);
                        DebugLogOutput(TAG, "User Register Success : " + byteArrayToHex(bleProtocol.userRegisterCompleteSuccessProtocol));
                        break;

                    case bleProtocolState.USER_REGISTER_COMPLETE_FAIL:
                        isUserRegisteStatus = false;
                        requestHandler.removeMessages(bleProtocolState.USER_REGISTER_REQUEST_TIMEOUT);
                        bleGattWriteRequestCommand(bleProtocol.userRegisterCompleteFailProtocol);
                        break;

                    case bleProtocolState.USER_REGISTER_END:
                        changeUI(UI_STATUS_LOADING,"User Register 확인 응답 성공, AWS IOT 매핑을 시도합니다.");
                        RequestHandlerSendMessage(bleProtocolState.REQ_REGISTER_DEVICE_ID);
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                bleAwsDisconnect();
                                bleGattDisconnect();
                            }
                        }, 100);
                        break;

                    case bleProtocolState.USER_REGISTER_FAIL:
                        changeUI(UI_STATUS_SHOW_FAIL,"사용자 등록을 하지 못하였습니다.");
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {

                                bleAwsDisconnect();
                                bleGattDisconnect();
                            }
                        }, 100);
                        break;

                    case bleProtocolState.HDC_ICON_LIGHT_REGISTER_SUCCESS:
                        bleAwsDisconnect();
                        bleGattDisconnect();
                        break;

                    case bleProtocolState.RSP_BLINK_PROTOCOL_SUCCESS: {
                        requestHandler.removeMessages(bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT);
                        Intent intent = new Intent(LightRegisterDeviceActivity.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:
                        requestHandler.removeMessages(bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT);
                        //TODO 조명 깜빡임 실패시 로직 구현해야함.
                        break;
                    case bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT:
                        //TODO 조명 깜빡임 Timeout 로직 구현해야함.
                        break;
                    case bleProtocolState.RSP_GET_WIFI_STATUS_SUCCESS:
                        changeUI(UI_STATUS_LOADING,"사용자 등록 완료를 기다리는 중입니다.",USER_REGISTER_REQUEST_TIMEOUT_TIME);
                        sendAckStatus(true);
                        bleAwsDisconnect();
                        requestHandler.removeMessages(bleProtocolState.RSP_GET_WIFI_STATUS_TIMEOUT);
                        RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_REQUEST_TIMEOUT);
                        break;
                    case bleProtocolState.RSP_GET_WIFI_STATUS_FAIL:
                        //TODO - 와이파이 AP 등록 실패시(오류)
                        isUserRegisteStatus = false;
                        sendAckStatus(false);
                        requestHandler.removeMessages(bleProtocolState.RSP_GET_WIFI_STATUS_TIMEOUT);
                        inputPw(!amazonWifiInfo.getNetworkTypeName().equals("Open"));
                        changeUI(UI_STATUS_SHOW_FAIL,"AP 암호 등록중 오류가 발생하였습니다.");
                        break;
                    case bleProtocolState.RSP_GET_WIFI_STATUS_TIMEOUT:
                        //TODO - 와이파이 AP 등록 실패시(타임아웃)
                        isUserRegisteStatus = false;
                        //sendAckStatus(false);
                        inputPw(!amazonWifiInfo.getNetworkTypeName().equals("Open"));
                        changeUI(UI_STATUS_SHOW_FAIL,"AP 암호가 일치하지 않습니다.");
                        break;
                    case bleProtocolState.REQ_REGISTER_DEVICE_ID:
                        startMappingLightDevice();
                        break;
                    case bleProtocolState.BLUETOOTH_CONNECTED:
                        //changeUI(UI_STATUS_LOADING,"스마트 조명과 블루투스를 연결하였습니다.");
                        break;
                    case bleProtocolState.ON_BOARDING_BLE_DISCONNECTED:
                        //TODO - 연결 해제시 동작 구현해야함.
                        break;
                    default:
                        throw new IllegalStateException("Unexpected value: " + msg.what);
                }
            }
        };
    }

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

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

    @Override
    protected void onPause() {
        scanLeDevice(false);
        CommonUtils.ForceDismissConnectDialog();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        if (amazonFreeRTOSDevice != null) {
            DebugLogOutput(TAG, " onDestroy] disconnectFromDevice");
            try {
                amazonFreeRTOSManager.disconnectFromDevice(amazonFreeRTOSDevice);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
            amazonFreeRTOSManager = null;
            amazonFreeRTOSDevice = null;
        }

        for(String key : connectedDeviceMap.keySet()){
            DebugLogOutput(TAG, " onDestroy] connectedDeviceMap bluetoothGatt disconnect / MAC :"+key);
            connectedDeviceMap.get(key).disconnect();
            connectedDeviceMap.get(key).close();
        }

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

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

    @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_SCAN_DEVICE:
                if (resultCode == Activity.RESULT_OK) {
                    isRetry= true;
                    startScanningLightDevice();
                } else {
                    finish();
                }
                break;
            case RESULT_RETRY_MAPPING_DEVICE:
                if (resultCode == Activity.RESULT_OK) {
                    isRetryMapping= true;
                    startMappingLightDevice();
                } else {
                    finish();
                }
                break;
            case RESULT_RETRY_BUT_NO_DEVICE:
                finish();
                break;
            case RESULT_LIGHT_BLINK_SUCCESS:
                if (resultCode == Activity.RESULT_OK) {
                    //initLight(selectedLightPosition);
                    selectedLight(selectedLightPosition);
                } else {
                    selectedLightPosition = -1;
                    changeUI(UI_STATUS_SHOW_BLE_LIST);
                }
                break;
            case RESULT_INPUT_AP_PASS:
                if (resultCode == RESULT_OK) {
                    Bundle b = data.getBundleExtra(POPUP_BUNDLE);
                    if(b != null) {
                        try {
                            String wifipw = b.getString(PopupActivity.POPUP_INPUT);
                            changeUI(UI_STATUS_LOADING,"Wi-Fi PassWord 저장 중입니다.",60000);
                            saveNetwork(wifibssid, wifipw);
                        } catch (Exception e) {
                            // Log failure
                            showNotiMsg(LightRegisterDeviceActivity.this,"암호 입력중 오류가 발생하였습니다.");
                        }
                    } else {
                        showNotiMsg(LightRegisterDeviceActivity.this,"암호 입력중 오류가 발생하였습니다.");
                    }
                }
                break;
        }
    }

    @Override
    public void onBackPressed() {
        if(layoutCompleted.getVisibility()==View.VISIBLE) {
            findViewById(R.id.btnCompleted).callOnClick();
        } else {
            super.onBackPressed();
        }
    }

    @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.btnCompleted:
                actvityExit();
                break;
            case R.id.replaceListViewWifi:
                selectedLight(selectedLightPosition);
                break;
        }
    }

    private void actvityExit() {
        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 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() {
                    startScanningLightDevice();
                }
            }, FIRST_SCAN_PERIOD);
        }
    }

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

    private void setUi() {

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

        // Scan Device
        bleListView = findViewById(R.id.listViewBle);
        bleArrayAdapter = new BleArrayAdapter(bleList);
        bleListView.setAdapter(bleArrayAdapter);
        bleListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);

        // Scan Wifi
        /*
        wifiListView = findViewById(R.id.listViewWifi);
        wifiArrayAdapter = new WifiArrayAdapter(wifiInfoList);
        wifiListView.setAdapter(wifiArrayAdapter);
        wifiListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
        */

        // AWS FreeRTOS
        amazonFreeRTOSManager = AmazonFreeRTOSAgent.getAmazonFreeRTOSManager(getApplicationContext());

        // ui
        layoutBleList = findViewById(R.id.layoutBleList);
        layoutWifiList = findViewById(R.id.layoutWifiList);
        layoutListWifi = findViewById(R.id.layoutListWifi);
        layoutCompleted = findViewById(R.id.layoutCompleted);
        txtBleList = findViewById(R.id.txtBleList);
        txtWifiList = findViewById(R.id.txtWifiList);
        txtWifiDeviceName = findViewById(R.id.txtWifiDeviceName);
        txtCompleted = findViewById(R.id.txtCompleted);
        txtCompletedDeviceName = findViewById(R.id.txtCompletedDeviceName);
    }

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

    private void changeUI(final int status,final String msg) {
        changeUI(status, msg, DEFAULT_REG_DEVICE_LOADING_TIME);
    }
    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(LightRegisterDeviceActivity.this, msg, null, hideTime);
                            } else {
                                displayProgress(LightRegisterDeviceActivity.this, msg, null);
                            }
                        }
                        break;
                    case UI_STATUS_SHOW_BLE_LIST:
                        layoutBleList.setVisibility(View.VISIBLE);
                        layoutWifiList.setVisibility(View.GONE);
                        layoutCompleted.setVisibility(View.GONE);
                        if(msg!=null) {
                            txtBleList.setText(msg);
                        }
                        if(IsRunningProgress()) {
                            DismissConnectDialog();
                        }
                        break;
                    case UI_STATUS_SHOW_WIFI_LIST:
                        layoutBleList.setVisibility(View.GONE);
                        layoutWifiList.setVisibility(View.VISIBLE);
                        layoutCompleted.setVisibility(View.GONE);
                        if(msg!=null) {
                            txtWifiList.setText(msg);
                        }
                        if(IsRunningProgress()) {
                            DismissConnectDialog();
                        }
                        break;
                    case UI_STATUS_SHOW_COMPLETED:
                        findViewById(R.id.btnBack).setVisibility(View.INVISIBLE);
                        layoutBleList.setVisibility(View.GONE);
                        layoutWifiList.setVisibility(View.GONE);
                        layoutCompleted.setVisibility(View.VISIBLE);
                        if(msg!=null) {
                            txtCompleted.setText(msg);
                        }
                        if(IsRunningProgress()) {
                            DismissConnectDialog();
                        }
                        break;
                    case UI_STATUS_SHOW_FAIL:
                        showNotiMsg(LightRegisterDeviceActivity.this,msg);
                        /*
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                actvityExit();
                            }
                        },3000);
                         */
                        if(IsRunningProgress()) {
                            DismissConnectDialog();
                        }
                        break;
                }
            }
        });
    }

    private void bleGattDisconnect() {
        DebugLogOutput(TAG, " connectedDeviceMap bluetoothGatt disconnect / MAC :"+selectedBleMac);
        if(connectedDeviceMap.containsKey(selectedBleMac)) {
            if(connectedDeviceMap.get(selectedBleMac) != null) {
                connectedDeviceMap.get(selectedBleMac).disconnect();
                connectedDeviceMap.get(selectedBleMac).close();
            }
            connectedDeviceMap.remove(selectedBleMac);
        }
    }

    private void bleGattDisconnect(String mac) {
        DebugLogOutput(TAG, " connectedDeviceMap bluetoothGatt disconnect / MAC :"+mac);
        if(connectedDeviceMap.containsKey(mac)) {
            if(connectedDeviceMap.get(mac) != null) {
                connectedDeviceMap.get(mac).disconnect();
                connectedDeviceMap.get(mac).close();
            }
            connectedDeviceMap.remove(mac);
        }
    }

    private void bleAwsDisconnect() {
        if (amazonFreeRTOSDevice != null) {
            DebugLogOutput(TAG, "AWS disconnectFromDevice");
            try {
                amazonFreeRTOSManager.disconnectFromDevice(amazonFreeRTOSDevice);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
            amazonFreeRTOSDevice = null;
        }
    }

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

    private void startBlinkLight() {
        changeUI(UI_STATUS_LOADING,"스마트 조명과 블루투스를 연결하였습니다.");
        status_process = PROCESS_STATUS_BLINK;
        if (!connectedDeviceMap.containsKey(selectedBleMac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedBleMac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback,TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback));
            }

        } else if (connectedDeviceMap.containsKey(selectedBleMac)) {
            bleGattWriteRequestCommand(bleProtocol.blinkLightProtocol);
            DebugLogOutput(TAG, "blinkLightProtocol : " + byteArrayToHex(bleProtocol.blinkLightProtocol));
        }
    }

    private void startUserRegister() {
        status_process = PROCESS_STATUS_USER_REGISTER;
        if (!connectedDeviceMap.containsKey(selectedBleMac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedBleMac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback,TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback));
            }
        } else if (connectedDeviceMap.containsKey(selectedBleMac)) {
            bleGattWriteRequestCommand(bleProtocol.userRegisterRequestProtocol);
            DebugLogOutput(TAG, "userRegisterRequestProtocol : " + byteArrayToHex(bleProtocol.userRegisterRequestProtocol));
        }
    }

    private void initLight(final int pos) {
        changeUI(UI_STATUS_LOADING,getString(R.string.activity_light_register_find_device_text_6));
        status_process = PROCESS_STATUS_INIT_LIGHT;

        String dname = ((AmazonBLEinfo)bleArrayAdapter.getItem(pos)).getName();
        selectedBleMac = ((AmazonBLEinfo)bleArrayAdapter.getItem(pos)).getMacAddr();

        if (!connectedDeviceMap.containsKey(selectedBleMac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedBleMac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback,TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback));
            }
        } else if (connectedDeviceMap.containsKey(selectedBleMac)) {
            bleGattWriteRequestCommand(bleProtocol.FactoryResetLightProtocol);
            DebugLogOutput(TAG, "FactoryResetLightProtocol : " + byteArrayToHex(bleProtocol.FactoryResetLightProtocol));
        }
    }

    private void sendAckStatus(boolean success) {
        if(success) {
            status_process = PROCESS_STATUS_SEND_ACK_STATUS_SUCCESS;
        } else {
            status_process = PROCESS_STATUS_SEND_ACK_STATUS_FAIL;
        }
        if (!connectedDeviceMap.containsKey(selectedBleMac)) {
            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedBleMac);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback,TRANSPORT_LE));
            } else {
                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback));
            }
        } else if (connectedDeviceMap.containsKey(selectedBleMac)) {
            if(success) {
                bleGattWriteRequestCommand(bleProtocol.lightStatusSuccessResponseProtocol);
                DebugLogOutput(TAG, "lightStatusSuccessResponseProtocol : " + byteArrayToHex(bleProtocol.lightStatusSuccessResponseProtocol));
            } else {
                bleGattWriteRequestCommand(bleProtocol.lightStatusFailResponseProtocol);
                DebugLogOutput(TAG, "lightStatusFailResponseProtocol : " + byteArrayToHex(bleProtocol.lightStatusFailResponseProtocol));
            }
        }
    }

    private void selectedLight(final int pos) {
        changeUI(UI_STATUS_LOADING,getString(R.string.activity_light_register_find_device_text_3));
        layoutListWifi.removeAllViews();
        String dname = ((AmazonBLEinfo)bleArrayAdapter.getItem(pos)).getName();
        selectedBleMac = ((AmazonBLEinfo)bleArrayAdapter.getItem(pos)).getMacAddr();
        txtWifiDeviceName.setText(dname);
        txtCompletedDeviceName.setText(dname);

        DebugLogOutput(TAG, "selectedLight: User Choice Device Mac, After using onBoarding : " + selectedBleMac);
        DebugLogOutput(TAG, "selectedLight: Compare Device Scan List Data & user Choice Data, Because Ui index, data index difference");
        int savePos = 0;
        for (int i = 0; i < bleList.size(); i++) {
            if (bleList.get(i).getName().equals(dname)) {
                savePos = i;
                DebugLogOutput(TAG, "onClick: find Same Deice");
                DebugLogOutput(TAG, "selectedLight: List Device Name: " + bleList.get(i).getName());
                DebugLogOutput(TAG, "selectedLight: Select Device Name: " + dname);
            }
        }

        DebugLogOutput(TAG, "AWS BLE Disconnect");
        bleAwsDisconnect();

        AWSCredentialsProvider awsCredentialsProvider = AWSMobileClient.getInstance();
        DebugLogOutput(TAG, "getInstance awsCredentialsProvider : " + awsCredentialsProvider);

        try {
            DebugLogOutput(TAG, "AWS BLE New Connect");
            amazonFreeRTOSDevice = amazonFreeRTOSManager.connectToDevice(
                    bleList.get(savePos).getBluetoothDevice(),
                    connectionStatusCallback,
                    awsCredentialsProvider,
                    false);
            DebugLogOutput(TAG, "selectedLight: amazonFreeRTOSDevice : " + amazonFreeRTOSDevice);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void ListNetwork() {
        wifiInfoList.clear();
        ListNetworkReq listNetworkReq = new ListNetworkReq();
        listNetworkReq.maxNetworks = 20;
        listNetworkReq.timeout = 5;
        if (amazonFreeRTOSDevice != null) {
            amazonFreeRTOSDevice.listNetworks(listNetworkReq, mNetworkConfigCallback);
        } else {
            RequestHandlerSendMessage(bleProtocolState.PROVISIONING_LIST_NETWORK_FAIL);
        }
    }

    private void saveNetwork(String bssid, String pw) {
        DebugLogOutput(TAG, "Wi-Provisioning Info : BSSID : " + bssid + " / Password :" + pw);
        SaveNetworkReq saveNetworkReq = new SaveNetworkReq();
        AmazonWifiInfo wifiInfo = mBssid2WifiInfoMap.get(bssid);
        saveNetworkReq.ssid = wifiInfo.getSsid();
        saveNetworkReq.bssid = wifiInfo.getBssid();
        saveNetworkReq.psk = pw;
        saveNetworkReq.security = wifiInfo.getNetworkType();
        saveNetworkReq.index = wifiInfo.getIndex();
        if (amazonFreeRTOSDevice != null) {
            amazonFreeRTOSDevice.saveNetwork(saveNetworkReq, mNetworkConfigCallback);
        } else {
            RequestHandlerSendMessage(bleProtocolState.PROVISIONING_SAVE_NETWORK_FAIL);
        }
    }

    private void inputPw(boolean needPw) {
        /*
        // user input text
        final EditText editText = new EditText(this);

        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Wi-Fi PassWord 입력");
        builder.setView(editText);
        builder.setMessage("[주의] PassWord를 입력하지 않으면 비밀번호가 없는 상태로 연결을 진행합니다.");
        builder.setPositiveButton("선택",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        wifipw = editText.getText().toString();
                        changeUI(UI_STATUS_LOADING,"Wi-Fi PassWord 저장 완료");
                        saveNetwork(wifibssid, wifipw);
                    }
                });

        builder.setNegativeButton("exit", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
            }
        });
        builder.show();
        */
        if(needPw) {
            Intent intent = new Intent(this, PopupActivity.class);
            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_INPUT);
            Bundle b = new Bundle();
            b.putString(PopupActivity.POPUP_INPUT, "");
            intent.putExtra(POPUP_BUNDLE, b);
            intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.input_ap_password));
            startActivityForResult(intent, RESULT_INPUT_AP_PASS);
        } else {
            changeUI(UI_STATUS_LOADING,"Wi-Fi PassWord 저장 완료");
            saveNetwork(wifibssid, "");
        }
    }


    @SuppressLint("MissingPermission")
    private void scanLeDevice(final boolean enable) {
        handler = new Handler();
        if (enable) {
            bleList.clear();
            // Stops scanning after a pre-defined scan period.
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    bluetoothAdapter.stopLeScan(leScanCallback);
                    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

                    DebugLogOutput(TAG, "run: Stop Scan");
                    bluetoothAdapter.stopLeScan(leScanCallback);

                    if(bleArrayAdapter == null) {
                        changeUI(UI_STATUS_SHOW_BLE_LIST,"검색된 장치가 없습니다.");
                        retryScanDevice();
                    } else if(bleArrayAdapter.getCount() == 0) {
                        changeUI(UI_STATUS_SHOW_BLE_LIST,"검색된 장치가 없습니다.");
                        retryScanDevice();
                    } else {
                        changeUI(UI_STATUS_SHOW_BLE_LIST,getString(R.string.activity_light_register_find_device_text_2));
                        bleArrayAdapter.notifyDataSetChanged();
                    }
                }
            }, SCAN_PERIOD);
            bluetoothAdapter.startLeScan(leScanCallback);
        } else {
            bluetoothAdapter.stopLeScan(leScanCallback);
        }
    }

    @SuppressLint("MissingPermission")
    private final BluetoothAdapter.LeScanCallback leScanCallback =
            new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, int rssi,
                                     final byte[] scanRecord) {
                    DebugLogOutput(TAG, "leScanCallback: Find AmazonBLEinfo Device");
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (device.getName() != null && device.getName().toUpperCase().contains("ICON_LM")) {
                                AmazonBLEinfo thisDevice = new AmazonBLEinfo(device.getName(),
                                        device.getAddress(), device);
                                if (!bleList.contains(thisDevice)) {
                                    bleList.add(thisDevice);
                                }
                            }
                        }
                    });
                }
            };

    private final NetworkConfigCallback mNetworkConfigCallback = new NetworkConfigCallback() {
        @Override
        public void onListNetworkResponse(final ListNetworkResp response) {
            mHandler = new Handler(Looper.getMainLooper());
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    AmazonWifiInfo wifiInfo =
                            new AmazonWifiInfo(response.getSsid(), response.getBssid(),
                                    response.getRssi(), response.getSecurity(), response.getIndex(),
                                    response.getConnected());

                    if (!wifiInfoList.contains(wifiInfo)) {
                        wifiInfoList.add(wifiInfo);
                        mBssid2WifiInfoMap.put(bssidToString(wifiInfo.getBssid()), wifiInfo);

                        //wifiArrayAdapter.notifyDataSetChanged();

                        // 화면에 표시될 View(Layout이 inflate된)으로부터 위젯에 대한 참조 획득
                        final View child = getLayoutInflater().inflate(R.layout.listview_add_wifi_list, null);
                        LinearLayout layoutWifi = child.findViewById(R.id.layoutWifi);
                        TextView txtSsid = child.findViewById(R.id.txtSsid);
                        ImageView imgPw = child.findViewById(R.id.imgPw);
                        ImageView imgRssi = child.findViewById(R.id.imgRssi);

                        // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
                        layoutWifi.setBackgroundResource(R.drawable.background_actionbar_button);
                        txtSsid.setText(wifiInfo.getSsid());
                        if(wifiInfo.getNetworkTypeName().equals("Open")) {
                            imgPw.setVisibility(View.INVISIBLE);
                        } else {
                            imgPw.setVisibility(View.VISIBLE);
                        }
                        if(wifiInfo.getRssi() > RSSI_LEVEL_3) {
                            imgRssi.setImageLevel(3);
                        } else if(wifiInfo.getRssi() > RSSI_LEVEL_2) {
                            imgRssi.setImageLevel(2);
                        } else if(wifiInfo.getRssi() > RSSI_LEVEL_1) {
                            imgRssi.setImageLevel(1);
                        } else {
                            imgRssi.setImageLevel(0);
                        }

                        child.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                wifibssid = bssidToString(wifiInfo.getBssid());
                                amazonWifiInfo = wifiInfo;
                                inputPw(!amazonWifiInfo.getNetworkTypeName().equals("Open"));
                            }
                        });
                        layoutListWifi.addView(child);
                    }
                }
            }, 300);
            RequestHandlerSendMessage(bleProtocolState.PROVISIONING_LIST_NETWORK_SUCCESS);
        }

        @Override
        public void onSaveNetworkResponse(final SaveNetworkResp response) {
            DebugLogOutput(TAG, "onSaveNetworkResponse(SaveNetworkResp) : " + response);
            RequestHandlerSendMessage(bleProtocolState.PROVISIONING_SAVE_NETWORK_SUCCESS);
        }
    };

    final BleConnectionStatusCallback connectionStatusCallback = new BleConnectionStatusCallback() {
        @Override
        public void onBleConnectionStatusChanged(AmazonFreeRTOSConstants.BleConnectionState connectionStatus) {
            DebugLogOutput(TAG, "AWS connection status changed to : " + connectionStatus);
            if (connectionStatus == AmazonFreeRTOSConstants.BleConnectionState.BLE_CONNECTED) {
                RequestHandlerSendMessage(bleProtocolState.PROVISIONING_BLE_CONNECTED);

            } else if (connectionStatus == AmazonFreeRTOSConstants.BleConnectionState.BLE_DISCONNECTED) {
                DebugLogOutput(TAG, "AWS connection Fail State : " + connectionStatus);
                RequestHandlerSendMessage(bleProtocolState.PROVISIONING_BLE_DISCONNECTED);
            }
        }
    };

    private final BluetoothGattCallback gattCallback = 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);
                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.ON_BOARDING_BLE_DISCONNECTED);

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

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
            DebugLogOutput(TAG, String.format(Locale.getDefault(),
                    "onCharacteristicChanged State from %s (%s) - mtu %d",
                    gatt.getDevice().getName(),
                    gatt.getDevice().getAddress(),
                    mtu
            ));
            gatt.discoverServices();
        }

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

            DebugLogOutput(TAG, "onServicesDiscovered LIGHT Device");
            BluetoothGattService deviceGattService = gatt.getService(hdcUUID.LIGHT_GATT_SERVICE_UUID);
            if (deviceGattService == null) {
                DebugLogOutput(TAG, "LIGHT/gattInit # No deviceGattService !!");
                return;
            }

            BluetoothGattCharacteristic bluetoothGattCharacteristic = deviceGattService.getCharacteristic(hdcUUID.LIGHT_GATT_NOTIFICATION_CHARACTERISTIC_UUID);
            if (bluetoothGattCharacteristic == null) {
                DebugLogOutput(TAG, "LIGHT/gattInit # No mDeviceGattCharacteristic !!");
                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 onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            byte[] data = characteristic.getValue();
            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_USER_REGISTERATION_COMPLETE_REQ_TO_APP) {
                if (data[3] == bleProtocol.DEVICE_ID_TYPE) {
                    if (data[4] == (byte) 0x01) {
                        resetDeviceData();
                        // device id 14 byte
                        DebugLogOutput(TAG, "1st Device ID Type Data Length : " + data.length);
                        for (int i = 6; i < data.length; i++) {
                            deviceIdNumber += (char) data[i];
                        }
                        DebugLogOutput(TAG, String.format("1st Device id : %s", deviceIdNumber));
                    } else if (data[4] == (byte) 0x02) {
                        DebugLogOutput(TAG, "2nd Device ID Type Data Length : " + data.length);
                        for (int i = 5; i < data.length; i++) {
                            deviceIdNumber += (char) data[i];
                        }
                    }
                    DebugLogOutput(TAG, String.format("1st + 2nd Device id : %s", deviceIdNumber));
                } else if (data[3] == bleProtocol.MODEL_ID_TYPE) {
                    DebugLogOutput(TAG, "Model ID Type Data Length : " + data.length);
                    for (int i = 5; i < data.length; i++) {
                        modelIdNumber += (char) data[i];
                    }
                    DebugLogOutput(TAG, String.format("Model id : %s", modelIdNumber));

                } else if (data[3] == bleProtocol.MAC_TYPE) {
                    // Mac
                    DebugLogOutput(TAG, "Mac Type Data Length : " + data.length);
                    for (int i = 0; i < (int) data[4]; i++) {
                        macNumber += String.format(": %02x ", data[i + 5] & 0xff);
                    }

                    DebugLogOutput(TAG, String.format("Mac : %s", macNumber));

                    if(data[11] == (byte) 0x01){
                        if(isUserRegisteStatus) {
                            DebugLogOutput(TAG, "get USER_REGISTER_COMPLETE_SUCCESS");
                            RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_COMPLETE_SUCCESS);
                        } else {
                            DebugLogOutput(TAG, "get USER_REGISTER_COMPLETE_FAIL(cmd is USER_REGISTER_COMPLETE_SUCCESS, but Timeout. so it's error.)");
                            RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_COMPLETE_FAIL);
                        }
                    } else {
                        DebugLogOutput(TAG, "get USER_REGISTER_COMPLETE_FAIL");
                        RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_COMPLETE_FAIL);
                    }
                }
            } else 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);
                } else if (data[3] == (byte) 0x02) {
                    DebugLogOutput(TAG, "get RSP_BLINK_PROTOCOL_FAIL");
                    RequestHandlerSendMessage(bleProtocolState.RSP_BLINK_PROTOCOL_FAIL);
                }
            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_ATOL_APP_STATUS_REQ_TO_LIGHT /*&& data[2] == (byte)0x02*/) {
                if ((data[3] & (byte) 0x01) == 0x01) {
                    DebugLogOutput(TAG, "get RSP_GET_WIFI_STATUS_SUCCESS");
                    RequestHandlerSendMessage(bleProtocolState.RSP_GET_WIFI_STATUS_SUCCESS);
                } else {
                    DebugLogOutput(TAG, "get RSP_GET_WIFI_STATUS_FAIL");
                    RequestHandlerSendMessage(bleProtocolState.RSP_GET_WIFI_STATUS_FAIL);
                }
            }
        }

        @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)
            ));

            if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_ATOL_USER_REGISTERATION_COMPLETE_RSP_TO_LIGHT && data[2] == (byte) (0x01)) {
                if (data[3] == (byte) (0x01)) {
                    RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_END);
                } else if (data[3] == (byte) (0x02)) {
                    RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_FAIL);
                }
            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_ATOL_BLINK_LIGHT_REQ_TO_LIGHT && data[2] == (byte) (0x01)) {
                RequestHandlerSendMessage(bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT);
            }
        }

        @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 (data[0] == (byte) 0x01 && data[1] == (byte) 0x00) {
                if (status_process == PROCESS_STATUS_USER_REGISTER) {
                    DebugLogOutput(TAG, "사용자등록 요청\nonDescriptorWrite: status_process = " + status_process);
                    bleGattWriteRequestCommand(bleProtocol.userRegisterRequestProtocol);
                } else if (status_process == PROCESS_STATUS_BLINK) {
                    DebugLogOutput(TAG, "깜빡임 요청\nonDescriptorWrite: status_process = " + status_process);
                    bleGattWriteRequestCommand(bleProtocol.blinkLightProtocol);
                } else if(status_process == PROCESS_STATUS_INIT_LIGHT) {
                    DebugLogOutput(TAG, "초기화 요청\nonDescriptorWrite: status_process = " + status_process);
                    bleGattWriteRequestCommand(bleProtocol.FactoryResetLightProtocol);
                } else if(status_process == PROCESS_STATUS_SEND_ACK_STATUS_SUCCESS) {
                    DebugLogOutput(TAG, "0x28 CMD STATUS 응답(0xA8) / SUCCESS\nonDescriptorWrite: status_process = " + status_process);
                    bleGattWriteRequestCommand(bleProtocol.lightStatusSuccessResponseProtocol);
                } else if(status_process == PROCESS_STATUS_SEND_ACK_STATUS_FAIL){
                    DebugLogOutput(TAG, "0x28 CMD STATUS 응답(0xA8) / FAIL\nonDescriptorWrite: status_process = " + status_process);
                    bleGattWriteRequestCommand(bleProtocol.lightStatusFailResponseProtocol);
                }
            }
        }
    };

    private void RequestHandlerSendMessage(int deviceState) {
        Message msg = new Message();
        msg.what = deviceState;
        if (deviceState == bleProtocolState.ON_BOARDING_TIMEOUT
                || deviceState == bleProtocolState.USER_REGISTER_REQUEST_TIMEOUT) {
            requestHandler.sendMessageDelayed(msg, USER_REGISTER_REQUEST_TIMEOUT_TIME);
        } else if (deviceState == bleProtocolState.HDC_ICON_LIGHT_REGISTER_SUCCESS) {
            requestHandler.sendMessageDelayed(msg, 3000);
        } else if(deviceState == bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT) {
            requestHandler.sendMessageDelayed(msg, DEFAULT_REG_DEVICE_LOADING_TIME);
        } else if(deviceState == bleProtocolState.RSP_GET_WIFI_STATUS_TIMEOUT) {
            requestHandler.sendMessageDelayed(msg, DEFAULT_WAIT_WIFI_STATUS_TIME);
        } else {
            requestHandler.sendMessage(msg);
        }
    }

    private void bleGattWriteRequestCommand(byte[] protocol) {
        bluetoothGattCharacteristic = connectedDeviceMap.get(selectedBleMac).getService(hdcUUID.LIGHT_GATT_SERVICE_UUID)
                .getCharacteristic(hdcUUID.LIGHT_GATT_WRITE_CHARACTERISTIC_UUID);
        bluetoothGattCharacteristic.setValue(protocol);
        connectedDeviceMap.get(selectedBleMac).writeCharacteristic(bluetoothGattCharacteristic);
    }

    private void resetDeviceData() {
        deviceIdNumber = "";
        modelIdNumber = "";
        macNumber = "";
    }

    private void retryScanDevice() {
        if(isRetry) {
            Intent intent = new Intent(LightRegisterDeviceActivity.this, PopupActivity.class);
            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL_WITH_ICON);
            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(LightRegisterDeviceActivity.this, PopupActivity.class);
            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO_WITH_ICON);
            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 retryMappingDevice() {
        if(isRetryMapping) {
            Intent intent = new Intent(LightRegisterDeviceActivity.this, PopupActivity.class);
            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL_WITH_ICON);
            intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_error_wifi_for_mapping_title));
            intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_error_wifi_for_mapping_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(LightRegisterDeviceActivity.this, PopupActivity.class);
            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO_WITH_ICON);
            intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_error_wifi_for_mapping_title));
            intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_error_wifi_for_mapping_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_MAPPING_DEVICE);
        }
    }

    /**
     * 매핑 API를 호출하여 조명모듈 매핑 요청 함수.
     */
    private void startMappingLightDevice() {
        if(deviceIdNumber.isEmpty()) {
            changeUI(UI_STATUS_SHOW_FAIL,"매핑할 Device ID가 존재하지 않습니다.");
            retryScanDevice();
        } else {
            if(!IsRunningProgress()) {
                displayProgress(LightRegisterDeviceActivity.this,"","");
            }
            try {
                HttpApi.mappingDevice(deviceIdNumber, new okhttp3.Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        e.printStackTrace();
                        ErrorLogOutput(TAG, "HttpApi.mappingDevice(onFailure)\n" + e.toString());
                        changeUI(UI_STATUS_SHOW_FAIL,"AWS IOT 매핑에 실패하였습니다.");
                        retryMappingDevice();
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        if(response.code() == HttpApi.HTTP_NO_CONTENT) {
                            WarnLogOutput(TAG, "HttpApi.mappingDevice(onResponse) HTTP_NO_CONTENT");

                            //다른 Activity에서 처리하게 수정 막음
                            //RequestHandlerSendMessage(bleProtocolState.HDC_ICON_LIGHT_REGISTER_SUCCESS);
                            //changeUI(UI_STATUS_SHOW_COMPLETED,"등록이 완료되었습니다.");

                            //매핑 완료시 BLE 연결해제 로직을 이동 - > bleProtocolState.USER_REGISTER_END, bleProtocolState.USER_REGISTER_FAIL에 항상 호출.
                            //bleAwsDisconnect();
                            //bleGattDisconnect();

                            if(IsRunningProgress()) {
                                DismissConnectDialog();
                            }

                            Intent intent = new Intent(LightRegisterDeviceActivity.this, LightRegisterDeviceFinishActivity.class);
                            intent.putExtra(EXTRA_DEVICE_ID,deviceIdNumber);
                            startActivity(intent);
                            finish();

                        } else if(response.code() == HTTP_UNAUTHORIZED) {
                            showNotiMsg(LightRegisterDeviceActivity.this, getString(R.string.snackbar_expired_token));
                            restartApp(LightRegisterDeviceActivity.this);
                        } else {
                            ErrorLogOutput(TAG, "HttpApi.mappingDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
                            try {
                                JSONObject json_data = new JSONObject(response.body().string());
                                ErrorLogOutput(TAG, "HttpApi.mappingDevice(onResponse) body : " + json_data.toString());
                            } catch (JSONException e) {
                                ErrorLogOutput(TAG, "HttpApi.mappingDevice(onResponse) JSONException : " + e.toString());
                            }
                            changeUI(UI_STATUS_SHOW_FAIL,"AWS IOT 매핑에 실패하였습니다.");
                            retryMappingDevice();
                        }
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
                ErrorLogOutput(TAG, "HttpApi.mappingDevice(Exception)\n" + e.toString());
                changeUI(UI_STATUS_SHOW_FAIL,"AWS IOT 매핑에 실패하였습니다.");
                retryMappingDevice();
            }
        }
    }

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

        // ListViewAdapter의 생성자
        public BleArrayAdapter(List<AmazonBLEinfo> listItem) {
            listViewItemList = listItem;
        }

        // 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된)으로부터 위젯에 대한 참조 획득
            TextView txtName = convertView.findViewById(R.id.txtName);
            ImageView imgIcon = convertView.findViewById(R.id.imgIcon);

            // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
            imgIcon.setImageResource(R.drawable.icon_add_device_light);
            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();
        }
    }

    /**
     * Wifi 리스트 아답터
     */
    public class WifiArrayAdapter extends BaseAdapter {
        // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
        private List<AmazonWifiInfo> listViewItemList;

        // ListViewAdapter의 생성자
        public WifiArrayAdapter(List<AmazonWifiInfo> listItem) {
            listViewItemList = listItem;
        }

        // 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_wifi_list, parent, false);
            }

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

            // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
            txtSsid.setText(listViewItemList.get(position).getSsid());

            if(listViewItemList.get(position).getNetworkTypeName().equals("Open")) {
                imgPw.setVisibility(View.INVISIBLE);
            } else {
                imgPw.setVisibility(View.VISIBLE);
            }

            if(listViewItemList.get(position).getRssi() > RSSI_LEVEL_3) {
                imgRssi.setImageLevel(3);
            } else if(listViewItemList.get(position).getRssi() > RSSI_LEVEL_2) {
                imgRssi.setImageLevel(2);
            } else if(listViewItemList.get(position).getRssi() > RSSI_LEVEL_1) {
                imgRssi.setImageLevel(1);
            } else {
                imgRssi.setImageLevel(0);
            }

            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();
        }
    }
}