const readServiceID = '0000FFE0-0000-1000-8000-00805F9B34FB';
const readID = '0000FFE4-0000-1000-8000-00805F9B34FB';
const writeServiceID = '0000FFE5-0000-1000-8000-00805F9B34FB';
const writeID = '0000FFE9-0000-1000-8000-00805F9B34FB';
var readPart = '';

function acceptDevice(device) {
    return device.btid ? true : false;
}

function isDevice(device, data) {
    const advertisData = new Uint8Array(data.advertisData);
    const mac = device.btid
        .split('')
        .map((p, i) => parseInt(p + device.btid[i + 1], 16))
        .filter((p, i) => i % 2 == 0);

    if (advertisData.slice(0, 2).toString() == [0x52, 0x53].toString() && advertisData.slice(4, 10).toString() == mac.toString()) {
        return true;
    }

    return false;
}

function readDataBP00(value, data) {
    if (value.split('HSO').length == 2) {
        data.imei = value.split('HSO')[0];
        value = value.split('HSO')[1];

        while (value != '') {
            switch (value[0]) {
                case 'P':
                    data.quantity = parseInt(value.slice(1, 3));
                    value = value.slice(3, value.length);
                    break;

                case 'S':
                    data.gpscount = parseInt(value.slice(1, 3), 16);
                    value = value.slice(3, value.length);
                    break;

                case 'G':
                    data.gsmlevel = parseInt(value.slice(1, 3), 16);
                    value = value.slice(3, value.length);
                    break;

                case 'T':
                    data.temp = parseInt(value.slice(1, 5), 16) < 0x8000 ? parseInt(value.slice(1, 5), 16) : parseInt(value.slice(1, 5), 16) - 0x10000;
                    value = value.slice(5, value.length);
                    break;

                case 'C':
                    data.cycle = parseInt(value.slice(1, 5), 16);
                    value = value.slice(5, value.length);
                    break;

                case 'B':
                    var state = parseInt(value.slice(1, 3), 16);
                    data.lowTempProtectState = (state >> 0) & 1;
                    data.highTempProtectState = (state >> 1) & 1;
                    data.dischargeProtectState = (state >> 2) & 1;
                    data.chargeProtectState = (state >> 3) & 1;
                    data.lowVoltageState = (state >> 4) & 1;
                    data.chargeState = (state >> 5) & 3;
                    data.oilState = (state >> 7) & (1 == 0) ? 1 : 0;
                    value = value.slice(3, value.length);
                    break;

                case 'X':
                    var state = parseInt(value.slice(1, 3), 16);
                    data.autoProtectState = (state >> 0) & 1;
                    data.elecState = (state >> 1) & (1 == 0) ? 1 : 0;
                    data.factoryType = state >> 2;
                    value = value.slice(3, value.length);
                    break;

                default:
                    if (['P', 'S', 'G', 'T', 'C', 'B', 'X'].indexOf(value[3]) == -1) {
                        //data.voltage = parseInt(value.slice(0, 4), 16) / 100
                        value = value.slice(4, value.length);
                    } else {
                        //data.voltage = parseInt(value.slice(0, 3), 16) / 100
                        value = value.slice(3, value.length);
                    }
            }
        }

        return data;
    }

    return false;
}

function readDataBS50(value, data) {
    const length = parseInt(value.slice(0, 4), 16);
    value = value.slice(4, value.length);

    if (value.length == length * 2) {
        return BMSReply(
            value
                .split('')
                .map((p, i) => parseInt(p + value[i + 1], 16))
                .filter((p, i) => i % 2 == 0),
            data
        );
    }

    return false;
}

function BMSReply(value, data) {
    if (value[0] == 0x4e && value[1] == 0x57) {
        //value = value.slice(2, value.length)
        const length = value[2] * 0x100 + value[3];

        if (value.length == length + 2) {
            data.bmsid = value
                .slice(4, 8)
                .map((p) => ('00' + p.toString(16)).substr(p.toString(16).length))
                .join('');

            if (
                value[8] == 0x06 &&
                value[value.length - 5] == 0x68 &&
                value.slice(0, value.length - 4).reduce((p, c) => p + c) == value[value.length - 2] * 0x100 + value[value.length - 1]
            ) {
                value = value.slice(11, value.length - 9);

                while (value.length != 0) {
                    switch (value[0]) {
                        case 0x79:
                            const voltageLength = parseInt(value[1] / 3);
                            data.voltageList = Array(voltageLength);
                            let maxVoltage = -1;
                            let minVoltage = 99999;
                            let sumVoltage = 0;

                            for (var i = 0; i < voltageLength; i++) {
                                let _v = Math.round(value[i * 3 + 3] * 0x100 + value[i * 3 + 4]) / 1000;

                                data.voltageList[value[i * 3 + 2] - 1] = _v;
                                sumVoltage += _v;
                                if (_v > maxVoltage) maxVoltage = _v;
                                if (_v < minVoltage) minVoltage = _v;
                            }

                            data.maxVoltage = maxVoltage;
                            data.minVoltage = minVoltage;
                            data.sumVoltage = sumVoltage;
                            value = value.slice(voltageLength * 3 + 2, value.length);
                            break;

                        case 0x80:
                            data.powerMOSTemp = value[1] * 0x100 + value[2];
                            if (data.powerMOSTemp > 100) data.powerMOSTemp = 100 - data.powerMOSTemp;
                            value = value.slice(3, value.length);
                            break;

                        case 0x81:
                            data.batteryChamberTemp = value[1] * 0x100 + value[2];
                            if (data.batteryChamberTemp > 100) data.batteryChamberTemp = 100 - data.batteryChamberTemp;
                            value = value.slice(3, value.length);
                            break;

                        case 0x82:
                            data.temp = value[1] * 0x100 + value[2];
                            if (data.temp > 100) data.temp = 100 - data.temp;
                            value = value.slice(3, value.length);
                            break;

                        case 0x83:
                            data.voltage = Math.round(value[1] * 0x100 + value[2]) / 100;
                            value = value.slice(3, value.length);
                            break;

                        case 0x84:
                            data.chargeState = 0;
                            _value = value[1] * 0x100 + value[2];

                            if (_value > 10000) {
                                data.current = (_value - 10000) * 0.01 * -1;
                                data.chargeState = 2;
                            } else if (_value < 10000) {
                                data.current = (10000 - _value) * 0.01;
                                data.chargeState = 1;
                            }

                            value = value.slice(3, value.length);
                            break;

                        case 0x85:
                            data.quantity = value[1];
                            data.soc = value[1];
                            value = value.slice(2, value.length);
                            break;

                        case 0x86:
                            data.tempCount = value[1];
                            value = value.slice(2, value.length);
                            break;

                        case 0x87:
                            data.cycle = value[1] * 0x100 + value[2];
                            value = value.slice(3, value.length);
                            break;

                        case 0x89:
                            data.totalCirculatingCapacity = value[1] * 0x1000000 + value[2] * 0x10000 + value[3] * 0x100 + value[4];
                            value = value.slice(5, value.length);
                            break;

                        case 0x8a:
                            data.count = value[1] * 0x100 + value[2];
                            value = value.slice(3, value.length);
                            break;

                        case 0x8b:
                            data.alarmState = [];
                            if (((value[2] >> 0) & 1) == 1) data.alarmState.push('低容量报警');
                            if (((value[2] >> 1) & 1) == 1) data.alarmState.push('MOS管超温报警');
                            if (((value[2] >> 2) & 1) == 1) data.alarmState.push('充电过压报警');
                            if (((value[2] >> 3) & 1) == 1) data.alarmState.push('放电欠压报警');
                            if (((value[2] >> 4) & 1) == 1) data.alarmState.push('电池超温报警');
                            if (((value[2] >> 5) & 1) == 1) data.alarmState.push('充电过流报警');
                            if (((value[2] >> 6) & 1) == 1) data.alarmState.push('放电过流报警');
                            if (((value[2] >> 7) & 1) == 1) data.alarmState.push('电芯压差报警');
                            if (((value[1] >> 0) & 1) == 1) data.alarmState.push('电池箱内超温报警');
                            if (((value[1] >> 1) & 1) == 1) data.alarmState.push('电池低温报警');
                            if (((value[1] >> 2) & 1) == 1) data.alarmState.push('单体过压报警');
                            if (((value[1] >> 3) & 1) == 1) data.alarmState.push('单体欠压报警');
                            if (((value[1] >> 4) & 1) == 1) data.alarmState.push('309_A保护');
                            if (((value[1] >> 5) & 1) == 1) data.alarmState.push('309_B保护');
                            value = value.slice(3, value.length);
                            break;

                        case 0x8c:
                            data.chargeProtectState = (value[2] >> 0) & 1;
                            data.dischargeProtectState = (value[2] >> 1) & 1;
                            data.equilibrium = (value[2] >> 2) & 1;
                            data.disconnectState = (value[2] >> 3) & 1;
                            value = value.slice(3, value.length);
                            break;

                        case 0x8e:
                            value = value.slice(3, value.length);
                            break;

                        case 0x8f:
                            value = value.slice(3, value.length);
                            break;

                        case 0x90:
                            value = value.slice(3, value.length);
                            break;

                        case 0x91:
                        case 0x92:
                        case 0x93:
                        case 0x94:
                        case 0x95:
                        case 0x96:
                        case 0x97:
                        case 0x98:
                        case 0x99:
                        case 0x9a:
                        case 0x9b:
                        case 0x9c:
                            value = value.slice(3, value.length);
                            break;

                        case 0x9d:
                            value = value.slice(2, value.length);
                            break;

                        case 0x9e:
                        case 0x9f:
                        case 0xa0:
                        case 0xa1:
                        case 0xa2:
                        case 0xa3:
                        case 0xa4:
                        case 0xa5:
                        case 0xa6:
                        case 0xa7:
                        case 0xa8:
                            value = value.slice(3, value.length);
                            break;

                        case 0xa9:
                            value = value.slice(2, value.length);
                            break;

                        case 0xaa:
                            // 电池容量
                            data.capacity = (value[1] << 24) | (value[2] << 16) | (value[3] << 8) | value[4];
                            value = value.slice(5, value.length);
                            break;

                        case 0xab:
                        case 0xac:
                            value = value.slice(2, value.length);
                            break;

                        case 0xad:
                            value = value.slice(3, value.length);
                            break;

                        case 0xae:
                        case 0xaf:
                            value = value.slice(2, value.length);
                            break;

                        case 0xb0:
                            value = value.slice(3, value.length);
                            break;

                        case 0xb1:
                            value = value.slice(2, value.length);
                            break;

                        case 0xb2:
                            value = value.slice(11, value.length);
                            break;

                        case 0xb3:
                            value = value.slice(2, value.length);
                            break;

                        case 0xb4:
                            value = value.slice(9, value.length);
                            break;

                        case 0xb5:
                            value = value.slice(5, value.length);
                            break;

                        case 0xb6:
                            value = value.slice(5, value.length);
                            break;

                        case 0xb7:
                            // 软件版本号 15bit
                            data.firmware = '';

                            for (let i = 1; i <= 15; i++) {
                                data.firmware += String.fromCharCode(value[i]);
                            }

                            value = value.slice(16, value.length);
                            break;

                        case 0xb8:
                            value = value.slice(2, value.length);
                            break;

                        case 0xb9:
                            value = value.slice(5, value.length);
                            break;

                        case 0xba:
                            value = value.slice(25, value.length);
                            break;

                        default:
                            value = [];
                    }
                }
            }

            return data;
        }
    }

    return false;
}

function readData(device, value, data) {
    value = Array.from(new Int8Array(value))
        .map((p) => String.fromCharCode(p))
        .join('');

    if (value[value.length - 1] == ')') {
        value = readPart.concat(value);
        readPart = '';
    } else if (value[0] == '(') {
        readPart = value;
    } else {
        readPart = readPart.concat(value);
    }

    if (value[0] == '(' && value[value.length - 1] == ')') {
        value = value.slice(1, value.length - 1);

        if (value.slice(0, 12) == device.mac_id) {
            value = value.slice(12, value.length);

            switch (value.slice(0, 4)) {
                case 'BP00':
                    return readDataBP00(value.slice(4, value.length), data);

                case 'BS50':
                    return readDataBS50(value.slice(4, value.length), data);
            }
        }
    }

    return false;
}
/**
 * cmd: 命令字 02:写
 */

function BMSCommand(data, cmd, type = 0) {
    // 帧来源: 01
    // 传输类型: 00
    const length = data.length + 18;
    var data = [0x4e, 0x57, parseInt(length / 0x100), parseInt(length % 0x100), 0x00, 0x00, 0x00, 0x00, cmd, 0x01, type].concat(data).concat([0x00, 0x00, 0x00, 0x00, 0x68]);
    const sum = data.reduce((p, c) => p + c);
    data.push(parseInt(sum / 0x1000000));
    data.push(parseInt((sum % 0x1000000) / 0x10000));
    data.push(parseInt((sum % 0x10000) / 0x100));
    data.push(parseInt(sum % 0x100));
    console.log(data);
    return data;
}

function BMSRead() {
    return BMSCommand([0x00], 0x06, 0);
}

function BMSTurnOn() {
    return BMSCommand([0xac, 1], 0x02, 1);
}

function BMSTurnOff() {
    return BMSCommand([0xac, 0], 0x02, 1);
}

function bmsChargingMOS(value) {
    return BMSCommand([0xab, value], 0x02, 0);
}

function bmsDischargeMOS(value) {
    return BMSCommand([0xac, value], 0x02, 0);
}

function sendData(device, cmd, data = []) {
    return (
        '(' +
        device.mac_id +
        cmd +
        data
            .map((p) => ('00' + p.toString(16)).substr(p.toString(16).length))
            .join('')
            .toUpperCase() +
        ')'
    )
        .split('')
        .map((p) => p.charCodeAt(0));
}

function sendDataAE00(device, data) {
    return sendData(device, 'AE00', [data.length % 0x100, parseInt(data.length / 0x100)].concat(data));
}

function stateUpdate(device, deviceId) {
    return [sendData(device, 'AU20'), sendDataAE00(device, BMSRead())];
}

function turnOn(device, deviceId) {
    return [sendDataAE00(device, BMSTurnOn())];
}

function turnOff(device, deviceId) {
    return [sendDataAE00(device, BMSTurnOff())];
}

function bmsInfo(device, deviceId, info) {
    return false;
}

function bmsSet(device, deviceId, name, vars) {
    return false;
}

module.exports = {
    readServiceID: readServiceID,
    readID: readID,
    writeServiceID: writeServiceID,
    writeID: writeID,
    acceptDevice: acceptDevice,
    isDevice: isDevice,
    readData: readData,
    stateUpdate: stateUpdate,
    turnOn: turnOn,
    turnOff: turnOff,
    bmsInfo: bmsInfo,
    bmsSet: bmsSet,
    BMSCommand: BMSCommand,
    BMSRead: BMSRead,
    BMSTurnOn: BMSTurnOn,
    BMSTurnOff: BMSTurnOff,
    BMSReply: BMSReply,
    bmsChargingMOS: bmsChargingMOS,
    bmsDischargeMOS: bmsDischargeMOS
};
