package aicare.net.cn.iweightdemo.utils;

import android.content.Context;
import android.os.Handler;
import android.text.TextUtils;

import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nullable;

import aicare.net.cn.iweightlibrary.bleprofile.BleProfileService;
import aicare.net.cn.iweightlibrary.bleprofile.BleProfileServiceReadyActivity;
import aicare.net.cn.iweightlibrary.entity.AlgorithmInfo;
import aicare.net.cn.iweightlibrary.entity.BM09Data;
import aicare.net.cn.iweightlibrary.entity.BM15Data;
import aicare.net.cn.iweightlibrary.entity.BodyFatData;
import aicare.net.cn.iweightlibrary.entity.BroadData;
import aicare.net.cn.iweightlibrary.entity.DecimalInfo;
import aicare.net.cn.iweightlibrary.entity.User;
import aicare.net.cn.iweightlibrary.entity.WeightData;
import aicare.net.cn.iweightlibrary.utils.AicareBleConfig;
import aicare.net.cn.iweightlibrary.utils.L;
import aicare.net.cn.iweightlibrary.wby.WBYService;

public class BLEProfileServiceManager extends BleProfileServiceReadyActivity {
    private final static String TAG = "WeighingScale";

    private WBYService.WBYBinder binder;
    private BroadData cacheBroadData;
    private List<BroadData> listValues;

    private Handler handler = new Handler();
    private User user = null;

    private byte unit = AicareBleConfig.UNIT_KG;
    private boolean isNewBM15TestData;
    private BleProfileService bleProfileService;

    @Override
    public void onResume() {
        super.onResume();
        if (this.binder == null) {
            bindService(null);
        }
    }

    @Override
    public void onDestroy() {
        stopScan();
        if (isDeviceConnected()) {
            this.binder.disconnect();
        }
        super.onDestroy();
    }

    public void initiateBLE(Context context) {
        listValues = new ArrayList<>();
        if(context != null) {

            if (!ensureBLESupported()) {
                listValues = new ArrayList<>();
                //T.showShort(this, R.string.not_support_ble);
                //finish();
            }

            if (!isBLEEnabled()) {
                //showBLEDialog();
            }
        }

    }

    private void startLeScan() {
        startScan();
    }

    private void stopLeScan() {
        stopScan();
    }

    public void connect(BroadData device) {
        if (device.getDeviceType() == AicareBleConfig.TYPE_WEI_BROAD || device
                .getDeviceType() == AicareBleConfig.TYPE_WEI_TEMP_BROAD || device
                .getDeviceType() == AicareBleConfig.BM_09 || device
                .getDeviceType() == AicareBleConfig.BM_15) {
            cacheBroadData = device;
            //showInfo(getString(R.string.state_bound, device.getAddress()), true);
            //setStateTitle(device.getAddress(), -1);
            startLeScan();
        } else {
            startConnect(device.getAddress());
        }
    }

    @Override
    public void onStateChanged(String deviceAddress, int state) {
        super.onStateChanged(deviceAddress, state);
        switch (state) {
            case BleProfileService.STATE_CONNECTED:
                //showInfo(getString(R.string.state_connected, deviceAddress), true);
                setStateTitle(deviceAddress, state);
                break;
            case BleProfileService.STATE_DISCONNECTED:
                //showInfo(getString(R.string.state_disconnected), true);
                setStateTitle(deviceAddress, state);
                break;
            case BleProfileService.STATE_SERVICES_DISCOVERED:
                //showInfo(getString(R.string.state_service_discovered), true);
                break;
            case BleProfileService.STATE_INDICATION_SUCCESS:
                //showInfo(getString(R.string.state_indication_success), true);
                break;
            case BleProfileService.STATE_TIME_OUT:
                //showInfo(getString(R.string.state_time_out), true);
                break;
            case BleProfileService.STATE_CONNECTING:
                //showInfo(getString(R.string.state_connecting), true);
                break;
        }
    }

    private void setStateTitle(final String deviceAddress, final int state) {
        switch (state) {
            case BleProfileService.STATE_CONNECTED:
                L.e(TAG, "STATE_CONNECTED");
                //toolbar.setSubtitle(deviceAddress);
                //menu.getItem(0).setTitle(R.string.disconnect);
                break;
            case BleProfileService.STATE_DISCONNECTED:
                L.e(TAG, "STATE_DISCONNECTED");
                //toolbar.setSubtitle("");
                //menu.getItem(0).setTitle(R.string.start_scan);
                //setDefault();
                break;
            case -1:
                //toolbar.setSubtitle(deviceAddress);
                //menu.getItem(0).setTitle(R.string.unbound);
                break;
        }
    }

    @Override
    public void onError(final String errMsg, final int errCode) {
        L.e(TAG, "Message = " + errMsg + " errCode = " + errCode);
        //showInfo(getString(R.string.state_error, errMsg, errCode), true);
    }

    @Override
    public void onGetWeightData(final WeightData weightData) {
        if (weightData == null)
            return;

        String weight = AicareBleConfig.getWeight(weightData.getWeight(), unit, weightData.getDecimalInfo());
        L.e(TAG, "Weight = " + weight);
        /*if (weightData.getTemp() != Double.MAX_VALUE) {
            tv_temp.setText(getString(R.string.temp, String.valueOf(weightData.getTemp())));
        }

        if (weightData.getDeviceType() == AicareBleConfig.BM_15) {
            if (weightData.getCmdType() != 3) {
                isNewBM15TestData = true;
                //showInfo(weightData.toString(), false);
            }
            if (weightData.getCmdType() == 3 && weightData.getAdc() > 0 && isNewBM15TestData) {
                isNewBM15TestData = false;
                BodyFatData bm15BodyFatData = AicareBleConfig
                        .getBM15BodyFatData(weightData, user.getSex(), user.getAge(), user
                                .getHeight());
                //showInfo(bm15BodyFatData.toString(), true);
            }
        }*/
    }

    @Override
    public void onGetSettingStatus(@AicareBleConfig.SettingStatus int status) {
        L.e(TAG, "SettingStatus = " + status);
        switch (status) {
            case AicareBleConfig.SettingStatus.NORMAL:
                //showInfo(getString(R.string.settings_status, getString(R.string.normal)), true);
                break;
            case AicareBleConfig.SettingStatus.LOW_POWER:
                //showInfo(getString(R.string.settings_status, getString(R.string.low_power)), true);
                break;
            case AicareBleConfig.SettingStatus.LOW_VOLTAGE:
                //showInfo(getString(R.string.settings_status, getString(R.string.low_voltage)), true);
                break;
            case AicareBleConfig.SettingStatus.ERROR:
                //showInfo(getString(R.string.settings_status, getString(R.string.error)), true);
                break;
            case AicareBleConfig.SettingStatus.TIME_OUT:
                //showInfo(getString(R.string.settings_status, getString(R.string.time_out)), true);
                break;
            case AicareBleConfig.SettingStatus.UNSTABLE:
                //showInfo(getString(R.string.settings_status, getString(R.string.unstable)), true);
                break;
            case AicareBleConfig.SettingStatus.SET_UNIT_SUCCESS:
                //showInfo(getString(R.string.settings_status, getString(R.string.set_unit_success)), true);
                break;
            case AicareBleConfig.SettingStatus.SET_UNIT_FAILED:
                //showInfo(getString(R.string.settings_status, getString(R.string.set_unit_failed)), true);
                break;
            case AicareBleConfig.SettingStatus.SET_TIME_SUCCESS:
                //showInfo(getString(R.string.settings_status, getString(R.string.set_time_success)), true);
                break;
            case AicareBleConfig.SettingStatus.SET_TIME_FAILED:
                //showInfo(getString(R.string.settings_status, getString(R.string.set_time_failed)), true);
                break;
            case AicareBleConfig.SettingStatus.SET_USER_SUCCESS:
                //showInfo(getString(R.string.settings_status, getString(R.string.set_user_success)), true);
                break;
            case AicareBleConfig.SettingStatus.SET_USER_FAILED:
                //showInfo(getString(R.string.settings_status, getString(R.string.set_user_failed)), true);
                break;
            case AicareBleConfig.SettingStatus.UPDATE_USER_LIST_SUCCESS:
                //showInfo(getString(R.string.settings_status, getString(R.string.update_user_list_success)), true);
                break;
            case AicareBleConfig.SettingStatus.UPDATE_USER_LIST_FAILED:
                //showInfo(getString(R.string.settings_status, getString(R.string.update_user_list_failed)), true);
                break;
            case AicareBleConfig.SettingStatus.UPDATE_USER_SUCCESS:
                //showInfo(getString(R.string.settings_status, getString(R.string.update_user_success)), true);
                break;
            case AicareBleConfig.SettingStatus.UPDATE_USER_FAILED:
                //showInfo(getString(R.string.settings_status, getString(R.string.update_user_failed)), true);
                break;
            case AicareBleConfig.SettingStatus.NO_HISTORY:
                //showInfo(getString(R.string.settings_status, getString(R.string.no_history)), true);
                break;
            case AicareBleConfig.SettingStatus.HISTORY_START_SEND:
                //showInfo(getString(R.string.settings_status, getString(R.string.history_start_send)), true);
                break;
            case AicareBleConfig.SettingStatus.HISTORY_SEND_OVER:
                //showInfo(getString(R.string.settings_status, getString(R.string.history_send_over)), true);
                break;
            case AicareBleConfig.SettingStatus.NO_MATCH_USER:
                //showInfo(getString(R.string.settings_status, getString(R.string.no_match_user)), true);
                break;
            case AicareBleConfig.SettingStatus.ADC_MEASURED_ING:
                //showInfo(getString(R.string.settings_status, getString(R.string.adc_measured_ind)), true);
                break;
            case AicareBleConfig.SettingStatus.ADC_ERROR:
                //showInfo(getString(R.string.settings_status, getString(R.string.adc_error)), true);
                break;
            case AicareBleConfig.SettingStatus.UNKNOWN:
                //showInfo(getString(R.string.settings_status, getString(R.string.unknown)), true);
                break;
            case AicareBleConfig.SettingStatus.REQUEST_DISCONNECT:
                //showInfo(getString(R.string.settings_status, getString(R.string.request_disconnect)), true);
                break;

            case AicareBleConfig.SettingStatus.DATA_SEND_END:
                //showInfo(getString(R.string.settings_status, getString(R.string.data_send_end)), true);
                break;
        }
    }

    @Override
    public void onGetResult(final int index, final String result) {
        L.e(TAG, "index = " + index + "; result = " + result);
        switch (index) {
            case WBYService.BLE_VERSION:
                //showInfo(getString(R.string.ble_version, result), true);
                break;
            case WBYService.USER_ID:
                //showInfo(getString(R.string.user_id, result), true);
                break;
            case WBYService.MCU_DATE:
                //showInfo(getString(R.string.mcu_date, result), true);
                break;
            case WBYService.MCU_TIME:
                //showInfo(getString(R.string.mcu_time, result), true);
                break;
            case WBYService.ADC:
                //showInfo(getString(R.string.adc, result), true);
                break;
        }
    }

    @Override
    public void onGetFatData(boolean isHistory, final BodyFatData bodyFatData) {
        L.e(TAG, "isHistory = " + isHistory + "; BodyFatData = " + bodyFatData.toString());
        if (isHistory) {
            //showInfo(getString(R.string.history_data, bodyFatData.toString()), true);
        } else {
            //showInfo(getString(R.string.body_fat_data, bodyFatData.toString()), true);
            Double bodyFat = Double.valueOf(
                    AicareBleConfig.getWeight(bodyFatData.getWeight(),
                    AicareBleConfig.UNIT_KG,
                    bodyFatData.getDecimalInfo())
                ) * 10;
            if (bodyFatData.getAdc() != 0) {
                //seek_bar_adc.setProgress(bodyFatData.getAdc());
            }
            if (isDeviceConnected() && bodyFatData.getAdc() != 0) {
                /*userList.clear();
                userList.add(user);*/
                handler.postDelayed(updateRunnable, 50);
            }
        }
    }

    private Runnable updateRunnable = new Runnable() {
        @Override
        public void run() {
            binder.updateUser(user);
        }
    };

    @Override
    protected void onGetDecimalInfo(DecimalInfo decimalInfo) {
        if (decimalInfo == null)
            return;
        L.e(TAG, decimalInfo.toString());
        /*String decimalStr = (getString(R.string.source_decimal, decimalInfo
                .getSourceDecimal())) + (getString(R.string.kg_decimal, decimalInfo
                .getKgDecimal())) + (getString(R.string.lb_decimal, decimalInfo
                .getLbDecimal())) + (getString(R.string.st_decimal, decimalInfo
                .getStDecimal())) + (getString(R.string.kg_graduation, decimalInfo
                .getKgGraduation())) + (getString(R.string.lb_graduation, decimalInfo
                .getLbGraduation()));*/
        //showInfo(decimalStr, true);
    }

    @Override
    protected void onGetAlgorithmInfo(AlgorithmInfo algorithmInfo) {
        if (algorithmInfo == null)
            return;
        /*String algorithmStr = (getString(R.string.adc, String
                .valueOf(algorithmInfo.getAdc())) + (getString(R.string.algorithm_id, algorithmInfo
                .getAlgorithmId())));*/
        //showInfo(algorithmStr, true);
    }

    @Override
    protected void onServiceBinded(WBYService.WBYBinder binder) {
        this.binder = binder;
        L.e("2017-11-20", TAG + ", onServiceBinded: binder = " + binder);
    }

    @Override
    protected void onServiceUnbinded() {
        this.binder = null;
        L.e("2017-11-20", TAG + ", onServiceUnbinded");
    }

    @Override
    protected void getAicareDevice(final BroadData broadData) {
        if (broadData != null) {
            L.e(TAG, broadData.toString());

            if (cacheBroadData != null && TextUtils
                    .equals(cacheBroadData.getAddress(), broadData.getAddress())) {
                if (broadData.getDeviceType() == AicareBleConfig.BM_09) {
                    if (broadData.getSpecificData() != null) {
                        BM09Data data = AicareBleConfig
                                .getBm09Data(broadData.getAddress(), broadData.getSpecificData());
                        if (isNewData(data) && data.getWeight() != 0) {
                            //showInfo(data.toString(), false);
                            //tv_did.setText("DID:" + data.getDid());
                        }
                    }
                } else if (broadData.getDeviceType() == AicareBleConfig.BM_15) {
                    if (broadData.getSpecificData() != null) {
                        //Radio Group
                        //rg_change_unit.setOnCheckedChangeListener(null);
                        BM15Data data = AicareBleConfig
                                .getBm15Data(broadData.getAddress(), broadData.getSpecificData());
                        WeightData weightData = new WeightData();
                        weightData.setAdc(data.getAdc());
                        weightData.setCmdType(data.getAgreementType());
                        weightData.setDeviceType(broadData.getDeviceType());
                        switch (data.getUnitType()) {
                            case 1:
                                unit = AicareBleConfig.UNIT_KG;
                                //rg_change_unit.check(R.id.rb_kg);
                                weightData.setDecimalInfo(new DecimalInfo(1, 1, 1, 1, 1, 2));
                                break;
                            case 2:
                                unit = AicareBleConfig.UNIT_LB;
                                //rg_change_unit.check(R.id.rb_lb);
                                weightData.setDecimalInfo(new DecimalInfo(1, 1, 1, 1, 1, 2));
                                break;
                            case 3:
                                unit = AicareBleConfig.UNIT_ST;
                                //rg_change_unit.check(R.id.rb_st);
                                weightData.setDecimalInfo(new DecimalInfo(1, 1, 1, 1, 1, 2));
                                break;
                            case 4:
                                unit = AicareBleConfig.UNIT_KG;
                                //rg_change_unit.check(R.id.rb_kg);
                                weightData.setDecimalInfo(new DecimalInfo(1, 1, 1, 1, 1, 1));
                                break;
                            case 5:
                                unit = AicareBleConfig.UNIT_LB;
                                //rg_change_unit.check(R.id.rb_lb);
                                weightData.setDecimalInfo(new DecimalInfo(1, 1, 1, 1, 1, 1));
                                break;
                            case 6:
                                unit = AicareBleConfig.UNIT_ST;
                                //rg_change_unit.check(R.id.rb_st);
                                weightData.setDecimalInfo(new DecimalInfo(1, 1, 1, 1, 1, 1));
                                break;
                        }
                        weightData.setWeight(data.getWeight());
                        weightData.setTemp(data.getTemp());
                        onGetWeightData(weightData);

                        if (isNewData(data) && data.getWeight() != 0) {
                            //tv_did.setText("DID:" + data.getDid());
                        }
                    }
                } else {
                    if (broadData.getSpecificData() != null) {
                        WeightData weightData = AicareBleConfig
                                .getWeightData(broadData.getSpecificData());
                        onGetWeightData(weightData);
                    }
                }
            }
        }
    }

    private BM09Data bm09Data;

    private boolean isNewData(BM09Data data) {
        if (bm09Data == null) {
            bm09Data = data;
            return true;
        }
        if (bm09Data.getWeight() != data.getWeight()) {
            bm09Data = data;
            return true;
        }
        return false;
    }

    private BM15Data mBM15Data;

    private boolean isNewData(BM15Data data) {
        if (mBM15Data == null) {
            mBM15Data = data;
            return true;
        }
        if (mBM15Data.getWeight() != data.getWeight()) {
            mBM15Data = data;
            return true;
        }
        return false;
    }

    //use this to send events/messages to React Native front
    public void sendEventToJS(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
        reactContext
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, params);
    }
}
