001package jmri.jmrit.operations.trains;
002
003import java.awt.*;
004import java.awt.JobAttributes.SidesType;
005import java.io.*;
006import java.nio.charset.StandardCharsets;
007import java.util.ArrayList;
008import java.util.List;
009
010import javax.swing.ImageIcon;
011import javax.swing.JLabel;
012
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016import jmri.jmrit.operations.setup.Setup;
017import jmri.jmrit.operations.trains.trainbuilder.TrainCommon;
018import jmri.util.davidflanagan.HardcopyWriter;
019
020/**
021 * Used for train Manifests and switch lists.
022 *
023 * @author Daniel Boudreau (C) 2025
024 */
025public class TrainPrintManifest extends TrainCommon {
026
027    static final char SPACE = ' ';
028
029    /**
030     * Print or preview a train Manifest or switch list.
031     *
032     * @param file          File to be printed or previewed
033     * @param name          Title of document
034     * @param isPreview     true if preview
035     * @param fontName      optional font to use when printing document
036     * @param logoURL       optional pathname for logo
037     * @param printerName   optional default printer name
038     * @param orientation   Setup.LANDSCAPE, Setup.PORTRAIT, or Setup.HANDHELD
039     * @param fontSize      font size
040     * @param isPrintHeader when true print page header
041     * @param sidesType     two sides long or short can be null
042     */
043    public static void printReport(File file, String name, boolean isPreview, String fontName, String logoURL,
044            String printerName, String orientation, int fontSize, boolean isPrintHeader, SidesType sidesType) {
045        // obtain a HardcopyWriter to do this
046
047        boolean isLandScape = false;
048        double margin = .5;
049        Dimension pagesize = null; // HardcopyWritter provides default page
050                                   // sizes for portrait and landscape
051        if (orientation.equals(Setup.LANDSCAPE)) {
052            margin = .65;
053            isLandScape = true;
054        }
055        if (orientation.equals(Setup.HANDHELD) || orientation.equals(Setup.HALFPAGE)) {
056            isPrintHeader = false;
057            // add margins to page size
058            pagesize = new Dimension(getPageSize(orientation).width + PAPER_MARGINS.width,
059                    getPageSize(orientation).height + PAPER_MARGINS.height);
060        }
061        try (HardcopyWriter writer = new HardcopyWriter(new Frame(), name, fontSize, margin,
062                margin, .5, .5, isPreview, printerName, isLandScape, isPrintHeader, sidesType, pagesize);
063                BufferedReader in = new BufferedReader(new InputStreamReader(
064                        new FileInputStream(file), StandardCharsets.UTF_8));) {
065
066            // set font
067            if (!fontName.isEmpty()) {
068                writer.setFontName(fontName);
069            }
070
071            if (logoURL != null && !logoURL.equals(Setup.NONE)) {
072                ImageIcon icon = new ImageIcon(logoURL);
073                if (icon.getIconWidth() == -1) {
074                    log.error("Logo not found: {}", logoURL);
075                } else {
076                    writer.write(icon.getImage(), new JLabel(icon));
077                }
078            }
079
080            List<String> lines = new ArrayList<>();
081            String line;
082            while (true) {
083                line = in.readLine();
084                if (line == null) {
085                    if (isPreview) {
086                        // need to do this in case the input file was empty to create preview
087                        writer.write(" ");
088                    }
089                    break;
090                }
091                lines.add(line);
092                if (line.isBlank()) {
093                    print(writer, lines, false);
094                }
095            }
096            print(writer, lines, true);
097        } catch (FileNotFoundException e) {
098            log.error("Build file doesn't exist", e);
099        } catch (HardcopyWriter.PrintCanceledException ex) {
100            log.debug("Print canceled");
101        } catch (IOException e) {
102            log.warn("Exception printing: {}", e.getLocalizedMessage());
103        }
104    }
105
106    private static void print(HardcopyWriter writer, List<String> lines, boolean lastBlock) throws IOException {
107        if (Setup.isPrintNoPageBreaksEnabled() &&
108                writer.getCurrentLineNumber() != 0 &&
109                writer.getLinesPerPage() - writer.getCurrentLineNumber() < lines.size() - 1) {
110            writer.pageBreak();
111        }
112        // check for normal page break
113        if (writer.getCurrentLineNumber() != 0 &&
114                writer.getLinesPerPage() - writer.getCurrentLineNumber() < lines.size()) {
115            // eliminate blank line after normal page break
116            String s = lines.get(lines.size() -1);
117            if (s.isBlank()) {
118                lines.remove(lines.size() - 1);
119            }
120        }
121
122        Color color = null;
123        boolean printingColor = false;
124        for (String line : lines) {
125            // determine if there's a line separator
126            if (printHorizontialLineSeparator(writer, line)) {
127                color = null;
128                continue;
129            }
130            // color text?
131            if (line.contains(TEXT_COLOR_START)) {
132                color = getTextColor(line);
133                if (line.contains(TEXT_COLOR_END)) {
134                    printingColor = false;
135                } else {
136                    // printing multiple lines in color
137                    printingColor = true;
138                }
139                // could be a color change when using two column format
140                if (line.contains(Character.toString(VERTICAL_LINE_CHAR))) {
141                    String s = line.substring(0, line.indexOf(VERTICAL_LINE_CHAR));
142                    s = getTextColorString(s);
143                    writer.write(color, s); // 1st half of line printed
144                    // get the new color and text
145                    line = line.substring(line.indexOf(VERTICAL_LINE_CHAR));
146                    color = getTextColor(line);
147                    // pad out string
148                    line = tabString(getTextColorString(line), s.length());
149                } else {
150                    // simple case only one color
151                    line = getTextColorString(line);
152                }
153            } else if (line.contains(TEXT_COLOR_END)) {
154                printingColor = false;
155                line = getTextColorString(line);
156            } else if (!printingColor) {
157                color = null;
158            }
159
160            printVerticalLineSeparator(writer, line);
161            line = line.replace(VERTICAL_LINE_CHAR, SPACE);
162
163            if (color != null) {
164                writer.write(color, line + NEW_LINE);
165                continue;
166            }
167            writer.write(line);
168            // no line feed if last line of file, eliminates blank page
169            if (!lastBlock ||
170                    writer.getCurrentLineNumber() < writer.getLinesPerPage() - 1) {
171                writer.write(NEW_LINE);
172            }
173        }
174        lines.clear();
175    }
176
177    /*
178     * Returns true if horizontal line was printed, or line length = 0
179     */
180    private static boolean printHorizontialLineSeparator(HardcopyWriter writer, String line) {
181        boolean horizontialLineSeparatorFound = true;
182        if (line.length() > 0) {
183            for (int i = 0; i < line.length(); i++) {
184                if (line.charAt(i) != HORIZONTAL_LINE_CHAR) {
185                    horizontialLineSeparatorFound = false;
186                    break;
187                }
188            }
189            if (horizontialLineSeparatorFound) {
190                writer.write(writer.getCurrentLineNumber(), 0, writer.getCurrentLineNumber(),
191                        line.length() + 1);
192            }
193        }
194        return horizontialLineSeparatorFound;
195    }
196
197    private static void printVerticalLineSeparator(HardcopyWriter writer, String line) {
198        for (int i = 0; i < line.length(); i++) {
199            if (line.charAt(i) == VERTICAL_LINE_CHAR) {
200                // make a frame (two column format)
201                if (Setup.isTabEnabled()) {
202                    writer.write(writer.getCurrentLineNumber(), 0, writer.getCurrentLineNumber() + 1, 0);
203                    writer.write(writer.getCurrentLineNumber(), line.length() + 1,
204                            writer.getCurrentLineNumber() + 1, line.length() + 1);
205                }
206                writer.write(writer.getCurrentLineNumber(), i + 1, writer.getCurrentLineNumber() + 1,
207                        i + 1);
208            }
209        }
210    }
211
212    private final static Logger log = LoggerFactory.getLogger(TrainPrintManifest.class);
213}