package net.northking.cctp.upperComputer.driver.adb;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;

/**
 * Adb 设备对象<br>
 * 每个对象即为一台安卓设备。<br>
 * 此对象应随Adb类列出数据时产生。
 */
public class AdbDevice implements Cloneable {
    /**
     * 设备Adb 序列号
     */
    private final String serial;

    /**
     * 设备USB位置<br>
     * USB集线器序号和接口号
     */
    private String usb;

    /**
     * 设备产品名称
     */
    private String product;

    /**
     * 设备型号
     */
    private String model;

    /**
     * 设备名称
     */
    private String device;

    /**
     * 传输端口id
     */
    private int transportId;

    /**
     * 当前Adb设备状态<br>
     * 注意状态不会平滑变化，更新也具有几百毫秒的延迟（详见Adb::watchDevice）。
     */
    public State state = State.Unknown;

    private Logger log = LoggerFactory.getLogger(AdbDevice.class);

    //region 解析内容行的开头
    private static final String PREFIX_USB = "usb:";
    private static final String PREFIX_PRODUCT = "product:";
    private static final String PREFIX_MODEL = "model:";
    private static final String PREFIX_DEVICE = "device:";
    private static final String PREFIX_TRANSPORT_ID = "transport_id:";
    //endregion

    private UpdateListener mUpdateListener = null;


    public AdbDevice(String lineText) {
        if (lineText == null || lineText.isEmpty()) {
            throw new IllegalArgumentException("无法从null或空内容中创建AdbDevice");
        }
        String[] contentList = lineText.trim().split(" ");
        if (contentList.length == 0) {
            throw new IllegalStateException("无法从[" + lineText + "]中解析出任何内容");
        }
        String serial = contentList[0];
        if (serial == null || serial.isEmpty()) {
            throw new IllegalArgumentException("无法从[" + lineText + "]中解析出设备serial");
        }
        this.serial = serial;

        //todo 读取信息
        for (int i = 1; i < contentList.length; i++) {
            String content = contentList[i];
            if (content != null && !content.isEmpty()) {
                if (state == State.Unknown) {
                    setStateByText(content);
                } else if (content.startsWith(PREFIX_USB)) {
                    usb = content.substring(PREFIX_USB.length());
                } else if (content.startsWith(PREFIX_PRODUCT)) {
                    product = content.substring(PREFIX_PRODUCT.length());
                } else if (content.startsWith(PREFIX_MODEL)) {
                    model = content.substring(PREFIX_MODEL.length());
                } else if (content.startsWith(PREFIX_DEVICE)) {
                    device = content.substring(PREFIX_DEVICE.length());
                } else if (content.startsWith(PREFIX_TRANSPORT_ID)) {
                    try {
                        transportId = Integer.parseInt(content.substring(PREFIX_TRANSPORT_ID.length()));
                    } catch (NumberFormatException numberFormatException) {
                        transportId = -1;
                        log.warn("无法将[" + content + "]中的值作为整型处理");
                    }
                }
            }
        }

    }


    /**
     * 获得设备Adb 序列号
     *
     * @return Adb序列号
     */
    public String getSerial() {
        return serial;
    }

    /**
     * 获得USB位置
     *
     * @return USB位置
     */
    public String getUsb() {
        return usb;
    }

    /**
     * 获得设备产品名称
     *
     * @return 设备产品名称
     */
    public String getProduct() {
        return product;
    }

    /**
     * 获得设备型号
     *
     * @return 设备型号
     */
    public String getModel() {
        return model;
    }

    /**
     * 获得设备名称
     *
     * @return 设备名称
     */
    public String getDevice() {
        return device;
    }

    /**
     * 获得传输端口id
     *
     * @return 传输端口id
     */
    public int getTransportId() {
        return transportId;
    }


    public void setUpdateListener(UpdateListener updateListener) {
        this.mUpdateListener = updateListener;
    }

    /**
     * 用adb返回的新设备状态更新已有的设备信息
     *
     * @param adbDevice 最新设备数据
     */
    public void update(AdbDevice adbDevice) {
        if (!adbDevice.serial.equals(serial)) throw new IllegalArgumentException("更新的对象serial不匹配");
        AdbDevice old = null;
        UpdateListener updateListener = mUpdateListener;
        if (updateListener != null) {
            old = clone();
        }

        state = adbDevice.state;
        usb = adbDevice.usb;
        product = adbDevice.product;
        model = adbDevice.model;
        device = adbDevice.device;
        transportId = adbDevice.transportId;

        if (old != null) {
            updateListener.onUpdate(old, this);
        }
    }

    public void updateStateByText(String stateText) {
        AdbDevice old = null;
        UpdateListener updateListener = mUpdateListener;
        if (updateListener != null) {
            old = clone();
        }
        setStateByText(stateText);
        if (old != null) {
            updateListener.onUpdate(old, this);
        }
    }

    /**
     * 根据字符串内容设置当前设备状态
     *
     * @param text 状态文本
     */
    private void setStateByText(String text) {
        switch (text) {
            case "offline" :
                state = State.Offline;
                break;
            case "device" :
                state = State.Device;
                break;
            case "recovery" :
                state = State.Recovery;
                break;
            case "bootloader" :
                state = State.BootLoader;
                break;
            case "unauthorized" :
                state = State.Unauthorized;
                break;
            case "authorizing" :
                state = State.Authorizing;
                break;
            case "sideload" :
                state = State.Sideload;
                break;
            case "connecting" :
                state = State.Connecting;
                break;
            case "rescue" :
                state = State.Rescue;
                break;
            default :
                throw new IllegalStateException("未知的设备状态：" + state);
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AdbDevice adbDevice = (AdbDevice) o;
        return serial.equals(adbDevice.serial);
    }

    @Override
    public int hashCode() {
        return Objects.hash(serial);
    }

    @Override
    public String toString() {
        return "AdbDevice{" +
                "serial='" + serial + '\'' +
                ", usb='" + usb + '\'' +
                ", product='" + product + '\'' +
                ", model='" + model + '\'' +
                ", device='" + device + '\'' +
                ", transportId=" + transportId +
                ", state=" + state +
                '}';
    }


    @Override
    public AdbDevice clone() {
        try {
            return (AdbDevice) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

    interface UpdateListener {
        void onUpdate(AdbDevice old, AdbDevice current);
    }

    /**
     * 设备Adb连接状态
     */
    public enum State {
        /**
         * 未知状态<br>
         * （默认）
         */
        Unknown,

        /**
         * 离线<br>
         * 线缆已连接，USB调试模式打开，但设备未开始或结束了ADB调试。<br>
         * 这种情况需要在设备上选择USB传输模式为文件或照片管理，或者重新连接设备。
         */
        Offline,

        /**
         * 正常可用<br>
         * 设备已正常连接，且处于常规操作系统内。
         */
        Device,

        /**
         * 恢复模式<br>
         * 设备正处于恢复程序状态，部分功能受限，但sideload可用
         */
        Recovery,

        /**
         * 引导程序模式<br>
         * 设备正处于引导程序模式，所有功能受限，仅fastboot可用
         */
        BootLoader,

        /**
         * 没有授权<br>
         * 设备未授权此Adb Server进行连接
         */
        Unauthorized,

        /**
         * 正在授权<br>
         * 设备正在让用户进行授权操作（输入密码等）<br>
         */
        Authorizing,

        /**
         * 侧载模式<br>
         * 设备在等待Adb server推送所需要的数据
         */
        Sideload,

        /**
         * 正在连接
         */
        Connecting,

        /**
         * 救援模式
         */
        Rescue;

        /**
         * 是否处于可开启shell的状态
         *
         * @return 是否处于可开启shell的状态
         */
        boolean canCreateShell() {
            return this == Device || this == Recovery;
        }

        /**
         * 是否处于可进行端口转发的状态
         *
         * @return 是否处于可进行端口转发的状态
         */
        boolean canCreateTunnel() {
            return this == Device || this == Recovery;
        }
    }
}
