/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.ctecinf;

import br.com.ctecinf.model.Crediario;
import br.com.ctecinf.model.Parcela;
import br.com.ctecinf.model.Receita;
import br.com.ctecinf.nfe.Constants;
import br.com.ctecinf.swing.OptionPane;
import br.com.ctecinf.text.DateFormatter;
import br.com.ctecinf.text.MaskFormatter;
import br.com.ctecinf.text.NumberFormatter;
import br.com.ctecinf.text.TimestampFormatter;
import br.com.ctecinf.text.UpperCaseFormatter;
import br.inf.portalfiscal.nfe.v400.autorizacao.TNFe;
import br.inf.portalfiscal.nfe.v400.autorizacao.TNfeProc;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import javax.swing.JOptionPane;

/**
 *
 * @author Cássio Conceição
 * @since 29/05/2019
 * @version 201905
 * @see http://ctecinf.com.br/
 */
public class Daruma {

    /**
     * DEFAULT "/dev/ttyUSB0"
     */
    public static String PORT;

    /**
     * DEFAULT 57
     */
    public static int COLUMNS;

    static {

        Properties prop = new Properties();

        try {

            File file = new File("config", "impressora.properties");

            if (!file.exists()) {

                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }

                prop.setProperty("porta", "/dev/ttyUSB0");
                prop.setProperty("colunas", "57");
                prop.store(new FileOutputStream(file), "Configurações do sistema");

            } else {
                prop.load(new FileInputStream(file));
            }

            PORT = prop.getProperty("porta");
            COLUMNS = Integer.parseInt(prop.getProperty("colunas"));

        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, ex, "Exception", JOptionPane.ERROR_MESSAGE);
        }

    }

    public static final int FONT_SIZE_DEFAULT = 1;
    public static final int FONT_SIZE_MEDIUM = 2;
    public static final int FONT_SIZE_LARGER = 3;
    public static final int FONT_SIZE_EXTRA = 4;

    private static final char[] TAB = {0x09};

    private static final char[] ALIGN_LEFT = {0x1B, 0x6A, 0};
    private static final char[] ALIGN_CENTER = {0x1B, 0x6A, 1};
    private static final char[] ALIGN_RIGHT = {0x1B, 0x6A, 2};

    private static final char[] CHAR_SIZE_0 = {0x1B, 0x21, 0x00};
    private static final char[] CHAR_SIZE_1 = {0x1B, 0x21, 0x01};
    private static final char[] CHAR_SIZE_2 = {0x1B, 0x21, 0x30};
    private static final char[] CHAR_SIZE_3 = {0x1B, 0x21, 0x31};

    private static final char[] START_ITALIC = {0x1B, 0x34, 1};
    private static final char[] END_ITALIC = {0x1B, 0x34, 0};

    private static final char[] START_BOLD = {0x1B, 0x45};
    private static final char[] END_BOLD = {0x1B, 0x46};

    private static final char[] START_TEXT_COND = {0x0F};
    private static final char[] END_TEXT_COND = {0x12};

    private static final char[] TEXT_EXPAND = {0x0E};
    private static final char[] TEXT_NORMAL = {0x14};

    private static final char[] GUILHOTINA = {0x6d};

    private final FileOutputStream outputStream;

    public Daruma() throws IOException {
        outputStream = new FileOutputStream(PORT);
    }

    private static byte[] byteArray(int... value) {

        byte[] arr = new byte[value.length];

        for (int i = 0; i < value.length; i++) {
            arr[i] = (byte) value[i];
        }

        return arr;
    }

    /**
     * QrCode
     *
     * @param str
     * @throws IOException
     */
    public void qrCode(String str) throws IOException {

        int tamanho = str.length() + 3;

        int tamI;
        int tamF;

        if (tamanho > 255) {
            tamI = tamanho % 255;
            tamF = tamanho / 255;
        } else {
            tamI = tamanho;
            tamF = 0;
        }

        outputStream.write(byteArray(27, 129));
        outputStream.write(byteArray((tamI - 1), tamF, 4, 4));
        outputStream.write(str.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * 12 dígitos de 0 a 9
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void ean13Bar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(1));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * 7 dígitos de 0 a 9
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void ean8Bar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * Standard 2 of 5 (Industrial): 0 a 9. Sem dígito de verificação
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void s2of5Bar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(3));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * Interleaved 2 of 5: tamanho sempre par. 0 a 9. Sem dígito de verificação
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void i2of5Bar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(4));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * Tamanho variável. Todos os caracteres ASCII.
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void code128Bar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(5));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * Tamanho variável. 0-9, A-Z, '-', '.', '%', '/', '$', ' ', '+' O caracter
     * '*' de start/stop é inserido automaticamente. Sem dígito de verificação
     * MOD 43
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void code39Bar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(6));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * Tamanho variável. 0-9, A-Z, '-', '.', ' ', '$', '/', '+', '%' O caracter
     * '*' de start/stop é inserido automaticamente.
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void code93Bar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(7));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * 11 dígitos de 0 a 9
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void upcABar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(8));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * Tamanho variável. 0 - 9, '$', '-', ':', '/', '.', '+' Existem 4
     * diferentes caracteres de start/stop: A, B, C, and D que são usados em
     * pares e não podem aparecer em nenhum outro lugar do código. Sem dígito de
     * verificação
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void codaBar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(9));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * Tamanho variável. 0 - 9. 1 dígito de verificação sn ≤ 50
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void msiBar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(10));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * Tamanho variável. 0 a 9 Checksum de dois caracteres.
     *
     * @param code
     * @param size 50..200
     * @param showText Exibir número abaixo
     * @throws IOException
     */
    public void code11Bar(String code, int size, boolean showText) throws IOException {

        outputStream.write(byteArray(27, 98));
        outputStream.write(byteArray(11));
        outputStream.write(byteArray(2));
        outputStream.write(byteArray(size));
        outputStream.write(showText ? byteArray(1) : byteArray(0));
        outputStream.write(code.getBytes());
        outputStream.write(byteArray(0));
    }

    /**
     * Tamanho da Fonte
     *
     * @param size 1..4
     * @throws java.io.IOException
     */
    public void tamanhoFonte(int size) throws IOException {

        switch (size) {
            case 2 ->
                outputStream.write(new String(CHAR_SIZE_1).getBytes());
            case 3 ->
                outputStream.write(new String(CHAR_SIZE_2).getBytes());
            case 4 ->
                outputStream.write(new String(CHAR_SIZE_3).getBytes());
            default ->
                outputStream.write(new String(CHAR_SIZE_0).getBytes());
        }
    }

    /**
     * @param numRows Número de linhas para saltar
     * @throws IOException
     */
    public void saltarLinhas(int numRows) throws IOException {
        for (int i = 0; i < numRows; i++) {
            outputStream.write("\n".getBytes());
        }
    }

    public void alinharEsquerda() throws IOException {
        outputStream.write(new String(ALIGN_LEFT).getBytes());
    }

    public void alinharCentro() throws IOException {
        outputStream.write(new String(ALIGN_CENTER).getBytes());
    }

    public void alinharDireita() throws IOException {
        outputStream.write(new String(ALIGN_RIGHT).getBytes());
    }

    public void tab() throws IOException {
        outputStream.write(new String(TAB).getBytes());
    }

    public void novaLinha() throws IOException {
        saltarLinhas(1);
    }

    public void linhaCorte() throws IOException {
        novaLinha();
        alinharCentro();
        iniciarCond();
        for (int i = 0; i < COLUMNS; i++) {
            texto("-");
        }
        encerrarCond();
        novaLinha();
    }

    public void iniciarCond() throws IOException {
        outputStream.write(new String(START_TEXT_COND).getBytes());
    }

    public void encerrarCond() throws IOException {
        outputStream.write(new String(END_TEXT_COND).getBytes());
    }

    public void texto(String str) throws IOException {
        outputStream.write(str.getBytes());
    }

    public void textoNormal(String str) throws IOException {
        outputStream.write(new String(TEXT_NORMAL).getBytes());
        outputStream.write(str.getBytes());
    }

    public void textoExpand(String str) throws IOException {
        outputStream.write(new String(TEXT_EXPAND).getBytes());
        outputStream.write(str.getBytes());
        outputStream.write(new String(TEXT_NORMAL).getBytes());
    }

    public void negrito(String str) throws IOException {
        outputStream.write(new String(START_BOLD).getBytes());
        outputStream.write(str.getBytes());
        outputStream.write(new String(END_BOLD).getBytes());
    }

    public void italico(String str) throws IOException {
        outputStream.write(new String(START_ITALIC).getBytes());
        outputStream.write(str.getBytes());
        outputStream.write(new String(END_ITALIC).getBytes());
    }

    public void beep() throws IOException {
        outputStream.write(byteArray(7));
    }

    /**
     * Encerra Stream
     *
     * @throws IOException
     */
    public void end() throws IOException {
        outputStream.write(byteArray(0));
        outputStream.write(new String(CHAR_SIZE_1).getBytes());
        outputStream.write(byteArray(7));
        outputStream.write(new String(GUILHOTINA).getBytes());
        outputStream.flush();
        outputStream.close();
    }

    public void leftLine(String str) throws IOException {
        leftLine(str, FONT_SIZE_DEFAULT);
    }

    public void leftLine(String str, int font) throws IOException {

        iniciarCond();
        tamanhoFonte(font);
        alinharEsquerda();

        boolean bold = false;

        if (str == null) {
            texto("");
        } else {

            str = str.replace("null ", " ").replace(" null", "");

            for (String s : str.split(" ")) {

                if (s.startsWith("<b>")) {
                    bold = true;
                }

                if (bold) {

                    if (s.endsWith("</b>")) {
                        bold = false;
                    }

                    negrito(s.replace("<b>", "").replace("</b>", "") + " ");

                } else {

                    if (s.endsWith("</b>")) {
                        bold = false;
                    }

                    texto(s.replace("<b>", "").replace("</b>", "") + " ");
                }
            }
        }

        encerrarCond();
        novaLinha();
    }

    public void centerLine(String str) throws IOException {
        centerLine(str, FONT_SIZE_DEFAULT);
    }

    public void centerLine(String str, int font) throws IOException {

        iniciarCond();
        tamanhoFonte(font);
        alinharCentro();

        boolean bold = false;

        if (str == null) {
            texto("");
        } else {

            str = str.replace("null ", " ");

            for (String s : str.split(" ")) {

                if (s.startsWith("<b>")) {
                    bold = true;
                }

                if (bold) {

                    if (s.endsWith("</b>")) {
                        bold = false;
                    }

                    negrito(s.replace("<b>", "").replace("</b>", "") + " ");

                } else {

                    if (s.endsWith("</b>")) {
                        bold = false;
                    }

                    texto(s.replace("<b>", "").replace("</b>", "") + " ");
                }
            }
        }

        encerrarCond();
        novaLinha();
    }

    public void rightLine(String str) throws IOException {
        rightLine(str, FONT_SIZE_DEFAULT);
    }

    public void rightLine(String str, int font) throws IOException {

        iniciarCond();
        tamanhoFonte(font);
        alinharDireita();

        boolean bold = false;

        if (str == null) {
            texto("");
        } else {

            str = str.replace("null ", " ");

            for (String s : str.split(" ")) {

                if (s.startsWith("<b>")) {
                    bold = true;
                }

                if (bold) {

                    if (s.endsWith("</b>")) {
                        bold = false;
                    }

                    negrito(s.replace("<b>", "").replace("</b>", "") + " ");

                } else {

                    if (s.endsWith("</b>")) {
                        bold = false;
                    }

                    texto(s.replace("<b>", "").replace("</b>", "") + " ");
                }
            }
        }

        encerrarCond();
        novaLinha();
    }

    public static String bold(String str) {
        return "<b>" + str + "</b>";
    }

    /**
     * Imprimi um Recibo de pagamento
     *
     * @throws IOException
     */
    public static void recibo() throws IOException {

        int num = (int) ((double) Math.random() * 99999999);

        String cliente = (String) OptionPane.input("Nome do cliente", null, new UpperCaseFormatter());

        if (cliente == null) {
            return;
        }

        String referente = (String) OptionPane.input("Referente", null, new UpperCaseFormatter());

        if (referente == null) {
            return;
        }

        Number valor = (Number) OptionPane.input("Valor", null, new NumberFormatter(2));

        if (valor == null) {
            return;
        }

        printRecibo(num, cliente, valor.doubleValue(), referente, true);

        if (OptionPane.confirm("Deseja imprimir via do estabelecimento?")) {
            printRecibo(num, cliente, valor.doubleValue(), referente, false);
        }
    }

    /**
     * Imprimi um recibo de pagamento
     *
     * @param numero
     * @param nomeCliente
     * @param valorRecibo
     * @param referente
     * @param viaConsumidor
     * @throws IOException
     */
    public static void printRecibo(int numero, String nomeCliente, double valorRecibo, String referente, boolean viaConsumidor) throws IOException {

        Daruma p = new Daruma();

        p.head();

        p.alinharCentro();
        p.tamanhoFonte(4);
        p.negrito("RECIBO");
        p.novaLinha();

        p.novaLinha();
        p.tamanhoFonte(1);
        p.alinharEsquerda();
        p.tab();

        p.texto("Recebemos de ");
        p.negrito(nomeCliente);
        p.texto(", a quantia de ");
        p.negrito(NumberFormatter.format(2).format(valorRecibo));
        p.texto(", correspondente a: ");
        p.negrito(referente);
        p.texto(" do que passamos o presente recibo.");

        p.novaLinha();
        p.novaLinha();

        p.alinharCentro();
        p.negrito("SEM VALOR FISCAL");
        p.novaLinha();
        p.alinharCentro();
        p.texto("Num.:" + Utils.leftPad2(numero, 6, '0') + " Emissao:" + DateFormatter.format().format(new Date()));
        p.novaLinha();

        p.alinharCentro();
        p.texto("Via");
        p.novaLinha();

        p.alinharCentro();
        p.texto(viaConsumidor ? "Consumidor" : "Estabelecimento");

        p.saltarLinhas(5);

        p.linhaCorte();

        p.alinharCentro();
        p.texto("Autenticacao");

        p.novaLinha();

        p.novaLinha();
        p.alinharCentro();
        p.negrito(Empresa.getSlogan());

        p.saltarLinhas(6);

        p.end();
    }

    /**
     * Imprimi Cabeçalho
     *
     * @throws IOException
     */
    public void head() throws IOException {

        tamanhoFonte(1);
        iniciarCond();
        alinharCentro();
        negrito(Empresa.getRazaoSocial());

        novaLinha();
        alinharCentro();
        texto("CNPJ " + MaskFormatter.format(Empresa.getCnpj(), "##.###.###/####-##") + "    IE " + Empresa.getInscEstadual());

        novaLinha();
        alinharCentro();
        texto(Empresa.getLogradouro() + ", " + Empresa.getNumero() + ", " + Empresa.getComplemento());

        novaLinha();
        alinharCentro();
        texto(Empresa.getBairro() + " - " + Empresa.getMunicipio() + " - " + Empresa.getUf());

        novaLinha();
        alinharCentro();
        texto("CEP " + Empresa.getCep() + "  Fone " + MaskFormatter.format(Empresa.getFone(), "(##) ####-####"));

        iniciarCond();
        novaLinha();
        novaLinha();
    }

    /**
     * Imprimi uma Receita do cliente
     *
     * @param receita
     * @throws java.lang.Exception
     */
    public static void receita(Receita receita) throws Exception {

        if (receita == null) {
            throw new Exception("Receita nula.");
        }

        if (receita.getCliente() == null) {
            throw new Exception("Receita não está vinculada a um cliente.");
        }

        Daruma p = new Daruma();

        p.head();
        p.linhaCorte();

        p.tamanhoFonte(2);
        p.negrito("COPIA RECEITA");
        p.novaLinha();
        p.novaLinha();

        p.leftLine("Cliente: " + receita.getCliente().getNome());
        p.leftLine("CPF: " + receita.getCliente().getCpf());
        p.leftLine("Endereco: " + receita.getCliente().getEndereco());
        p.leftLine("Fone: " + receita.getCliente().getFone() + " Celular: " + receita.getCliente().getCelular());
        p.linhaCorte();
        p.leftLine("Lente: <b>" + receita.getLente() + "</b>");
        p.leftLine("Armacao: " + receita.getArmacao());
        p.linhaCorte();
        p.centerLine("LONGE");
        p.leftLine("          Dir.: " + Utils.leftPad2(receita.getLongeDireitoEsferico(), 6, ' ') + "   " + Utils.leftPad2(receita.getLongeDireitoCilindrico(), 6, ' ') + " X " + receita.getLongeDireitoEixo());
        p.leftLine("          Esq.: " + Utils.leftPad2(receita.getLongeEsquerdoEsferico(), 6, ' ') + "   " + Utils.leftPad2(receita.getLongeEsquerdoCilindrico(), 6, ' ') + " X " + receita.getLongeEsquerdoEixo());

        p.centerLine("PERTO");
        p.leftLine("          Dir.: " + Utils.leftPad2(receita.getPertoDireitoEsferico(), 6, ' ') + "   " + Utils.leftPad2(receita.getPertoDireitoCilindrico(), 6, ' ') + " X " + receita.getPertoDireitoEixo());
        p.leftLine("          Esq.: " + Utils.leftPad2(receita.getPertoEsquerdoEsferico(), 6, ' ') + "   " + Utils.leftPad2(receita.getPertoEsquerdoCilindrico(), 6, ' ') + " X " + receita.getPertoEsquerdoEixo());
        p.linhaCorte();
        p.leftLine("Adicao: <b>" + receita.getAdicao() + "</b>    DNP: <b>" + receita.getDnp() + "</b>   Altura: <b>" + receita.getAltura() + "</b>");
        p.leftLine("Data: " + DateFormatter.format().format(receita.getDataEncomenda()));
        p.leftLine("Medico: " + receita.getMedico());
        p.leftLine("Vendedor: " + receita.getVendedor());
        p.leftLine("<b>OBSERVACAO<b>");
        p.leftLine(receita.getObservacao());

        p.saltarLinhas(5);
    }

    /**
     * Imprimi um Carnet crediario
     *
     * @param crediario
     * @throws java.io.IOException
     */
    public static void carnet(Crediario crediario) throws Exception {

        if (crediario == null) {
            throw new Exception("Crediário nulo.");
        }

        if (crediario.getCliente() == null) {
            throw new Exception("Crediário não está vinculado a um cliente.");
        }

        List<Parcela> parcelas = crediario.listOf(Parcela.class);

        if (parcelas.isEmpty()) {
            throw new Exception("Lista de parcelas vazia.");
        }

        Daruma p = new Daruma();

        p.head();
        p.linhaCorte();

        p.tamanhoFonte(2);
        p.negrito("CARNET");
        p.novaLinha();
        p.novaLinha();

        p.leftLine("Cliente: " + crediario.getCliente());
        p.leftLine("Data da compra: " + DateFormatter.format().format(crediario.getDataVenda()));
        p.leftLine("Valor total: R$ " + bold(NumberFormatter.format(2).format(crediario.getValorTotal())));
        p.leftLine("Numero de Parcelas: " + parcelas.size());
        p.linhaCorte();

        for (Parcela parcela : parcelas) {

            p.leftLine("Parcela: " + parcela.getNumeroParcela());
            p.leftLine("Valor: " + NumberFormatter.format(2).format(parcela.getValor()));
            p.leftLine("Data Venc.: " + DateFormatter.format().format(parcela.getDataVencimento()));
            p.leftLine("Data Pgto.: " + (parcela.getDataPagamento() != null ? DateFormatter.format().format(parcela.getDataPagamento()) : "    /    /"));

            p.alinharCentro();
            p.ean13Bar(Utils.leftPad2(parcela.getId(), 12, '0'), 80, false);
            p.centerLine(Utils.leftPad2(parcela.getId(), 12, '0'));
            p.linhaCorte();
        }

        p.saltarLinhas(5);
    }

    /**
     * Imprimi uma DANFE NFC-e V310
     *
     * @param nfeProc
     * @param cscToken
     * @throws Exception
     */
    public static void danfeNFCe(br.inf.portalfiscal.nfe.v310.autorizacao.TNfeProc nfeProc, String cscToken) throws Exception {

        if (nfeProc.getNFe().getInfNFeSupl() == null || nfeProc.getNFe().getInfNFeSupl().getQrCode() == null) {

            br.inf.portalfiscal.nfe.v310.autorizacao.TNFe.InfNFe inf = nfeProc.getNFe().getInfNFe();

            String cpf = inf.getDest() != null ? inf.getDest().getCPF() : null;
            String dhEmi = inf.getIde().getDhEmi();
            String vTotal = inf.getTotal().getICMSTot().getVNF();
            String chaveAcesso = inf.getId();
            String tpAmb = inf.getIde().getTpAmb();
            String digestValue = new String(nfeProc.getNFe().getSignature().getSignedInfo().getReference().getDigestValue());

            StringBuilder params = new StringBuilder();

            params.append("chNFe=").append(chaveAcesso.replace("NFe", "")).append("&");
            params.append("nVersao=100&");
            params.append("tpAmb=").append(tpAmb).append("&");

            if (cpf != null) {
                params.append("cDest=").append(cpf).append("&");
            }

            params.append("dhEmi=").append(Utils.hexa(dhEmi)).append("&");
            params.append("vNF=").append(vTotal).append("&");
            params.append("vICMS=0.00&");
            params.append("digVal=").append(Utils.hexa(digestValue)).append("&");
            params.append("cIdToken=000001");
            params.append(cscToken);

            String url = "https://www.sefaz.rs.gov.br/NFCE/NFCE-COM.aspx?" + params.toString().replace(cscToken, "") + "&cHashQRCode=" + Utils.sha1(params.toString()).toLowerCase();

            br.inf.portalfiscal.nfe.v310.autorizacao.TNFe.InfNFeSupl infNFeSupl = new br.inf.portalfiscal.nfe.v310.autorizacao.TNFe.InfNFeSupl();
            infNFeSupl.setQrCode(url);
            nfeProc.getNFe().setInfNFeSupl(infNFeSupl);
        }

        Daruma p = new Daruma();

        p.tamanhoFonte(1);
        p.iniciarCond();
        p.alinharCentro();

        p.texto("CNPJ " + MaskFormatter.format(nfeProc.getNFe().getInfNFe().getEmit().getCNPJ(), "##.###.###/####-##") + "  ");
        p.negrito(nfeProc.getNFe().getInfNFe().getEmit().getXNome());

        p.novaLinha();
        p.alinharCentro();
        p.texto(nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getXLgr() + "," + nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getNro() + "," + nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getXCpl() + "," + nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getXBairro() + "," + nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getXMun() + "-" + nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getUF());

        p.novaLinha();
        p.alinharCentro();
        p.texto("Documento Auxiliar Nota Fiscal de Consumidor Eletronica");

        p.novaLinha();

        p.novaLinha();
        p.alinharEsquerda();
        p.negrito(Utils.rightPad2("Codigo", 7, ' ') + Utils.rightPad2("Descricao", 23, ' ') + Utils.rightPad2("Qtde", 5, ' ') + Utils.rightPad2("UN", 6, ' ') + Utils.rightPad2("Vl Unit", 8, ' ') + Utils.leftPad2("Vl Total", 8, ' '));

        for (br.inf.portalfiscal.nfe.v310.autorizacao.TNFe.InfNFe.Det det : nfeProc.getNFe().getInfNFe().getDet()) {
            p.novaLinha();
            p.alinharEsquerda();
            p.texto(Utils.rightPad2(Utils.leftPad2(det.getProd().getCProd(), 6, '0'), 7, ' ') + Utils.rightPad2(Utils.leftPad2(Utils.limitWords(det.getProd().getXProd(), 3), 22, ' '), 23, ' ') + Utils.rightPad2(Utils.leftPad2(det.getProd().getQCom(), 4, ' '), 5, ' ') + Utils.rightPad2(det.getProd().getUCom(), 4, ' ') + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(det.getProd().getVUnCom())), 9, ' ') + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(det.getProd().getVProd())), 9, ' '));
        }

        p.novaLinha();

        p.novaLinha();
        p.alinharEsquerda();
        p.texto("Qtde total de itens" + Utils.leftPad2(nfeProc.getNFe().getInfNFe().getDet().size(), 38, ' '));

        p.novaLinha();
        p.alinharEsquerda();
        p.texto("Valor total R$" + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(nfeProc.getNFe().getInfNFe().getTotal().getICMSTot().getVProd())), 43, ' '));

        if (Double.parseDouble(nfeProc.getNFe().getInfNFe().getTotal().getICMSTot().getVDesc()) > 0) {

            p.novaLinha();
            p.alinharEsquerda();
            p.texto("Desconto R$" + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(nfeProc.getNFe().getInfNFe().getTotal().getICMSTot().getVDesc())), 46, ' '));

            p.novaLinha();
            p.alinharEsquerda();
            p.negrito("Valor a Pagar R$" + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(nfeProc.getNFe().getInfNFe().getTotal().getICMSTot().getVNF())), 41, ' '));
        }

        p.novaLinha();

        p.novaLinha();
        p.alinharEsquerda();
        p.texto("FORMA PAGAMENTO" + Utils.leftPad2("VALOR PAGO R$", 42, ' '));

        for (br.inf.portalfiscal.nfe.v310.autorizacao.TNFe.InfNFe.Pag pag : nfeProc.getNFe().getInfNFe().getPag()) {

            String tPag = switch (pag.getTPag()) {
                case "01" ->
                    "Dinheiro";
                case "02" ->
                    "Cheque";
                case "03" ->
                    "Cartao de Credito";
                case "04" ->
                    "Cartao de Debito";
                case "05" ->
                    "Credito Loja";
                case "10" ->
                    "Vale Alimentacao";
                case "11" ->
                    "Vale Refeicao";
                case "12" ->
                    "Vale Presente";
                case "13" ->
                    "Vale Combustivel";
                case "15" ->
                    "Boleto Bancario";
                case "16" ->
                    "Deposito Bancario";
                case "17" ->
                    "Pagamento Instantaneo (PIX)";
                case "18" ->
                    "Transferencia bancaria, Carteira Digita";
                case "19" ->
                    "Programa de fidelidade, Cashback, Crédito Virtual";
                case "90" ->
                    "Sem Pagamento";
                default ->
                    "Outros";
            };

            p.novaLinha();
            p.alinharEsquerda();
            p.texto(tPag + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(pag.getVPag())), 57 - tPag.length(), ' '));
        }

        p.novaLinha();

        p.novaLinha();
        p.alinharCentro();
        p.negrito("Consulte pela Chave de Acesso em");

        p.novaLinha();
        p.alinharCentro();
        p.texto(Constants.URL_CONSULTA);

        p.novaLinha();
        p.alinharCentro();
        p.texto(MaskFormatter.format(nfeProc.getNFe().getInfNFe().getId().replace("NFe", ""), "#### #### #### #### #### #### #### #### #### #### ####"));

        p.novaLinha();

        if (nfeProc.getNFe().getInfNFe().getDest() != null) {

            String nome = nfeProc.getNFe().getInfNFe().getDest().getXNome();

            if (nfeProc.getNFe().getInfNFe().getDest().getCPF() != null) {

                String cpf = nfeProc.getNFe().getInfNFe().getDest().getCPF();

                p.novaLinha();
                p.alinharCentro();
                p.negrito("CONSUMIDOR - CPF " + MaskFormatter.format(cpf, "###.###.###-##"));

            } else if (nfeProc.getNFe().getInfNFe().getDest().getIdEstrangeiro() != null) {

                String idEstrangeiro = nfeProc.getNFe().getInfNFe().getDest().getCPF();

                p.novaLinha();
                p.alinharCentro();
                p.negrito("CONSUMIDOR - ID ESTRANGEIRO " + idEstrangeiro);
            }

            if (!nome.isEmpty()) {
                p.novaLinha();
                p.alinharCentro();
                p.texto(nome);
            }

        } else {
            p.novaLinha();
            p.alinharCentro();
            p.negrito("CONSUMIDOR NAO IDENTIFICADO");
        }

        p.novaLinha();

        p.novaLinha();
        p.alinharCentro();
        p.negrito("NFC-e " + Utils.leftPad2(nfeProc.getNFe().getInfNFe().getIde().getNNF(), 9, '0') + " Serie " + Utils.leftPad2(nfeProc.getNFe().getInfNFe().getIde().getSerie(), 3, '0') + " " + TimestampFormatter.format().format(Utils.dateNFe2Date(nfeProc.getNFe().getInfNFe().getIde().getDhEmi()).getTime()));

        if (nfeProc.getProtNFe().getInfProt().getNProt() != null && nfeProc.getProtNFe().getInfProt().getDhRecbto() != null && nfeProc.getNFe().getInfNFeSupl().getQrCode() != null) {

            p.novaLinha();
            p.alinharCentro();
            p.negrito("Protocolo de autorizacao: ");
            p.texto(MaskFormatter.format(nfeProc.getProtNFe().getInfProt().getNProt(), "### ########## ##"));

            p.novaLinha();
            p.alinharCentro();
            p.negrito("Data de autorizacao ");
            p.texto(TimestampFormatter.format().format(Utils.dateNFe2Date(nfeProc.getProtNFe().getInfProt().getDhRecbto()).getTime()));

            p.novaLinha();

            p.novaLinha();
            p.alinharCentro();
            p.qrCode(nfeProc.getNFe().getInfNFeSupl().getQrCode());

            p.novaLinha();
            p.alinharCentro();
            p.texto(nfeProc.getNFe().getInfNFe().getInfAdic().getInfCpl());

            p.saltarLinhas(7);
        }

        p.end();
    }

    /**
     * Imprimi uma DANFE NFC-e V400
     *
     * @param nfeProc
     * @throws IOException
     */
    public static void danfeNFCe4(TNfeProc nfeProc) throws IOException {

        Daruma p = new Daruma();

        double troco = Double.parseDouble(nfeProc.getNFe().getInfNFe().getPag().getVTroco());

        p.tamanhoFonte(1);
        p.iniciarCond();
        p.alinharCentro();
        p.negrito(nfeProc.getNFe().getInfNFe().getEmit().getXNome());

        p.novaLinha();
        p.alinharCentro();
        p.texto("CNPJ " + MaskFormatter.format(nfeProc.getNFe().getInfNFe().getEmit().getCNPJ(), "##.###.###/####-##") + "    IE " + nfeProc.getNFe().getInfNFe().getEmit().getIE());

        p.novaLinha();
        p.alinharCentro();
        p.texto(nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getXLgr() + ", " + nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getNro() + ", " + nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getXCpl());

        p.novaLinha();
        p.alinharCentro();
        p.texto(nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getXBairro() + " - " + nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getXMun() + " - " + nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getUF());

        p.novaLinha();
        p.alinharCentro();
        p.texto("CEP " + nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getCEP() + "  Fone " + MaskFormatter.format(nfeProc.getNFe().getInfNFe().getEmit().getEnderEmit().getFone(), "(##) ####-####"));

        p.iniciarCond();
        p.novaLinha();

        p.iniciarCond();
        p.novaLinha();
        p.alinharCentro();
        p.negrito("DANFE NFC-e - Documento Auxiliar");

        p.novaLinha();
        p.alinharCentro();
        p.negrito("da Nota Fiscal de Consumidor Eletronica");

        p.novaLinha();

        p.novaLinha();
        p.alinharEsquerda();
        p.negrito(Utils.rightPad2("Codigo", 7, ' ') + Utils.rightPad2("Descricao", 51, ' '));

        p.alinharDireita();
        p.negrito(Utils.rightPad2("Qtde", 10, ' ') + Utils.rightPad2("UN", 10, ' ') + " x " + Utils.leftPad2("Vl Unit", 17, ' ') + Utils.leftPad2("Vl Total", 16, ' '));

        for (TNFe.InfNFe.Det det : nfeProc.getNFe().getInfNFe().getDet()) {
            p.novaLinha();
            p.alinharEsquerda();
            p.texto(Utils.rightPad2(Utils.leftPad2(det.getProd().getCProd(), 6, '0'), 7, ' ') + Utils.rightPad2(Utils.leftPad2(det.getProd().getXProd(), 50, ' '), 51, ' '));

            p.alinharDireita();
            p.texto(Utils.rightPad2(Utils.leftPad2(det.getProd().getQCom(), 6, ' '), 10, ' ') + Utils.rightPad2(det.getProd().getUCom(), 10, ' ') + " x " + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(det.getProd().getVUnCom())), 17, ' ') + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(det.getProd().getVProd())), 16, ' '));

            if (det.getProd().getVDesc() != null && Double.parseDouble(det.getProd().getVDesc()) > 0) {
                p.novaLinha();
                p.alinharDireita();
                p.texto("Desconto " + NumberFormatter.format(2).format(Double.parseDouble(det.getProd().getVDesc())));
            }
        }

        p.novaLinha();

        p.novaLinha();
        p.alinharEsquerda();
        p.texto("Qtde total de itens" + Utils.leftPad2(nfeProc.getNFe().getInfNFe().getDet().size(), 38, ' '));

        p.novaLinha();
        p.alinharEsquerda();
        p.texto("Valor total R$" + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(nfeProc.getNFe().getInfNFe().getTotal().getICMSTot().getVProd())), 43, ' '));

        if (Double.parseDouble(nfeProc.getNFe().getInfNFe().getTotal().getICMSTot().getVDesc()) > 0) {

            p.novaLinha();
            p.alinharEsquerda();
            p.texto("Desconto R$" + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(nfeProc.getNFe().getInfNFe().getTotal().getICMSTot().getVDesc())), 46, ' '));

            p.novaLinha();
            p.alinharEsquerda();
            p.negrito("Valor a Pagar R$" + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(nfeProc.getNFe().getInfNFe().getTotal().getICMSTot().getVNF())), 41, ' '));
        }

        p.novaLinha();

        p.novaLinha();
        p.alinharEsquerda();
        p.texto("FORMA PAGAMENTO" + Utils.leftPad2("VALOR PAGO R$", 42, ' '));

        for (TNFe.InfNFe.Pag.DetPag det : nfeProc.getNFe().getInfNFe().getPag().getDetPag()) {

            String tPag = switch (det.getTPag()) {
                case "01" ->
                    "Dinheiro";
                case "02" ->
                    "Cheque";
                case "03" ->
                    "Cartao de Credito";
                case "04" ->
                    "Cartao de Debito";
                case "05" ->
                    "Credito Loja";
                case "10" ->
                    "Vale Alimentacao";
                case "11" ->
                    "Vale Refeicao";
                case "12" ->
                    "Vale Presente";
                case "13" ->
                    "Vale Combustivel";
                case "15" ->
                    "Boleto Bancario";
                case "16" ->
                    "Deposito Bancario";
                case "17" ->
                    "Pagamento Instantaneo (PIX)";
                case "18" ->
                    "Transferencia bancaria, Carteira Digita";
                case "19" ->
                    "Programa de fidelidade, Cashback, Crédito Virtual";
                case "90" ->
                    "Sem Pagamento";
                default ->
                    "Outros";
            };

            p.novaLinha();
            p.alinharEsquerda();
            p.texto(tPag + Utils.leftPad2(NumberFormatter.format().format(Double.valueOf(det.getVPag())), 57 - tPag.length(), ' '));
        }

        p.novaLinha();
        p.alinharEsquerda();
        p.texto("Troco R$" + Utils.leftPad2(NumberFormatter.format().format(troco), 49, ' '));

        p.novaLinha();

        p.novaLinha();
        p.alinharCentro();
        p.negrito("Consulte pela Chave de Acesso em");

        p.novaLinha();
        p.alinharCentro();
        p.texto(nfeProc.getNFe().getInfNFeSupl().getUrlChave());

        p.novaLinha();

        p.novaLinha();
        p.alinharCentro();
        p.texto("CHAVE DE ACESSO");

        p.novaLinha();
        p.alinharCentro();
        p.texto(MaskFormatter.format(nfeProc.getNFe().getInfNFe().getId().replace("NFe", ""), "#### #### #### #### #### #### #### #### #### #### ####"));

        p.novaLinha();

        if (nfeProc.getNFe().getInfNFe().getDest() != null) {

            if (nfeProc.getNFe().getInfNFe().getDest().getCPF() != null) {

                String cpf = nfeProc.getNFe().getInfNFe().getDest().getCPF();

                p.novaLinha();
                p.alinharCentro();
                p.negrito("CONSUMIDOR - CPF " + MaskFormatter.format(cpf, "###.###.###-##"));

            } else if (nfeProc.getNFe().getInfNFe().getDest().getIdEstrangeiro() != null) {

                String idEstrangeiro = nfeProc.getNFe().getInfNFe().getDest().getCPF();

                p.novaLinha();
                p.alinharCentro();
                p.negrito("CONSUMIDOR - ID ESTRANGEIRO " + idEstrangeiro);
            }

            if (nfeProc.getNFe().getInfNFe().getDest().getXNome() != null) {

                String nome = nfeProc.getNFe().getInfNFe().getDest().getXNome();

                p.novaLinha();
                p.alinharCentro();
                p.texto(nome);
            }

        } else {
            p.novaLinha();
            p.alinharCentro();
            p.negrito("CONSUMIDOR NAO IDENTIFICADO");
        }

        p.novaLinha();

        p.novaLinha();
        p.alinharCentro();
        p.negrito("NFC-e " + Utils.leftPad2(nfeProc.getNFe().getInfNFe().getIde().getNNF(), 9, '0') + " Serie " + Utils.leftPad2(nfeProc.getNFe().getInfNFe().getIde().getSerie(), 3, '0') + " " + TimestampFormatter.format().format(Utils.dateNFe2Date(nfeProc.getNFe().getInfNFe().getIde().getDhEmi()).getTime()));

        if (nfeProc.getProtNFe().getInfProt().getNProt() != null && nfeProc.getProtNFe().getInfProt().getDhRecbto() != null && nfeProc.getNFe().getInfNFeSupl().getQrCode() != null) {

            p.novaLinha();
            p.alinharCentro();
            p.negrito("Protocolo de autorizacao: ");
            p.texto(MaskFormatter.format(nfeProc.getProtNFe().getInfProt().getNProt(), "### ########## ##"));

            p.novaLinha();
            p.alinharCentro();
            p.negrito("Data de autorizacao ");
            p.texto(TimestampFormatter.format().format(Utils.dateNFe2Date(nfeProc.getProtNFe().getInfProt().getDhRecbto()).getTime()));

            p.novaLinha();

            p.novaLinha();
            p.alinharCentro();
            p.qrCode(nfeProc.getNFe().getInfNFeSupl().getQrCode());

            p.novaLinha();
            p.alinharCentro();
            p.texto("Documento emitido por ME ou EPP optante pelo");
            p.novaLinha();
            p.alinharCentro();
            p.texto("Simples Nacional.");

            p.novaLinha();
            p.alinharCentro();
            p.texto(nfeProc.getNFe().getInfNFe().getInfAdic().getInfCpl().replace("Documento emitido por ME ou EPP optante pelo Simples Nacional.", ""));

            p.novaLinha();

            p.novaLinha();
            p.alinharCentro();
            p.negrito(Empresa.getSlogan());

            p.saltarLinhas(7);
        }

        p.end();
    }
}
