package cn.joyway.joywaybeacon;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

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

import cn.joyway.lib.ConvertEx;
import cn.joyway.lib.JoywayLib;
import cn.joyway.lib.bluetooth.BT;
import cn.joyway.lib.bluetooth.Beacon;
import cn.joyway.lib.bluetooth.BeaconConnectStatus;
import cn.joyway.lib.bluetooth.dfu.Dfu;
import cn.joyway.lib.bluetooth.dfu.DfuEventHandler;
import cn.joyway.lib.bluetooth.dfu.DfuStatus;
import cn.joyway.lib.bluetooth.dfu.FirmwareVersions;
import cn.joyway.lib.bluetooth.dfu.RequestFirmwareVersionInfoFromServerCallback;
import cn.joyway.lib.device_config.DeviceConfig;
import cn.joyway.lib.util.InputMethodHelper;

//所有的设备配置窗口，都继承本类。 因为本类已经封装了共用的基本的功能。
public abstract class Activity_JWBase extends AppCompatActivity {

    //region 一般成员变量
    String _mac;
    String _passwordInputByUser;
    int _currentFWPID = -1;
    FirmwareVersions _firmwareVersions;
    ArrayList<Byte> _workingParamsFromDevice = new ArrayList<>(); // byte[] _workingParamsFromDevice = new byte[72];
    DeviceConfig _deviceConfig;
    //endregion

    //region 控件变量
    TextView _tvTitle;
    TextView _tvMac;
    TextView _tvFWPID;//固件发布ID号
    TextView _tvConnectStatus;
    Button _bnSave;
    EditText _etDevicePassword;
    CheckBox _cbShowDevicePassword;
    TextView _tvNoticeForWaiting;
    //endregion

    //region 抽象函数，需要子类必须实现，因为基类要调用它们。
    abstract void bindUI();//子类必须实现此函数来将成员变量跟UI控件绑定，并监听控件的事件。
    abstract int WORKING_PARAM_LEN();
    abstract void showWorkingParamsToUI();//将接收到的设备参数显示到UI上。
    abstract byte[] getWorkingParamsFromUI();//将UI里输入的全部参数，整理到byte[]里，用于发送给设备。
    //endregion

    void showConnectStatus(BeaconConnectStatus connectStatus) {

        if (connectStatus == BeaconConnectStatus.Disconnected) {
            _tvConnectStatus.setText(R.string.disconnected);
        } else if (connectStatus == BeaconConnectStatus.Connecting) {
            _tvConnectStatus.setText(R.string.connecting);
        } else if (connectStatus == BeaconConnectStatus.Connected) {
            _tvConnectStatus.setText(R.string.connected);
        }

        if (connectStatus == BeaconConnectStatus.Connected) {
            _tvConnectStatus.setTextColor(Color.parseColor("#00AA00"));
        } else {
            _tvConnectStatus.setTextColor(Color.parseColor("#DD0000"));
        }
    }

    void listenCommonButtonEvent() {
        _bnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //隐藏输入法
                InputMethodHelper.hide(Activity_JWBase.this);
                //执行保存
                saveParamToDevice_showWaitingFrame();
            }
        });
        _tvFWPID.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //region 如果没有从服务器获取到版本信息，就无法升级。再连接服务器查询一次版本信息。
                if (_firmwareVersions == null) {
                    new AlertDialog.Builder(Activity_JWBase.this)
                            .setTitle(R.string.notice)
                            .setMessage(R.string.getting_version_info_from_server_please_retry_in_3_seconds)
                            .setPositiveButton(R.string.ok, null)
                            .show();
                    requestFirmwareInfoFromServer(); //再连接服务器一次查询版本信息。
                }
                //endregion

                //如果有新版本
                if (_currentFWPID < _firmwareVersions.lastestFWPID()){
                    showDialogToUpdateFirmwareByFWPID();
                }
                //已经最新版本，客户可以强刷固件
                else if (_currentFWPID >= 0 && _currentFWPID <= 65534 &&
                        _currentFWPID >= _firmwareVersions.lastestFWPID()) {
                    showDialogToUpdateFirmwareByFWPIDForcely();
                }
                //如果是 65535， 只能通过“ProductName”来更新固件
                else if (_currentFWPID == 65535) {
                    showDialogToUpdateFirmwareByProductName();
                }
            }
        });
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //region 读取窗口intent的参数
        Intent intent = getIntent();
        _mac = intent.getStringExtra("MAC");
        _passwordInputByUser = intent.getStringExtra("PASSWORD");
        //endregion

        //region 【注意：基类里所有UI控件变量都没有初始化，都是null，因此基类是通过调用抽象函数来实现对子类控件的访问】
        bindUI();
        listenCommonButtonEvent();
        show_Title_Mac_ConnectStatus();
        show_WaitingFrame(getString(R.string.reading_parameters));
        //endregion

        //region 使用DeviceConfig SDK初始化设备配置
        _deviceConfig = new DeviceConfig(_mac, _passwordInputByUser, WORKING_PARAM_LEN(), _deviceConfigEventHandler);
        _deviceConfig.start();
        //endregion
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (_deviceConfig != null) {
            _deviceConfig.stop();
        }
    }

    // DeviceConfig事件处理器
    DeviceConfig.DeviceConfigEventHandler _deviceConfigEventHandler = new DeviceConfig.DeviceConfigEventHandler() {
        @Override
        public void onConnectStatusChanged(String mac, String status, boolean isConnected) {
            Beacon beacon = BT.getBeacon(mac);
            if (beacon != null) {
                showConnectStatus(beacon.getConnectStatus());
            }
        }

        @Override
        public void onParamsRead(String mac, ArrayList<Byte> params) {
            _workingParamsFromDevice = params;

            //如果参数的长度符合预期，就显示到UI上
            int receivedParamsLen = params.size();
            boolean valid = receivedParamsLen == 72 || receivedParamsLen == 75 ||
                    receivedParamsLen == 128 || receivedParamsLen == 135 ||
                    receivedParamsLen == 152 || receivedParamsLen == 165 ||
                    receivedParamsLen == 200 || receivedParamsLen == 210;
            if (valid) {
                try {
                    showWorkingParamsToUI();
                } catch (Exception ex) {
                    new AlertDialog.Builder(Activity_JWBase.this)
                            .setMessage(R.string.this_device_need_to_update_firmware)
                            .create().show();
                }
            } else {
                new AlertDialog.Builder(Activity_JWBase.this)
                        .setMessage(R.string.this_device_need_to_update_firmware)
                        .create().show();
            }

            //得到参数了，用户就可以动手操作了
            _tvNoticeForWaiting.setText(R.string.jw_activity_reading_done);
            _tvNoticeForWaiting.setVisibility(View.GONE);
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
        }

        @Override
        public void onParamsSaved(String mac, boolean success, String errorMsg) {
            if (success) {
                _tvNoticeForWaiting.setText(R.string.jw_activity_saving_done);
                new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        _tvNoticeForWaiting.setVisibility(View.GONE);
                        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
                        Activity_JWBase.this.finish();
                    }
                }, 2000);
            } else {
                if ("Incorrect password".equals(errorMsg)) {
                    new AlertDialog.Builder(Activity_JWBase.this)
                            .setTitle(R.string.incorrect_password)
                            .setIcon(android.R.drawable.ic_dialog_alert)
                            .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.dismiss();
                                    Activity_JWBase.this.finish();
                                }
                            })
                            .show();
                } else {
                    _tvNoticeForWaiting.setText(R.string.jw_activity_saving_rejected);
                    new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            _tvNoticeForWaiting.setVisibility(View.GONE);
                            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
                        }
                    }, 2000);
                }
            }
        }

        @Override
        public void onFirmwareVersionReceived(String mac, int fwpId) {
            _currentFWPID = fwpId;
            _tvFWPID.setText(String.valueOf(fwpId));

            //根据当前PublishID查询服务器上的固件完整版本信息
            requestFirmwareInfoFromServer();
        }
    };

    //根据当前FWPID查询服务器上的固件完整版本信息
    void requestFirmwareInfoFromServer() {
        Dfu.requestFirmwarePublishInfoFromServer(JoywayLib.getContext(), Consts.ERPWS_Url(), Consts.ERPWS_Namespace(),
                _currentFWPID, new RequestFirmwareVersionInfoFromServerCallback() {
                    @Override
                    public void onFirmwareVersionInfoRequestResult(FirmwareVersions versions) {
                        _firmwareVersions = versions;
                        //有新固件
                        if (versions.lastestFWPID() > _currentFWPID) {
                            String fwpidText = String.format(Locale.getDefault(), "%d->%d\n%s",
                                    _currentFWPID, _firmwareVersions.lastestFWPID(),
                                    _firmwareVersions.getWhatsNew(JoywayLib.getContext()));
                            _tvFWPID.setText(fwpidText);
                            _tvFWPID.setTextColor(Color.parseColor("#0000AA"));
                            _tvFWPID.setPaintFlags(_tvFWPID.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);//增加下划线
                            showDialogToUpdateFirmwareByFWPID();
                        }
                        //没有新固件
                        else {
                            String fwpidText = String.valueOf(_currentFWPID);
                            _tvFWPID.setText(fwpidText);
                            _tvFWPID.setTextColor(Color.parseColor("#000000"));
                            _tvFWPID.setPaintFlags(_tvFWPID.getPaintFlags() & ~Paint.UNDERLINE_TEXT_FLAG);//去掉下划线
                        }
                    }
                });
    }

    // 弹出对话框 “发现新的固件，是否升级？”
    private void showDialogToUpdateFirmwareByFWPID() {
        new AlertDialog.Builder(Activity_JWBase.this)
                .setTitle(R.string.device_firmsare_update)
                .setMessage(R.string.new_firmare_is_availabe_update_it_now)
                .setPositiveButton(R.string.update, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        show_WaitingFrame(getString(R.string.updating));
                        BT.setScanTimeLength(0);//为了dfu过程不卡死，能顺利连接设备，这里必须停止扫描。
                        //关闭自动连接，但是这里不要执行断开动作//开始DFU之前，需要设置为不需要连接。让DFU模块去连接和升级。 //【实测确实能加速DFU传输速度】
                        BT.setAutoConnect(_mac, false);
                        Dfu.startDfuToTargetFWPID(Consts.ERPWS_Url(), Consts.ERPWS_Namespace(), _mac, _currentFWPID,
                                _firmwareVersions.lastestFWPID(), _tvTitle.getText().toString(),
                                false, _dfuEventHandler);
                    }
                }).setNegativeButton(R.string.no_ask_me_next_time, null).create().show();
    }

    private void showDialogToUpdateFirmwareByFWPIDForcely() {
        new AlertDialog.Builder(Activity_JWBase.this)
                .setTitle(R.string.device_firmsare_update)
                .setMessage(R.string.its_already_using_the_latest_firmware_update_anyway)
                .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        show_WaitingFrame(getString(R.string.updating));
                        BT.setScanTimeLength(0);//为了dfu过程不卡死，能顺利连接设备，这里必须停止扫描。
                        //关闭自动连接，但是这里不要执行断开动作//开始DFU之前，需要设置为不需要连接。让DFU模块去连接和升级。 //【实测确实能加速DFU传输速度】
                        BT.setAutoConnect(_mac, false);
                        Dfu.startDfuToTargetFWPID(Consts.ERPWS_Url(), Consts.ERPWS_Namespace(), _mac, _currentFWPID,
                                _firmwareVersions.lastestFWPID(), _tvTitle.getText().toString(),
                                false, _dfuEventHandler);
                    }
                })
                .setNegativeButton(R.string.no, null)
                .show();
    }

    private void showDialogToUpdateFirmwareByProductName() {

        String productName = _tvTitle.getText().toString().trim();
        if (productName.endsWith("%") && productName.contains("_")) {
            int index = productName.lastIndexOf("_");
            productName = productName.substring(0, index);
        }

        EditText etProductName = new EditText(this);
        etProductName.setText(productName);
        new AlertDialog.Builder(Activity_JWBase.this)
                .setTitle(R.string.device_firmsare_update)
                .setMessage(R.string.please_confirm_product_name_to_update_firmware_for_example)
                .setView(etProductName)
                .setPositiveButton(R.string.update, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        show_WaitingFrame(getString(R.string.updating));
                        BT.setScanTimeLength(0);//为了dfu过程不卡死，能顺利连接设备，这里必须停止扫描。
                        //关闭自动连接，但是这里不要执行断开动作//开始DFU之前，需要设置为不需要连接。让DFU模块去连接和升级。 //【实测确实能加速DFU传输速度】
                        BT.setAutoConnect(_mac, false);

                        Dfu.startDfuWithProductName(Consts.ERPWS_Url(), Consts.ERPWS_Namespace(), _mac,
                                etProductName.getText().toString(), false, _dfuEventHandler);
                    }
                })
                .setNegativeButton(R.string.cancel, null)
                .show();
    }

    //从网站下载最新的固件，然后开始更新。两种升级方式，都使用同一个 dfuEventHandler。
    DfuEventHandler _dfuEventHandler = new DfuEventHandler() {
        @Override
        public void onDfuProgress(String mac, DfuStatus dfuStatus, int progress) {
            if (dfuStatus == DfuStatus.Stopped_NoNewVersion) {
                _tvNoticeForWaiting.setText(R.string.there_is_no_firmware_for_target_fwpid_on_server);
                _tvNoticeForWaiting.setVisibility(View.VISIBLE);
                getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
            }
            if (dfuStatus == DfuStatus.FileDownloaded) {
                _tvNoticeForWaiting.setText(R.string.firmware_downloaded_starting_ota);
                _tvNoticeForWaiting.setVisibility(View.VISIBLE);
                getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
            }
            else if (dfuStatus == DfuStatus.UploadingProgressChanged) {
                String msg;
                if (progress < 100) {
                    String fmtMsg = getString(R.string.updating) + "\n(%d%%)\n";
                    msg = String.format(fmtMsg, progress);
                } else {
                    msg = getString(R.string.restarting_device_please_wait);
                }
                _tvNoticeForWaiting.setText(msg);
                _tvNoticeForWaiting.setVisibility(View.VISIBLE);
                getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
            }
            else if (dfuStatus == DfuStatus.DfuCompleted) {
                _tvNoticeForWaiting.setText(R.string.jw_activity_update_done);
                _tvNoticeForWaiting.setVisibility(View.GONE);
                getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

                //DFU完成了，就需要这本页面中设置为需要连接。
                BT.setAutoConnect(_mac, true);

                //提示用户 “升级成功”！
                new AlertDialog.Builder(Activity_JWBase.this).setTitle(R.string.firmware_updated_successfully)
                        .setPositiveButton(R.string.ok, null).show();
            }
        }
    };

    void saveParamToDevice_showWaitingFrame() {

        byte[] workingParamsToSave = getWorkingParamsFromUI();

        //如果没有从UI上获取到有效的参数，就不能执行保存
        if (workingParamsToSave == null) {
            return;
        }

        //region 显示正在保存的窗口，禁止用户操作UI
        _tvNoticeForWaiting.setText(R.string.jw_activity_saving_parameters);
        _tvNoticeForWaiting.setVisibility(View.VISIBLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
        //endregion

        //使用DeviceConfig SDK保存参数到设备
        if (_deviceConfig != null) {
            _deviceConfig.saveParamsToDevice(workingParamsToSave);
        }
    }

    void show_WaitingFrame(String msg) {
        //region 用户不可点击画面
        _tvNoticeForWaiting.setText(msg);
        _tvNoticeForWaiting.setVisibility(View.VISIBLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
        //endregion
    }

    void show_Title_Mac_ConnectStatus() {
        Beacon beacon = BT.getBeacon(_mac);
        _tvTitle.setText(beacon.getDeviceAdvertisingName());
        _tvMac.setText(_mac);
        showConnectStatus(beacon.getConnectStatus());
    }
}
