/**
 * 将二进制数组转化为无符号整型数值的方法
 *   array             二进制数组
 *   islittleEndian    是否小端
 * @returns {number}
 */
function byteArrayToUInt(array: Uint8Array, isLittleEndian: boolean): number {
    const dataView = new DataView(new Uint8Array(array).buffer);
    let value = 0;
    switch (array.length) {
        case 2:
            value = dataView.getUint16(0, isLittleEndian);
            break;
        case 4:
            value = dataView.getUint32(0, isLittleEndian);
            break;
    }
    return value;
}

/**
 * 解析行情订阅请求包
 * @param rspPackage
 */
export function parseSubscribeRsp(content: Uint8Array): Promise<Proto.QuoteRsp[]> {
    const result: Proto.QuoteRsp[] = [];
    return new Promise((resolve, reject) => {
        if (content.length === 0) {
            // 有可能空订阅，也有可能订阅返回数据为空，但其实已经订阅了商品
            // 暂做正常返回 空字符串 处理，如遇到问题继续优化即可
            console.warn('行情订阅请求包返回为空');
            resolve(result);
        } else {
            const count = byteArrayToUInt(content.subarray(0, 4), false);
            if (count > 1000) {
                // 游客模式 刷新成长问题，为了不让页面卡死，暂时处理
                resolve(result);
            } else {
                switch (count) {
                    case -1:
                        reject('订阅失败');
                        break;
                    case -2:
                        reject('Token校验失败');
                        break;
                    case -3:
                        reject('无对应商品信息');
                        break;
                    default:
                        // 判断返回的Count是否有问题
                        if (!count || count > (content.length - 4) / 66) {
                            reject('行情订阅返回数据不正确');
                        } else {
                            let position = 4;
                            for (let i = 0; i < count; i++) {
                                const dataArray = content.subarray(position, position + 66);
                                const subState = dataArray[0];
                                // FIXME: 目前行情接入服务不能正确返回交易所代码
                                const exchangeCode = dataArray[2];
                                const goodsCode = String.fromCharCode(...dataArray.subarray(2, dataArray.length)).trim();
                                result.push({ subState, goodsCode, exchangeCode });
                                position += 66;
                            }
                            resolve(result);
                        }
                }
            }
        }
    })
}

/**
 * 解析行情推送报文
 * @param {Array} quotationData	行情信息
 */
export function parseReceivePush(quotationData: Uint8Array) {
    const result: Proto.Quote[] = [];

    // 目前发现可能会传入空数组
    if (quotationData.length) {
        // 分解行正则
        const regRow = /10\s.*?11/g;
        // 分解单行 key 正则
        const regKey = /01\s.*?02/g;
        // 分解单行 value 正则
        const regValue = /02\s.*?01|02\s.*?11/g;

        // 记录已经更新盘面的商品信息数组
        // 0x10 ... 0x01 key 0x02 value 0x01 key 0x02 value ... 0x11
        const hexString = Array.prototype.map.call(quotationData, (x) => ('00' + x.toString(16)).slice(-2)).join(' ');
        // let hexString = 'FF 10 01 55 02 45 46 47 48 01 66 02 48 47 46 45 11 10 01 77 02 AA BB CC DD 11 00';

        // 获取单行行情
        const rows = hexString.match(regRow);
        if (rows) {
            for (const low of rows) {
                // 获取 key value 表列
                const keys = low.match(regKey);
                const values = low.match(regValue);

                if (keys && values) {
                    const quote: Proto.Quote = {};

                    for (let i = 0; i < keys.length; i++) {
                        const key = parseInt(keys[i].substring(3, 5), 16);
                        const tmpValue = values[i];
                        const value = new Uint8Array(tmpValue.substring(3, tmpValue.length - 3).split(' ').map((byte) => parseInt(byte, 16)));

                        setQuoteTikFieldByByte(quote, key, value);
                    }

                    if (Object.keys(quote).length) {
                        result.push(quote);
                        //console.log('行情推送', quote);
                    }
                }
            }
        }
    }

    return result;
}

/**
 * 分解缓存行情字段的方法
 * @param quote 
 * @param key 
 * @param value 
 */
function setQuoteTikFieldByByte(quote: Proto.Quote, key: number, value: Uint8Array) {
    const strValue = String.fromCharCode(...value);
    switch (key) {
        case 0x56:
            quote.exchangecode = Number(strValue);
            break;
        case 0x21:
            quote.goodscode = strValue;
            break;
        case 0x24:
            quote.last = Number(strValue);
            break;
        case 0x5b:
            quote.holdvolume = Number(strValue);
            break;
        case 0x25:
            quote.lastvolume = Number(strValue);
            break;
        case 0x3c:
            quote.preholdvolume = Number(strValue);
            break;
        case 0x32:
            quote.presettle = Number(strValue);
            break;
        case 0x33:
            quote.settle = Number(strValue);
            break;
        case 0x29:
            quote.totalturnover = Number(strValue);
            break;
        case 0x28:
            quote.totalvolume = Number(strValue);
            break;
        case 0x35:
            quote.limithigh = Number(strValue);
            break;
        case 0x36:
            quote.limitlow = Number(strValue);
            break;
        case 'L'.charCodeAt(0):
            quote.ask = Number(strValue);
            break;
        case 'M'.charCodeAt(0):
            quote.ask2 = Number(strValue);
            break;
        case 'N'.charCodeAt(0):
            quote.ask3 = Number(strValue);
            break;
        case 'O'.charCodeAt(0):
            quote.ask4 = Number(strValue);
            break;
        case 'P'.charCodeAt(0):
            quote.ask5 = Number(strValue);
            break;
        case 'Q'.charCodeAt(0):
            quote.askvolume = Number(strValue);
            break;
        case 'R'.charCodeAt(0):
            quote.askvolume2 = Number(strValue);
            break;
        case 'S'.charCodeAt(0):
            quote.askvolume3 = Number(strValue);
            break;
        case 'T'.charCodeAt(0):
            quote.askvolume4 = Number(strValue);
            break;
        case 'U'.charCodeAt(0):
            quote.askvolume5 = Number(strValue);
            break;
        case 'B'.charCodeAt(0):
            quote.bid = Number(strValue);
            break;
        case 'C'.charCodeAt(0):
            quote.bid2 = Number(strValue);
            break;
        case 'D'.charCodeAt(0):
            quote.bid3 = Number(strValue);
            break;
        case 'E'.charCodeAt(0):
            quote.bid4 = Number(strValue);
            break;
        case 'F'.charCodeAt(0):
            quote.bid5 = Number(strValue);
            break;
        case 'G'.charCodeAt(0):
            quote.bidvolume = Number(strValue);
            break;
        case 'H'.charCodeAt(0):
            quote.bidvolume2 = Number(strValue);
            break;
        case 'I'.charCodeAt(0):
            quote.bidvolume3 = Number(strValue);
            break;
        case 'J'.charCodeAt(0):
            quote.bidvolume4 = Number(strValue);
            break;
        case 'K'.charCodeAt(0):
            quote.bidvolume5 = Number(strValue);
            break;
        case ','.charCodeAt(0):
            quote.highest = Number(strValue);
            break;
        case '-'.charCodeAt(0):
            quote.lowest = Number(strValue);
            break;
        case '@'.charCodeAt(0):
            quote.date = strValue;
            break;
        case 'A'.charCodeAt(0):
            quote.time = strValue;
            break;
        case '+'.charCodeAt(0):
            quote.preclose = Number(strValue);
            break;
        case '.'.charCodeAt(0):
            quote.opened = Number(strValue);
            break;
        case 0x5c:
            quote.exerciseprice = Number(strValue);
            break;
        case 0x7a:
            quote.inventory = Number(strValue);
            break;
        case 0x7c:
            quote.exchangedate = Number(strValue);
            break;
        case 0x70:
            quote.strbidorder = strValue;
            break;
        case 0x71:
            quote.strbidorder2 = strValue;
            break;
        case 0x72:
            quote.strbidorder3 = strValue;
            break;
        case 0x73:
            quote.strbidorder4 = strValue;
            break;
        case 0x74:
            quote.strbidorder5 = strValue;
            break;
        case 0x75:
            quote.straskorder = strValue;
            break;
        case 0x76:
            quote.straskorder2 = strValue;
            break;
        case 0x77:
            quote.straskorder3 = strValue;
            break;
        case 0x78:
            quote.straskorder4 = strValue;
            break;
        case 0x79:
            quote.straskorder5 = strValue;
            break;
        case 0x7d:
            quote.putoptionpremiums = Number(strValue);
            break;
        case 0x7e:
            quote.putoptionpremiums2 = Number(strValue);
            break;
        case 0x80:
            quote.putoptionpremiums3 = Number(strValue);
            break;
        case 0x81:
            quote.putoptionpremiums4 = Number(strValue);
            break;
        case 0x82:
            quote.putoptionpremiums5 = Number(strValue);
            break;
        case 0x83:
            quote.calloptionpremiums = Number(strValue);
            break;
        case 0x84:
            quote.calloptionpremiums2 = Number(strValue);
            break;
        case 0x85:
            quote.calloptionpremiums3 = Number(strValue);
            break;
        case 0x86:
            quote.calloptionpremiums4 = Number(strValue);
            break;
        case 0x87:
            quote.calloptionpremiums5 = Number(strValue);
            break;
    }
}