package com.printer.demo.utils;

import android.os.Build;
import android.util.Log;
import java.io.UnsupportedEncodingException;

/**
 * 打印机数据分块发送（≤1 KB/块，流控+超时）
 * 返回码：
 *   0 – 成功
 *   2 – 缺纸
 *   3 – 开盖
 *   4 – 过热
 *   5 – 其他异常/离线
 *   6 – 超时
 *
 * 协议：0=缓冲区满（内部等待），1=可以发，其他值直接返回
 */
public class PrinterBlockSender {

    private static final String TAG = "BlockSender";
    private static final int BLOCK_SIZE  = 1024;
    private static final long SLEEP_FULL = 50;
    private int timeout = 30_000;

    private static final byte[] CMD_QUERY = {0x10, 0x05, 0x01};

    // 打印机命令
    private static final byte[] CMD_LINE_FEED = {0x0A}; // 换行
    private static final byte[] CMD_INIT = {0x1B, 0x40}; // 初始化
    private static final byte[] CMD_SET_LINE_SPACING = {0x1B, 0x33, 0x00}; // 设置行间距

    public interface PrinterSendRead {
        int sendBytesData(byte[] data);
        int read(byte[] buffer);
    }

    private final PrinterSendRead printer;

    public PrinterBlockSender(PrinterSendRead printer) {
        this.printer = printer;
    }

    public PrinterBlockSender setTimeout(int timeout) {
        this.timeout = timeout;
        return this;
    }

    /**
     * 根据字符数计算并设置超时时间
     * @param charCount 字符数
     * @return 当前对象（链式调用）
     */
    public PrinterBlockSender setTimeoutByCharCount(int charCount) {
        this.timeout = calculateTimeoutByCharCount(charCount);
        Log.d(TAG, "根据字符数设置超时时间：" + charCount + "字符 -> " + this.timeout + "ms");
        return this;
    }

    /**
     * 根据字符数计算超时时间
     * @param charCount 字符数
     * @return 计算后的超时时间（毫秒）
     */
    public static int calculateTimeoutByCharCount(int charCount) {
        // 基础时间 + 每个字符的额外时间
        int calculatedTimeout = 30_000 + (charCount * 20);

        // 确保在最小和最大范围内
        if (calculatedTimeout < 10_000) {
            calculatedTimeout = 10_000;
        } else if (calculatedTimeout > 120_000) {
            calculatedTimeout = 120_000;
        }

        return calculatedTimeout;
    }

    /**
     * 发送字节数据块（带流控）
     * @param data 要发送的数据
     * @return 发送是否成功
     */
    public boolean sendChunk(byte[] data) {
        return sendChunkWithCode(data) == 0;
    }

    /**
     * 发送字节数据块（带流控）
     * @param data 要发送的数据
     * @return 状态码
     */
    public int sendChunkWithCode(byte[] data) {
        if (data == null || data.length == 0) return 0;

        int offset = 0, left = data.length;
        byte[] block = new byte[BLOCK_SIZE];
        long deadLine = System.currentTimeMillis() + timeout;

        while (left > 0) {
            if (System.currentTimeMillis() > deadLine) return 6;

            int len = Math.min(left, BLOCK_SIZE);
            System.arraycopy(data, offset, block, 0, len);

            int raw;
            do {
                raw = queryRaw();
                if (raw == 1) break;          // 可以发
                if (raw == 0) {               // 缓冲区满
                    try { Thread.sleep(SLEEP_FULL); } catch (InterruptedException ignored) {}
                    continue;
                }
                // 直接返回硬件值（2/3/4），其他归5
                return raw == 2 ? 2 : raw == 3 ? 3 : raw == 4 ? 4 : 5;
            } while (System.currentTimeMillis() < deadLine);

            if (System.currentTimeMillis() > deadLine) return 6;

            byte[] tmp = new byte[len];
            System.arraycopy(block, 0, tmp, 0, len);
            if (printer.sendBytesData(tmp) != len) return 5;
            offset += len;
            left -= len;
        }
        return 0;
    }

    /**
     * 打印文本（带流控）
     * @param text 要打印的文本
     * @param encoding 编码格式，如 "GBK"、"UTF-8"
     * @return 状态码
     */
    public int printText(String text, String encoding) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
            if (text == null || text.isEmpty()) return 0;
        }

        try {
            byte[] data = text.getBytes(encoding);
            return sendChunkWithCode(data);
        } catch (UnsupportedEncodingException e) {
            Log.e(TAG, "不支持的编码格式: " + encoding, e);
            return 5;
        }
    }

    /**
     * 打印文本（使用GBK编码，带流控）
     * @param text 要打印的文本
     * @return 状态码
     */
    public int printText(String text) {
        return printText(text, "GBK");
    }

    /**
     * 打印文本并换行（带流控）
     * @param text 要打印的文本
     * @param encoding 编码格式
     * @return 状态码
     */
    public int printTextLine(String text, String encoding) {
        int code = printText(text, encoding);
        if (code != 0) return code;

        return sendChunkWithCode(CMD_LINE_FEED);
    }

    /**
     * 打印文本并换行（使用GBK编码，带流控）
     * @param text 要打印的文本
     * @return 状态码
     */
    public int printTextLine(String text) {
        return printTextLine(text, "GBK");
    }

    /**
     * 发送换行命令（带流控）
     * @return 状态码
     */
    public int lineFeed() {
        return sendChunkWithCode(CMD_LINE_FEED);
    }

    /**
     * 发送多个换行（带流控）
     * @param count 换行次数
     * @return 状态码
     */
    public int lineFeeds(int count) {
        for (int i = 0; i < count; i++) {
            int code = sendChunkWithCode(CMD_LINE_FEED);
            if (code != 0) return code;
        }
        return 0;
    }

    /**
     * 发送打印机初始化命令（带流控）
     * @return 状态码
     */
    public int initPrinter() {
        return sendChunkWithCode(CMD_INIT);
    }

    /**
     * 设置行间距（带流控）
     * @param spacing 行间距（0-255）
     * @return 状态码
     */
    public int setLineSpacing(int spacing) {
        if (spacing < 0) spacing = 0;
        if (spacing > 255) spacing = 255;

        byte[] cmd = CMD_SET_LINE_SPACING.clone();
        cmd[2] = (byte) spacing;
        return sendChunkWithCode(cmd);
    }

    /* 查原始状态 0/1/其他 */
    private int queryRaw() {
        for (int i = 0; i < 3; i++) {
            if (printer.sendBytesData(CMD_QUERY) <= 0) continue;
            byte[] buf = new byte[64];
            for (int j = 0; j < 10; j++) {
                int read = printer.read(buf);
                if (read > 0) return buf[read - 1] & 0xFF;
                try { Thread.sleep(50); } catch (InterruptedException ignored) {}
            }
        }
        return -1;
    }

    /**
     * 获取当前设置的超时时间
     * @return 当前超时时间（毫秒）
     */
    public int getCurrentTimeout() {
        return timeout;
    }
}