/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * DirectionDialog.java
 *
 * Created on 2010/04/19, 14:48:12
 */
package open.dolphin.client;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterJob;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintException;
import javax.print.PrintService;
import javax.print.SimpleDoc;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.JobName;
import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.OrientationRequested;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import open.dolphin.project.GlobalSettings;
import open.dolphin.infomodel.ClaimBundle;
import open.dolphin.infomodel.ClaimItem;
import open.dolphin.utils.CombinedStringParser;
import open.dolphin.infomodel.DocumentModel;
import open.dolphin.infomodel.KarteBean;
import open.dolphin.infomodel.ModuleModel;
import open.dolphin.infomodel.PatientModel;
import open.dolphin.infomodel.UserModel;
import open.dolphin.log.LogWriter;
import open.dolphin.project.GlobalConstants;
import open.dolphin.project.GlobalVariables;
import org.jdesktop.application.ApplicationContext;

/**
 *　指示箋画面　MEMO:画面
 * @author
 */
public class DirectionDialog extends javax.swing.JDialog {

    private DocumentModel documentModel;
    private DirectionTableModel tableModel;
    private String[] directionsList;
    //  private GlobalVariablesImplement grobalVariables;

    /** Creates new form DirectionDialog
     * @param parent
     * @param modal
     * @param model
     */
    public DirectionDialog(java.awt.Frame parent, boolean modal, DocumentModel model) {
        super(parent, modal);

        directionsList = new String[8];
        directionsList[0] = "処方";
        directionsList[1] = "注射";
        directionsList[2] = "処置";
        directionsList[3] = "手術";
        directionsList[4] = "検査";
        directionsList[5] = "画像診断";
        directionsList[6] = "その他";
        directionsList[7] = "なし";

        this.documentModel = model;
        initComponents();
        initCustomComponents();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        scrollPane = new javax.swing.JScrollPane();
        table = new javax.swing.JTable();
        buttonPanel = new javax.swing.JPanel();
        printButton = new javax.swing.JButton();
        cancelButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("指示箋印刷");

        scrollPane.setName("scrollPane"); // NOI18N

        table.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null},
                {null, null},
                {null, null},
                {null, null}
            },
            new String [] {
                "指示", "送付先"
            }
        ) {
            boolean[] canEdit = new boolean [] {
                false, true
            };

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        table.setName("table"); // NOI18N
        scrollPane.setViewportView(table);

        buttonPanel.setName("buttonPanel"); // NOI18N

        printButton.setText("送る");
        printButton.setName("printButton"); // NOI18N
        printButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                printButtonActionPerformed(evt);
            }
        });

        cancelButton.setText("閉じる");
        cancelButton.setName("cancelButton"); // NOI18N
        cancelButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cancelButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout buttonPanelLayout = new javax.swing.GroupLayout(buttonPanel);
        buttonPanel.setLayout(buttonPanelLayout);
        buttonPanelLayout.setHorizontalGroup(
            buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, buttonPanelLayout.createSequentialGroup()
                .addContainerGap(308, Short.MAX_VALUE)
                .addComponent(printButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(cancelButton)
                .addGap(7, 7, 7))
        );
        buttonPanelLayout.setVerticalGroup(
            buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(buttonPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(printButton)
                    .addComponent(cancelButton))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(buttonPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 436, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 181, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(buttonPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    /**
     *
     * @param assignment
     * @param description
     * @param clusterIndex
     */
    private void PrintTask(final String assignment, final String description, final int clusterIndex) {
        ApplicationContext context = GlobalConstants.getApplicationContext();
        org.jdesktop.application.Task task = new org.jdesktop.application.Task<Void, Void>(context.getApplication()) {

            /**
             *
             */
            @Override
            protected Void doInBackground() throws Exception {
                PrintService service = tableModel.findPrinter(assignment, clusterIndex);
                List<ModuleModel> modules = tableModel.getModules(assignment);
                if (!modules.isEmpty()) {
                    if (service != null) {
                        PrintDirection direction = new PrintDirection(service.createPrintJob(), assignment, modules, description, 60, 60);
                    }
                }
                return null;
            }

            /**
             *
             */
            @Override
            protected void succeeded(Void result) {
            }

            @Override
            protected void cancelled() {
            }

            @Override
            protected void failed(java.lang.Throwable cause) {
            }

            @Override
            protected void interrupted(java.lang.InterruptedException e) {
            }
        };
        context.getTaskService().execute(task);
    }

    /**
     *
     * @param assignment
     * @param description
     * @param clusterIndex
     */
    private void printDocument(String assignment, String description, int clusterIndex) {
        PrintTask(assignment, description, clusterIndex);
    }

    /**
     *
     */
    private void printAll() {
        for (int index = 0; index < directionsList.length; index++) {
            printDocument(directionsList[index], "", 0);
            printDocument(directionsList[index], "（控え）", 1);
        }
    }

    /**
     *
     * @param evt
     */
    private void printButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_printButtonActionPerformed
        printAll();
        this.setVisible(false);
        this.dispose();
    }//GEN-LAST:event_printButtonActionPerformed
    /**
     *
     * @param evt
     */
    private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
        this.setVisible(false);
        this.dispose();
    }//GEN-LAST:event_cancelButtonActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel buttonPanel;
    private javax.swing.JButton cancelButton;
    private javax.swing.JButton printButton;
    private javax.swing.JScrollPane scrollPane;
    private javax.swing.JTable table;
    // End of variables declaration//GEN-END:variables

    /**
     *
     */
    private void initCustomComponents() {

        scrollPane.getViewport().setBackground(GlobalSettings.getColors(GlobalSettings.Parts.TABLE_BACKGROUND));

        tableModel = new DirectionTableModel(documentModel.getModules());
        table.setModel(tableModel);

        JComboBox numberCombo = new JComboBox(directionsList);
        TableColumn numberColumn = table.getColumnModel().getColumn(1);
        numberColumn.setCellEditor(new DefaultCellEditor(numberCombo));
    }

    /**
     *　指示箋テーブルモデル　MEMO:モデル
     */
    private class DirectionTableModel extends AbstractTableModel {

        private List<ModuleModel> modules;
        private List<String> assignment;
        private List<CombinedStringParser> printerClusters;

        public DirectionTableModel(Collection<ModuleModel> bundle) {
            printerClusters = new ArrayList<CombinedStringParser>();
            printerClusters.add(GlobalVariables.getDirections());  //Main
            printerClusters.add(GlobalVariables.getCcDirections());//Cc
            modules = new ArrayList<ModuleModel>();
            assignment = new ArrayList<String>();
            initModel(bundle);
        }

        /**
         *
         * @param bundle
         */
        private void initModel(Collection<ModuleModel> bundle) {
            for (ModuleModel stamp : bundle) {
                if (stamp.getModuleInfo().getEntity().equals("medOrder")) {
                    addModelItems(directionsList[0], stamp);//処方
                    continue;
                }
                if (stamp.getModuleInfo().getEntity().equals("injectionOrder")) {
                    addModelItems(directionsList[1], stamp); //注射
                    continue;
                }
                if (stamp.getModuleInfo().getEntity().equals("treatmentOrder")) {
                    addModelItems(directionsList[2], stamp);//処置
                    continue;
                }
                if (stamp.getModuleInfo().getEntity().equals("surgeryOrder")) {
                    addModelItems(directionsList[3], stamp);//手術
                    continue;
                }
                if (stamp.getModuleInfo().getEntity().equals("testOrder")) {
                    addModelItems(directionsList[4], stamp);//検体検査
                    continue;
                }
                if (stamp.getModuleInfo().getEntity().equals("physiologyOrder")) {
                    addModelItems(directionsList[4], stamp);//検体検査
                    continue;
                }
                if (stamp.getModuleInfo().getEntity().equals("radiologyOrder")) {
                    addModelItems(directionsList[5], stamp);//画像診断
                    continue;
                }
                if (stamp.getModuleInfo().getEntity().equals("otherOrder")) {
                    addModelItems(directionsList[6], stamp);//その他
                    continue;
                }
            }
        }

        /**
         *
         * @param assignment_name
         * @param clusterIndex
         * @return
         */
        private PrintService findPrinter(String assignment_name, int clusterIndex) {
            for (int assignment_index = 0; assignment_index < directionsList.length; assignment_index++) {
                if (directionsList[assignment_index].equals(assignment_name)) {
                    return findPrinterByName(printerClusters.get(clusterIndex).get(assignment_index));
                }
            }
            return null;
        }

        /**
         *
         * @param printer_name
         * @return
         */
        private PrintService findPrinterByName(String printer_name) {
            PrintService[] services = PrinterJob.lookupPrintServices();
            for (int printers_index = 0; printers_index < services.length; printers_index++) {
                if (services[printers_index].getName().equals(printer_name)) {
                    return services[printers_index];
                }
            }
            return null;
        }

        /**
         *
         * @param name
         * @param model
         */
        private void addModelItems(String name, ModuleModel model) {
            modules.add(model);
            assignment.add(name);
        }

        /**
         *
         * @param name
         * @return
         */
        private List<ModuleModel> getModules(String name) {
            List<ModuleModel> result = new ArrayList<ModuleModel>();
            for (int index = 0; index < assignment.size(); index++) {
                if (assignment.get(index).equals(name)) {
                    result.add(modules.get(index));
                }
            }
            return result;
        }

        /**
         *
         * @param index
         * @return
         */
        public ModuleModel getStamp(int index) {
            return modules.get(index);
        }

        /**
         *
         * @param index
         * @return
         */
        public String getAssignment(int index) {
            return assignment.get(index);
        }

        /**
         *
         * @return
         */
        @Override
        public int getRowCount() {
            return modules.size();
        }

        /**
         *
         * @return
         */
        @Override
        public int getColumnCount() {
            return 2;
        }

        /**
         *
         * @param rowIndex
         * @param columnIndex
         * @return
         */
        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return (columnIndex == 1);
        }

        /**
         *
         * @param columnIndex
         * @return
         */
        @Override
        public String getColumnName(int columnIndex) {
            String result = "";
            switch (columnIndex) {
                case 0:
                    result = "指示";
                    break;
                case 1:
                    result = "送り先";
                    break;
                default:LogWriter.error(getClass(), "case default");
            }
            return result;
        }

        /**
         *
         * @param rowIndex
         * @param columnIndex
         * @return
         */
        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            Object result = "";
            switch (columnIndex) {
                case 0:
                    result = ((ModuleModel) modules.toArray()[rowIndex]).getModuleInfo().getStampName();
                    break;
                case 1:
                    result = assignment.get(rowIndex);
                    break;
                default:LogWriter.error(getClass(), "case default");
            }
            return result;
        }

        /**
         *
         * @param aValue
         * @param rowIndex
         * @param columnIndex
         */
        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            assignment.set(rowIndex, (String) aValue);
        }
    }

    /**
     *
     */
    private class PrintDirection implements Printable {

        private List<ModuleModel> models;
        private String assignment;
        //  private GlobalVariablesImplement globalVariables;
        private String description;//書類の種別。右上に印刷されるのみ。
        private List<Integer> modelIndexPerPage; //ページ単位に印刷可能なモデルのインデックス。ページ区切りに使用。
        private int top_margin;
        private int left_margin;

        /**
         *
         * @param job
         * @param assignment
         * @param models
         * @param description
         * @param top_margin
         * @param left_margin
         */
        public PrintDirection(DocPrintJob job, String assignment, List<ModuleModel> models, String description, int top_margin, int left_margin) {
            //  this.globalVariables = grobalVariables;
            this.models = models;
            this.assignment = assignment;
            this.description = description;
            this.top_margin = top_margin;
            this.left_margin = left_margin;

            modelIndexPerPage = new ArrayList<Integer>();

            PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
            attributes.add(new JobName(assignment, null));
            attributes.add(new Copies(1));
            //     attributes.add(MediaName.ISO_A4_WHITE);
            attributes.add(MediaSizeName.ISO_A4);
            attributes.add(OrientationRequested.PORTRAIT);

            try {
                Doc document = new SimpleDoc(this, DocFlavor.SERVICE_FORMATTED.PRINTABLE, null);
                job.print(document, attributes);
            } catch (PrintException e) {
                LogWriter.error(getClass(), e);
            }
        }

        /**
         *
         * @return　ユーザID
         */
        private String getUserID() {
            return GlobalVariables.getUserId();
        }

        /**
         *
         * @return　ユーザモデル
         */
        private UserModel getUserModel() {
            return GlobalVariables.getUserModel();
        }

        /**
         *
         * @return　印刷すべき対象があるか
         */
        private boolean hasKarte() {
            return (models.size() > 0);
        }

        /**
         *
         * @return　印刷すべき対象の一覧
         */
        private KarteBean getKarte() {
            KarteBean result = null;
            if (hasKarte()) {
                result = models.get(0).getKarte();
            }
            return result;
        }

        /**
         *
         * @return　患者
         */
        private PatientModel getPatient() {
            PatientModel result = null;
            KarteBean karte = getKarte();
            if (karte != null) {
                result = getKarte().getPatient();
            }
            return result;
        }

        /**
         *
         * @param item
         * @return　印刷対象の名前
         */
        private String getItemName(ClaimItem item) {
            return item.getName();
        }

        /**
         *
         * @param item
         * @return　印刷対象の数
         */
        private String getItemCount(ClaimItem item) {
            String result = "";
            String item_count = item.getNumber();
            String item_unit = item.getUnit();
            if (item_count != null) {
                result += item_count;
            }
            if (item_unit != null) {
                result += item_unit;
            }
            return result;
        }

        /**
         *　線を引く
         * @param page　印刷先
         * @param x1　開始
         * @param y1　開始
         * @param x2　終点
         * @param y2　終点
         * @param thickness　太さ
         */
        private void drawLine(Graphics2D page, int x1, int y1, int x2, int y2, float thickness) {
            Stroke current = page.getStroke();
            page.setStroke(new BasicStroke(thickness));
            page.drawLine(x1, y1, x2, y2);
            page.setStroke(current);
        }

        /**
         *　四角形を描く
         * @param page　印刷先
         * @param x　左上
         * @param y　左上
         * @param width　幅
         * @param height　高さ
         * @param thickness　線の太さ
         */
        private void drawRect(Graphics2D page, int x, int y, int width, int height, float thickness) {
            Stroke current = page.getStroke();
            page.setStroke(new BasicStroke(thickness));
            page.drawRect(x, y, width, height);
            page.setStroke(current);
        }

        /**
         *　ストリングを描く
         * @param page　印刷先
         * @param x　左上
         * @param y　左上
         * @param str　文字列
         * @param font　フォント
         */
        private void drawString(Graphics2D page, int x, int y, String str, Font font) {
            Font current = page.getFont();
            page.setFont(font);
            page.drawString(str, x, y);
            page.setFont(current);
        }

        /**
         * printXXXXメソッドには、印刷と計測の2つの機能を持つ必要がある。
         * pageをnullとすると計測、その他は印刷。
         * @param page　印刷先
         * @param top　オフセット
         * @return　オフセット
         */
        private int printUser(Graphics2D page, int top) {
            top += 15;
            if (page != null) {//page == nullならば計測のみを行う。
                UserModel user = getUserModel();
                drawString(page, 10 + left_margin, top, getUserID() + " " + user.getLicenseModel().getLicenseDesc() + " " + user.getDepartmentModel().getDepartmentDesc() + " " + user.getFacility().getFacilityName(), new Font(null, 0, 10));
            }
            return top;
        }

        /**
         *　日付印刷
         * @param page　印刷先
         * @param top　オフセット
         * @return　オフセット
         */
        private int printDate(Graphics2D page, int top) {
            top += 15;
            if (page != null) {//page == nullならば計測のみを行う。
                Date now = new Date();
                drawString(page, 10 + left_margin, top, now.toString(), new Font(null, 0, 10));
            }
            return top;
        }

        /**
         *　患者印刷
         * @param page　印刷先
         * @param top　オフセット
         * @return　オフセット
         */
        private int printPatient(Graphics2D page, int top) {
            top += 10;
            if (page != null) {//page == nullならば計測のみを行う。
                drawString(page, 60 + left_margin, top, getPatient().getKanaName(), new Font("SansSerif", Font.BOLD, 6));
            }
            top += 14;
            if (page != null) {//page == nullならば計測のみを行う。
                drawString(page, 15 + left_margin, top, getPatient().getPatientId(), new Font("SansSerif", Font.BOLD, 12));
                drawString(page, 60 + left_margin, top, getPatient().getFullName() + "  " + getPatient().getGenderDesc() + "  " + getPatient().getAgeBirthday(), new Font("SansSerif", Font.BOLD, 12));
            }
            return top;
        }

        /**
         *　線
         * @param page　印刷先
         * @param length
         * @param top　オフセット
         * @return　オフセット
         */
        private int printLine(Graphics2D page, int length, int top) {
            top += 8;
            if (page != null) {//page == nullならば計測のみを行う。
                page.drawLine(10 + left_margin, top, length, top);
            }
            return top;
        }

        /**
         *　はんこ枠
         * @param page　印刷先
         * @param top　オフセット
         * @return　オフセット
         */
        private int printSignatureRect(Graphics2D page, int top) {
            if (page != null) {//page == nullならば計測のみを行う。
                drawRect(page, 390 + left_margin, 15 + top_margin, 56, 56, 2.0f);
            }
            return top;
        }

        /**
         *　タイトル
         * @param page　印刷先
         * @param top　オフセット
         * @param currentPage　印刷ページ数
         * @param pageCount　総ページ数
         * @return　オフセット
         */
        private int printTitle(Graphics2D page, int top, int currentPage, int pageCount) {
            if (page != null) {//page == nullならば計測のみを行う。
                String pageName = "";
                if (pageCount != 1) {//１ページならページ数を印刷しない
                    pageName = "(" + Integer.toString(currentPage + 1) + "/" + Integer.toString(pageCount) + ")";
                }
                drawString(page, 10 + left_margin, top, assignment + "箋 " + pageName, new Font(null, 0, 20));
                drawString(page, 400 + left_margin, top, description, new Font(null, 0, 8));
            }
            return top;
        }

        /**
         * ヘッダーの印刷
         * @param page　印刷ページ
         * @param top　オフセット
         * @param currentPage　ページ数
         * @param pageCount　総ページ
         * @return　オフセット
         */
        private int printHeader(Graphics2D page, int top, int currentPage, int pageCount) {
            top = printTitle(page, top, currentPage, pageCount);
            top = printLine(page, 460 + left_margin, top);
            top = printUser(page, top);
            top = printDate(page, top);
            top = printLine(page, 380 + left_margin, top);
            top = printPatient(page, top);
            top = printSignatureRect(page, top);
            top = printLine(page, 460 + left_margin, top);
            top += 22;
            return top;
        }

        /**
         *　スタンプ名
         * @param page　印刷ページ
         * @param model
         * @param top　オフセット
         * @return　オフセット
         */
        private int printStampName(Graphics2D page, ModuleModel model, int top) {
            top += 25;
            if (page != null) {//page == nullならば計測のみを行う。
                String stampName = model.getModuleInfo().getStampName();
                drawRect(page, 20 + left_margin, top - 9, 9, 9, 1.2f);
                drawString(page, 30 + left_margin, top, stampName, new Font("SansSerif", Font.BOLD, 10));
                drawLine(page, 20 + left_margin, top + 4, 440 + left_margin, top + 4, 0.5f);
            }
            return top;
        }

        /**
         *　内容
         * @param page　印刷先
         * @param item
         * @param top　オフセット
         * @return　オフセット
         */
        private int printItem(Graphics2D page, ClaimItem item, int top) {
            top += 15;
            if (page != null) {//page == nullならば計測のみを行う。
                if (getItemName(item) != null) {
                    drawRect(page, 35 + left_margin, top - 8, 8, 8, 0.8f);
                    drawString(page, 45 + left_margin, top, getItemName(item), new Font(null, 0, 10));
                    drawString(page, 400 + left_margin, top, getItemCount(item), new Font(null, 0, 10));
                }
            }
            return top;
        }

        /**
         *　印刷者
         * @param page　印刷先
         * @param bundle
         * @param top　オフセット
         * @return　オフセット
         */
        private int printAdmin(Graphics2D page, ClaimBundle bundle, int top) {
            String admin = bundle.getAdmin();
            String bundleNumber = bundle.getBundleNumber();

            if (admin != null) {
                if (bundleNumber == null) {
                    bundleNumber = "";
                }

                top += 6;

                if (page != null) {//page == nullならば計測のみを行う。
                    drawRect(page, 35 + left_margin, top, 390, 20, 0.2f);
                }

                top += 14;

                if (page != null) {//page == nullならば計測のみを行う。
                    drawString(page, 45 + left_margin, top, admin, new Font(null, 0, 10));
                    drawString(page, 400 + left_margin, top, bundleNumber, new Font(null, 0, 10));
                }
            }
            return top;
        }

        /**
         *　
         * @param page　印刷先
         * @param modelIndex
         * @param top　オフセット
         * @return　オフセット
         */
        private int printStamp(Graphics2D page, int modelIndex, int top) {
            top = printStampName(page, models.get(modelIndex), top);
            ClaimBundle bundle = (ClaimBundle) models.get(modelIndex).getModel();
            ClaimItem[] item = bundle.getClaimItem();
            for (int itemIndex = 0; itemIndex < item.length; itemIndex++) {
                top = printItem(page, item[itemIndex], top);
            }
            top = printAdmin(page, bundle, top);
            return top;
        }

        /**
         *　ヘッダサイズ計測
         * @param top
         * @param currentPage
         * @param pageCount
         * @return　オフセット
         */
        private int measureHeaderSize(int top, int currentPage, int pageCount) {
            return printHeader(null, top, currentPage, pageCount);
        }

        /**
         *　スタンプサイズ計測
         * @param modelIndex
         * @param top　オフセット
         * @return　オフセット
         */
        private int measureStampSize(int modelIndex, int top) {
            return printStamp(null, modelIndex, top);
        }

        /**
         *　プリント
         * @param page　印刷先
         * @param pageLength
         * @param currentPage
         * @param pageCount
         * @return　オフセット
         */
        private boolean printPage(Graphics2D page, double pageLength, int currentPage, int pageCount) {
            if (currentPage < modelIndexPerPage.size()) {
                if (modelIndexPerPage.get(currentPage) < models.size()) {
                    int top = top_margin;
                    top = printHeader(page, top, currentPage, pageCount);       //ヘッダ印刷。
                    int modelCount = modelIndexPerPage.get(currentPage);        //そのページに印刷するモデルのインデックス（先頭）
                    for (; modelCount < models.size(); modelCount++) {          //モデルのインデックスがモデル数を上回らるなら終わり
                        if (measureStampSize(modelCount, top) < pageLength) {   //スタンプがそのページに収まるなら
                            top = printStamp(page, modelCount, top);            //スタンプを印刷
                        } else {
                            break;
                        }
                    }
                    return true;
                }
            }
            return false;
        }

        /**
         * modelIndexPerPageにモデルのインデックスを設定
         * @param currentIndex
         * @param pageLength
         * @param currentPage
         * @return　オフセット
         */
        private int measurePageSize(int currentIndex, double pageLength, int currentPage) {
            int top = top_margin;
            top = measureHeaderSize(top, currentPage, 0);
            for (; currentIndex < models.size(); currentIndex++) {
                top = measureStampSize(currentIndex, top);
                if (top >= pageLength) {
                    modelIndexPerPage.add(currentIndex);
                    break;
                }
            }
            return currentIndex;
        }

        /**
         * ページ数を計測し、同時にmodelIndexPerPageにモデルのインデックスを設定
         * @param pageLength
         * @return
         */
        private int measure(double pageLength) {
            int currentIndex = 0;
            int currentPage = 0;
            modelIndexPerPage.clear();
            modelIndexPerPage.add(0);
            while (true) {
                currentIndex = measurePageSize(currentIndex, pageLength, currentPage);
                currentPage++;
                if (currentIndex >= models.size()) {
                    break;
                }
            }
            return currentPage;
        }

        /**
         *　プリント
         * @param graphics
         * @param format
         * @param pageIndex
         * @return　オフセット
         */
        @Override
        public int print(Graphics graphics, PageFormat format, int pageIndex) {
            Graphics2D page = (Graphics2D) graphics;
            page.translate(format.getImageableX(), format.getImageableY());
            page.setColor(Color.black);
            int pages = measure(format.getImageableHeight() - top_margin);//ページ数計測
            if (printPage(page, format.getImageableHeight() - top_margin, pageIndex, pages)) {
                return Printable.PAGE_EXISTS;
            } else {
                return Printable.NO_SUCH_PAGE;
            }
        }
    }
}
