package com.kmall.common.utils.print.ticket;

import com.kmall.common.utils.print.ticket.doc.PrintPageableBuilder;
import com.kmall.common.utils.print.ticket.item.Goods;
import com.kmall.common.utils.print.ticket.item.Ticket;
import org.apache.commons.lang3.StringUtils;

import java.awt.*;
import java.awt.print.*;
import java.util.*;
import java.util.List;

/**
 * 票据构建类
 *
 * @author Scott Chen
 * @since 1.0
 * 2018-10-25
 */
public class TicketBuilder implements Printable {

    // 小票打印要素
    private Ticket ticket;

    /**
     * 向外提供打印文档Pageable
     * @return
     */
    public Pageable getPage() {
        return new PrintPageableBuilder().buildPage(this);
    }


    @Override
    public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
        //此 Graphics2D 类扩展 Graphics 类，以提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制。
        //它是用于在 Java(tm) 平台上呈现二维形状、文本和图像的基础类。
        Graphics2D g2 = (Graphics2D) graphics;
        //设置打印颜色为黑色
        g2.setColor(Color.black);

        //打印起点坐标
        //返回与此 PageFormat相关的 Paper对象的可成像区域左上方点的 x坐标, 根据实际调整加减
        double x = pageFormat.getImageableX() + 3;
        //返回与此 PageFormat相关的 Paper对象的可成像区域左上方点的 y坐标, 根据实际调整加减
        double y = pageFormat.getImageableY();

        float xIdx = (float) x;
        float yIdx = (float) y;



        //-------------------- 小票标题 --------------------

        //Font.PLAIN： 普通样式常量 Font.ITALIC 斜体样式常量 Font.BOLD 粗体样式常量
        //根据指定名称、样式和磅值大小，创建一个新 Font。
        Font fontTit = new Font(
                ticket.getTicketHeadFont().getFamily(),
                ticket.getTicketHeadFont().getWeight(),
                ticket.getTicketHeadFont().getSize()
        );
        //设置标题打印字体
        g2.setFont(fontTit);
        //获取字体的高度
        float fontHeigthTit = fontTit.getSize2D();

        yIdx += (y + fontHeigthTit);
        //yIdx += y;
        g2.drawString(ticket.getTicketHead().getTitle(), xIdx + 50, yIdx);


        //-------------------- 小票订单信息 --------------------

        //设置正文字体
        Font font = new Font(
                ticket.getTicketCommonFont().getFamily(),
                ticket.getTicketCommonFont().getWeight(),
                ticket.getTicketCommonFont().getSize()
        );
        g2.setFont(font);
        float fontHeigth = font.getSize2D();
        float line = fontHeigth + 2;

        xIdx = (float)x;
        yIdx += line + 2;
        //设置操作员
//        g2.drawString("收银员:" + ticket.getTicketHead().getCashierId(), xIdx, yIdx);

        //if (ticketItem.getCardNumber() != null && !"".equals(ticketItem.getCardNumber())) {
        yIdx += line;
        g2.drawString("会员:" + ticket.getTicketHead().getMemberId(), xIdx, yIdx);
//        yIdx += line;
//        g2.drawString("积分:" + ticket.getTicketHead().getIntegral(), xIdx, yIdx);
        //}

        yIdx += line;
        //设置订单号
        g2.drawString("订单号:" + ticket.getTicketHead().getOrderId(), xIdx, yIdx);
        yIdx += line;
        //设置订单号
        g2.drawString("交易时间:" + ticket.getTicketHead().getTradeTime(), xIdx, yIdx);


        yIdx += line - 2;

        /**
         * 虚线设置
         * width 虚线单点宽
         * cap <code>BasicStroke</code>末尾的装饰
         * join 当两条<code>BasicStroke</code>连接时, 连接处的形状
         * miterlimit 当两条<code>BasicStroke</code>连接时, 修剪斜接连接的限制, 大于或等于1.0f
         * dash <code>BasicStroke</code>连接时, 线与缺口各自的单元长度
         * dash_phase 与dash相关, 启动划线模式的偏移量
         *
         */
        g2.setStroke(new BasicStroke(0.4f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 4.0f, new float[]{4.0f, 2.0f, 1.0f, 2.0f}, 0.0f));
        //在此图形上下文的坐标系中使用当前颜色在点 (x1, y1) 和 (x2, y2) 之间画一条线。 即绘制虚线
        g2.drawLine((int) x, (int) yIdx, (int) (xIdx + 200), (int) yIdx);


        //-------------------- 小票商品标题 --------------------

        // 商品名称缩进长
        int gnInd = 4;

        // 商品标题字体
        Font fontGoodsTit = new Font(
                ticket.getTicketGoodsTitleFont().getFamily(),
                ticket.getTicketGoodsTitleFont().getWeight(),
                ticket.getTicketGoodsTitleFont().getSize()
        );
        g2.setFont(fontGoodsTit);
        float fontHeigthGoodsTit = fontGoodsTit.getSize2D();
        float lineGoodsTit = fontHeigthGoodsTit + 2;

        yIdx += lineGoodsTit + 2;
        //设置标题
        g2.drawString("商品名称", xIdx, yIdx);
        g2.drawString("单价", xIdx + 100 + gnInd, yIdx);
        g2.drawString("数量", xIdx + 130 + gnInd, yIdx);
        g2.drawString("小计", xIdx + 160 + gnInd, yIdx);

        //-------------------- 小票商品明细 --------------------

        // 还原回普通字体
        g2.setFont(font);

        //虚线设置
        yIdx += line - 3;
        g2.drawLine((int) x, (int) yIdx, (int) (xIdx + 200), (int) yIdx);

        //设置商品清单
        List<Goods> goodsList = ticket.getGoods();


        if (goodsList != null && goodsList.size() > 0) {
            int i = 0;
            for (Goods gdf : goodsList) {
                i++;

                //---------- 商品名称 ----------
                // 商品名称过长,字符串截分,换行打印
                List<String> gnameL = detachString(gdf.getGname(), 12);
                int gname = gnameL.size();

                if (gname > 0) {
                    for (int j = 0; j < gname; j++) {
                        // 顶部对齐
                        if (ticket.getGoodsAlign().equalsIgnoreCase(Ticket.TOP)) {
                            if (j == 0) {
                                yIdx += line + 3;
                                g2.drawString("#" + gnameL.get(j), xIdx, yIdx);

                                // 只有第一行打印单价, 数量, 小计
                                g2.drawString(gdf.getUprice(), xIdx + 100 + gnInd, yIdx);
                                g2.drawString(gdf.getNum(), xIdx + 130 + gnInd, yIdx);
                                g2.drawString(gdf.getSubtotal() + "元", xIdx + 160 + gnInd, yIdx);
                            } else {
                                yIdx += line;
                                // 第二行只打印剩余的商品名称
                                g2.drawString(String.valueOf(gnameL.get(j)), xIdx + gnInd, yIdx);
                            }
                        } else {
                            // 底部对齐
                            if (j < gname - 1) {
                                if (j == 0) {
                                    yIdx += line + 5;
                                    // 第一行, 只打印商品名称
                                    g2.drawString("#" + gnameL.get(j), xIdx, yIdx);
                                } else {
                                    yIdx += line;
                                    // 除第一行和最后一行外, 只打印商品名称
                                    g2.drawString(String.valueOf(gnameL.get(j)), xIdx + gnInd, yIdx);
                                }


                            } else {
                                yIdx += line;
                                // 最后一行打印剩余的商品名称, 打印单价, 数量, 小计
                                g2.drawString(String.valueOf(gnameL.get(j)), xIdx + gnInd, yIdx);
                                g2.drawString(gdf.getUprice(), xIdx + 100 + gnInd, yIdx);
                                g2.drawString(gdf.getNum(), xIdx + 130 + gnInd, yIdx);
                                g2.drawString(gdf.getSubtotal() + "元", xIdx + 160 + gnInd, yIdx);
                            }
                        }

                    }// end for
                }

            }
        }

        /*if (goodsList != null && goodsList.size() > 0) {
            int i = 0;
            for (Goods gdf : goodsList) {
                i++;
                 yIdx += line;
                // 商品名称过长,用星号代替
                g2.drawString(replaceStar(gdf.getGname(), 12, 0, 0), xIdx, yIdx);
                g2.drawString(gdf.getUprice(), xIdx + 100, yIdx);
                g2.drawString(gdf.getNum(), xIdx + 130, yIdx);
                g2.drawString(gdf.getSubtotal() + "元", xIdx + 160, yIdx);

            }
        }*/

        yIdx += line;
        g2.drawLine((int) x, (int) yIdx, (int) (xIdx + 200), (int) yIdx);


        //-------------------- 小票收银信息 --------------------

        yIdx += line + 2;
        g2.drawString("商品合计:", xIdx, yIdx);
        g2.drawString(ticket.getCashInfo().getGoodsTotal() + "件", xIdx + 130 + gnInd, yIdx);
        g2.drawString(ticket.getCashInfo().getTotal() + "元", xIdx + 160 + gnInd, yIdx);

        yIdx += line;
        g2.drawString("实收:", xIdx, yIdx);
        g2.drawString(ticket.getCashInfo().getReceipts() + "元", xIdx + 160 + gnInd, yIdx);
        yIdx += line;
        g2.drawString("找零:", xIdx, yIdx);
        g2.drawString(ticket.getCashInfo().getOddChange() + "元", xIdx + 160 + gnInd, yIdx);

        yIdx += line;
        g2.drawString("优惠券:", xIdx, yIdx);
        g2.drawString(ticket.getCashInfo().getCoupon() + "元", xIdx + 160 + gnInd, yIdx);
        yIdx += line;
        g2.drawString("运费:", xIdx, yIdx);
        g2.drawString(ticket.getCashInfo().getFreight() + "元", xIdx + 160 + gnInd, yIdx);
        yIdx += line;
        g2.drawString("支付渠道:", xIdx, yIdx);
        g2.drawString(ticket.getCashInfo().getPaymentMode(), xIdx + 160 + gnInd, yIdx);

        yIdx += line - 1;
        g2.drawLine((int) x, (int) yIdx, (int) (xIdx + 200), (int) yIdx);

        //-------------------- 小票清单信息 --------------------

        yIdx += line + 2;
        g2.drawString("订单号:" + ticket.getCusListing().getOrderId(), xIdx, yIdx);
        yIdx += line;
        g2.drawString("运单编号:" + ticket.getCusListing().getWaybillId(), xIdx, yIdx);
        yIdx += line;
        g2.drawString("海关清单号:" + ticket.getCusListing().getInvtNo(), xIdx, yIdx);

        yIdx += line;
        g2.drawString("收货人:" + ticket.getCusListing().getConsignee(), xIdx, yIdx);
        yIdx += line;
        g2.drawString("收货人电话:" + ticket.getCusListing().getConsigneeTel(), xIdx, yIdx);
        yIdx += line;
        g2.drawString("始发地:" + ticket.getCusListing().getOriginAddress(), xIdx, yIdx);
        yIdx += line;
        List<String> addressList = detachString(ticket.getCusListing().getDeliveryAddress(), 20);
        for (int i = 0; i < addressList.size(); i++) {
            if (i == 0) {
                g2.drawString("交货地:" + addressList.get(i), xIdx, yIdx);
            } else {
                yIdx += line;
                g2.drawString(addressList.get(i), xIdx + 30, yIdx);
            }
        }

        //-------------------- 小票其它信息 --------------------

        yIdx += line - 1;
        g2.drawLine((int) x, (int) yIdx, (int) (xIdx + 200), (int) yIdx);

        if (!StringUtils.isBlank(ticket.getTicketFoot().getSummary())) {
            yIdx += line + 1;
            g2.drawString(ticket.getTicketFoot().getSummary(), xIdx, yIdx);
        }
        if (!StringUtils.isBlank(ticket.getTicketFoot().getServiceTel())) {
            yIdx += line;
            g2.drawString("客服电话:" + ticket.getTicketFoot().getServiceTel(), xIdx, yIdx);
        }
        if (!StringUtils.isBlank(ticket.getTicketFoot().getUrl1())) {
            yIdx += line;
            g2.drawString("网址:" + ticket.getTicketFoot().getUrl1(), xIdx, yIdx);
        }
        if (!StringUtils.isBlank(ticket.getTicketFoot().getUrl2())) {
            yIdx += line;
            g2.drawString("网址:" + ticket.getTicketFoot().getUrl2(), xIdx, yIdx);
        }
        if (!StringUtils.isBlank(ticket.getTicketFoot().getUrl3())) {
            yIdx += line;
            g2.drawString("网址:" + ticket.getTicketFoot().getUrl3(), xIdx, yIdx);
        }
        yIdx += line;
        g2.drawString(ticket.getTicketFoot().getWelcome(), xIdx, yIdx);

        switch (pageIndex) {
            case 0:
                return PAGE_EXISTS; //0
            default:
                return NO_SUCH_PAGE;  //1
        }
    }

    /**
     * 分拆长字符串
     *
     * @param str 原字符
     * @param max 分拆最大长度
     * @return
     */
    public List<String> detachString(String str, int max) {
        if (StringUtils.isBlank(str)) {
            return Collections.EMPTY_LIST;
        }
        int len = str.length();

        List<String> list = new ArrayList<>();

        if (len <= max) {
            list.add(str);
            return list;
        }

        String tmp;

        while (true) {
            if (len > max) {
                tmp = str.substring(0, max);
                list.add(tmp);

                // 截去tmp字符串
                str = str.substring(tmp.length(), len);
                len = str.length();
            } else {
                list.add(str);
                break;
            }
        }
        return list;
    }


    /**
     * 名称过长, 星号替换
     * 替换为 ABCD****XYZ的样式
     *
     * @param str 原字符串
     * @param max 最长数目
     * @param beg 保留开始数目
     * @param end 保留结束数目
     * @return
     */
    public String replaceStar(String str, int max, int beg, int end) {
        if (StringUtils.isBlank(str)) {
            return str;
        }
        int len = str.length();

        if (beg == 0 || beg < 0) {
            beg = 10;
        }
        if (end == 0 || end < 0) {
            end = 8;
        }

        if (len <= max) {
            return str;
        } else {
            int ibeg = beg;
            int istar;
            int iend = end;
            do{
                ibeg--;
                iend--;
                istar = max - ibeg - iend;
                // 至少保证4个星号
            }while (istar < 4);

            String asterisk = "";
            for (int i = 0; i < istar; i++) {
                asterisk += "*";
            }
            return str.substring(0, ibeg) + asterisk + str.substring(len - iend, len);
        }
    }

    public void setTicket(Ticket ticket) {
        this.ticket = ticket;
    }

}
