package org.ts.ddcs.itemdatatemplate;


import lombok.Data;
import org.ts.ddcs.common.metadata.ElementDataMetadataEnum;
import org.ts.ddcs.common.util.BytesHelp;
import org.ts.ddcs.common.util.ValueLen;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.*;

@Data
public abstract class DataItemTemplate {
    protected String feture;
    protected String name;
    protected String desc;
    protected String unit;
    protected boolean macth = false;
    protected String varcharValue;
    protected byte[] valueBuff = new byte[0];
    protected List<BigDecimal> bigDecimalValues = new ArrayList<>();
    protected List<Integer> intValues = new ArrayList<>();
    protected Map<String, Object> datas = new HashMap<>();
    protected List<Map<String, Object>> dataList = new ArrayList<>();
    protected boolean outlierData = false;
    protected ValueLen valueLen;
    protected List<Map<String, Object>> valueList = new ArrayList<>();

    public void clear() {
        this.varcharValue = "";
        this.valueBuff = new byte[0];
        this.bigDecimalValues = new ArrayList<>();
        this.intValues = new ArrayList<>();
        this.datas.clear();
    }

    public abstract int stream(byte[] data, int index) throws UnsupportedEncodingException;

    public abstract int stream(byte[] data, int index, int batchSize);

    public abstract String getStringValue();

    public abstract void saveValue(Map<String, List<Map<String, Object>>> keyStore);

    public String toStringValue() {
        StringBuilder builder = new StringBuilder();
        for (Map<String, Object> item : this.valueList) {
            if (builder.length() > 0) {
                builder.append(",");
            }
            builder.append(item.get(ElementDataMetadataEnum.ELEMENT_METADATA_VALUE.getName()));
        }
        return builder.toString();
    }

    private boolean isErrorData(byte[] bytes) {
        boolean isError = true;
        for (int i = 0; i < bytes.length; i++) {
            byte[] byteBuff = BytesHelp.subBytes(bytes, i, 1);
            String hexStr = BytesHelp.byte2HexStr(byteBuff);
            if (!"FF".equals(hexStr)) {
                isError = false;
            }
        }
        return isError;
    }

    protected BigDecimal readBigDecimalValue(byte[] data, int pos, ValueLen len) {
        byte[] buff = BytesHelp.subBytes(data, pos, len.getByteLen());
        if (!isErrorData(buff)) {
            String hexStr = BytesHelp.byte2HexStr(buff);
            BigDecimal value = BytesHelp.toDecimal(hexStr, len.getDecimalLen());
            this.bigDecimalValues.add(value);
            return value;
        }
        return null;
    }

    protected BigDecimal readBigDecimalValue(byte[] data, int pos, int byteLen, int decimalLen) {
        byte[] buff = BytesHelp.subBytes(data, pos, byteLen);
        if (!isErrorData(buff)) {
            String hexStr = BytesHelp.byte2HexStr(buff);
            BigDecimal value = BytesHelp.toDecimal(hexStr, decimalLen);
            this.bigDecimalValues.add(value);
        }
        return null;
    }

    protected void readIntValue(byte[] data, int pos, ValueLen len) {
        byte[] buff = BytesHelp.subBytes(data, pos, len.getByteLen());
        if (!isErrorData(buff)) {
            String hexStr = BytesHelp.byte2HexStr(buff);
            this.intValues.add(BytesHelp.toInt(hexStr));
        }
    }

    protected void readVarcharValue(byte[] data, int pos, ValueLen len) {
        byte[] buff = BytesHelp.subBytes(data, pos, len.getByteLen());
        this.varcharValue = BytesHelp.byte2HexStr(buff);
    }

    protected void saveBigDecimalValues(Map<String, List<Map<String, Object>>> keyStore, int scale) {
        if (keyStore.containsKey(this.feture)) {
            List<Map<String, Object>> values = keyStore.get(this.feture);
            for (BigDecimal decimal : this.bigDecimalValues) {
                Map<String, Object> value = new HashMap<>();
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_CODE.getName(), this.feture);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_NAME.getName(), this.name);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_UNIT.getName(), this.unit);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_DESC.getName(), this.desc);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_VALUE.getName(), decimal.setScale(scale, BigDecimal.ROUND_DOWN).toPlainString());
                values.add(value);
            }
        } else {
            List<Map<String, Object>> values = new LinkedList<>();
            for (BigDecimal decimal : this.bigDecimalValues) {
                Map<String, Object> value = new HashMap<>();
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_CODE.getName(), this.feture);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_NAME.getName(), this.name);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_UNIT.getName(), this.unit);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_DESC.getName(), this.desc);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_VALUE.getName(), decimal.setScale(scale, BigDecimal.ROUND_DOWN).toPlainString());
                values.add(value);
            }
            keyStore.put(this.feture, values);
        }
    }

    protected void saveIntValues(Map<String, List<Map<String, Object>>> keyStore) {
        if (keyStore.containsKey(this.feture)) {
            List<Map<String, Object>> values = keyStore.get(this.feture);
            for (Integer v : this.intValues) {
                Map<String, Object> value = new HashMap<>();
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_CODE.getName(), this.feture);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_NAME.getName(), this.name);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_UNIT.getName(), this.unit);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_DESC.getName(), this.desc);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_VALUE.getName(), v.toString());
                values.add(value);
            }
        } else {
            List<Map<String, Object>> values = new LinkedList<>();
            for (Integer v : this.intValues) {
                Map<String, Object> value = new HashMap<>();
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_CODE.getName(), this.feture);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_NAME.getName(), this.name);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_UNIT.getName(), this.unit);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_DESC.getName(), this.desc);
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_VALUE.getName(), v.toString());
                values.add(value);
            }
            keyStore.put(this.feture, values);
        }
    }

    protected void saveVarcharValues(Map<String, List<Map<String, Object>>> keyStore) {
        if (keyStore.containsKey(this.feture)) {
            List<Map<String, Object>> values = keyStore.get(this.feture);
            Map<String, Object> value = new HashMap<>();
            value.put(ElementDataMetadataEnum.ELEMENT_METADATA_CODE.getName(), this.feture);
            value.put(ElementDataMetadataEnum.ELEMENT_METADATA_NAME.getName(), this.name);
            value.put(ElementDataMetadataEnum.ELEMENT_METADATA_UNIT.getName(), this.unit);
            value.put(ElementDataMetadataEnum.ELEMENT_METADATA_DESC.getName(), this.desc);
            if (null != this.varcharValue) {
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_VALUE.getName(), this.varcharValue);
            }
            values.add(value);
        } else {
            List<Map<String, Object>> values = new LinkedList<>();
            Map<String, Object> value = new HashMap<>();
            value.put(ElementDataMetadataEnum.ELEMENT_METADATA_CODE.getName(), this.feture);
            value.put(ElementDataMetadataEnum.ELEMENT_METADATA_NAME.getName(), this.name);
            value.put(ElementDataMetadataEnum.ELEMENT_METADATA_UNIT.getName(), this.unit);
            value.put(ElementDataMetadataEnum.ELEMENT_METADATA_DESC.getName(), this.desc);
            if (null != this.varcharValue) {
                value.put(ElementDataMetadataEnum.ELEMENT_METADATA_VALUE.getName(), this.varcharValue);
            }
            values.add(value);
            keyStore.put(this.feture, values);
        }
    }
}
