package net.northking.cctp.element.internal.protocol.socket.cups8583.internal;

import net.northking.cctp.element.core.APIFactory;
import net.northking.cctp.element.core.exception.ComponentRuntimeException;
import net.northking.cctp.element.internal.protocol.socket.cups8583.CupsUtils;
import net.northking.cctp.element.log.Logger;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

import java.nio.charset.Charset;
import java.util.Map;
import java.util.TreeMap;

/**
 * 报文域编码类
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
public class CupsMsgBodyEncoder {

    public final static String bitMap128 = "10000000" + "00000000" + "00000000" + "00000000"
            + "00000000" + "00000000" + "00000000" + "00000000"
            + "00000000" + "00000000" + "00000000" + "00000000"
            + "00000000" + "00000000" + "00000000" + "00000000";
    /**
     * 组件日志
     */
    private Logger logger = APIFactory.getLogger();
    /**
     * 字符集编码
     */
    private Charset charset;
    /**
     * 域定义
     */
    private Map<String, CupsField> defMap;

    public CupsMsgBodyEncoder(Map<String, CupsField> defMap, Charset charset) {
        this.defMap = defMap;
        this.charset = charset;
    }

    /**
     * <p>域字段编码</p>
     * key形如2、3...128
     */
    public byte[] encode(TreeMap<String, ValueObject> valueMap) {
        byte[] allFieldBytes = new byte[0];
        boolean useExtBitMap = false;
        StringBuffer bitMap = new StringBuffer(bitMap128);

        // 域字段组装
        for (Map.Entry<String, ValueObject> field : valueMap.entrySet()) {
            CupsField f = this.defMap.get(field.getKey());
            if (f == null) {
                throw new ComponentRuntimeException("位元" + field.getKey() + "未定义");
            }
            byte[] bytes = toFieldBytes(f, field.getValue());
            allFieldBytes = ArrayUtils.addAll(allFieldBytes, bytes);
            if (f.isExtBitMapField()) {
                useExtBitMap = true;
            }
            bitMap.replace(f.getTopIndex() - 1, f.getTopIndex(), "1");
        }

        // 位图处理
        if (!useExtBitMap) {
            bitMap.replace(0, 1, "0").delete(64, bitMap.length());
        }
        logger.debug("--> 请求位图Hex:{}, 二进制:{}", CupsUtils.bytes2HexString(CupsUtils.binaryStr2Bytes(bitMap.toString())), bitMap.toString());
        logger.debug("--> 请求字段域Hex:{}", CupsUtils.bytes2HexString(allFieldBytes));
        return ArrayUtils.addAll(CupsUtils.binaryStr2Bytes(bitMap.toString()), allFieldBytes);
    }

    /**
     * 根据域定义将值转换为规定字节数组
     */
    private byte[] toFieldBytes(CupsField f, ValueObject valueObject) {
        if (valueObject.getValue() == null) {
            throw new ComponentRuntimeException(String.format("域%s值不能为空", f.getCode()));
        }
        switch (f.fieldType) {
            case FIX: {
                return toFixBytes(f, valueObject);
            }
            case MMDD:
            case YYMM:
            case hhmmss:
            case YYMMDD:
            case MMDDhhmmss:
            case YYMMDDhhmmss: {
                return toDateTimeBytes(f, valueObject);
            }
            case LLVAR: {
                return toVarLengthBytes(f, 2, valueObject);
            }
            case LLLVAR: {
                return toVarLengthBytes(f, 3, valueObject);
            }
            default:
                throw new ComponentRuntimeException("未知的字段类型：" + f.fieldType);
        }
    }

    /**
     * 定长字段处理
     */
    private byte[] toFixBytes(CupsField f, ValueObject valueObject) {
        String value = valueObject.getValue();
        if (valueObject.isHex()) {
            value = new String(CupsUtils.hexString2Bytes(value), this.charset);
        }
        byte[] valueBytes = value.getBytes(this.charset);
        int len = valueBytes.length;
        if (len > f.length) {
            throw new ComponentRuntimeException(String.format("域%s：数据类型=%s，定长=%d，已超长",
                    f.getCode(), f.dataType.getDesc(), f.length));
        }
        switch (f.dataType) {
            case n: {
                if (!CupsUtils.isNumberStr(value)) {
                    throw new ComponentRuntimeException(String.format("域%s：数据类型=%s，定长=%d",
                            f.getCode(), f.dataType.getDesc(), f.length));
                }
                value = CupsUtils.leftPad(value, f.length - len, "0");
                return value.getBytes();
            }
            case b: {
                if (!CupsUtils.isBinaryStr(value)) {
                    throw new ComponentRuntimeException(String.format("域%s：数据类型=%s，定长=%d",
                            f.getCode(), f.dataType.getDesc(), f.length));
                }
                value = CupsUtils.leftPad(value, f.length - len, "0");
                return CupsUtils.binaryStr2Bytes(value);
            }
            default: {
                value = CupsUtils.rightPad(value, f.length - len, " ");
                return value.getBytes(this.charset);
            }
        }
    }

    /**
     * 日期时间字段处理
     */
    private byte[] toDateTimeBytes(CupsField f, ValueObject valueObject) {
        String value = valueObject.getValue();
        if (valueObject.isHex()) {
            value = new String(CupsUtils.hexString2Bytes(value));
        }
        if (value.getBytes().length > f.length) {
            throw new ComponentRuntimeException(String.format("域%s：数据类型=%s，定长=%d，已超长",
                    f.getCode(), f.dataType.getDesc(), f.length));
        }
        value = StringUtils.leftPad(value, f.length, "0");
        try {
            DateUtils.parseDate(value, f.fieldType.name());
        } catch (Exception e) {
            throw new ComponentRuntimeException(String.format("域%s：日期格式=%s", f.getCode(), f.fieldType.name()));
        }
        return value.getBytes();
    }

    /**
     * 变长字段处理
     */
    private byte[] toVarLengthBytes(CupsField f, int size, ValueObject valueObject) {
        byte[] bytes;
        String value = valueObject.getValue();
        if (valueObject.isHex()) {
            bytes = CupsUtils.hexString2Bytes(value);
        } else {
            bytes = value.getBytes(this.charset);
        }
        if (bytes.length > f.length) {
            throw new ComponentRuntimeException(String.format("域%s：数据类型=%s，变长最大值=%d，已超长",
                    f.getCode(), f.dataType.getDesc(), f.length));
        }
        String num = StringUtils.leftPad(bytes.length + "", size, "0");
        return ArrayUtils.addAll(num.getBytes(), bytes);
    }

}
