package usda.weru.wmrm;

import com.klg.jclass.table.CellAreaHandler;
import com.klg.jclass.table.CellAreaRenderer;
import com.klg.jclass.table.CellBorderModel;
import com.klg.jclass.table.CellLayoutModel;
import com.klg.jclass.table.CellStyleModel;
import com.klg.jclass.table.JCCellPosition;
import com.klg.jclass.table.JCCellRange;
import com.klg.jclass.table.JCPrintEvent;
import com.klg.jclass.table.JCPrintTable;
import static com.klg.jclass.table.JCPrintTable.ALL_PAGES;
import static com.klg.jclass.table.JCPrintTable.MARGIN_IN_INCHES;
import com.klg.jclass.table.JCTable;
import com.klg.jclass.table.JCTableEnum;
import com.klg.jclass.table.PrintCellAreaHandler;
import com.klg.jclass.table.SpanHandler;
import com.klg.jclass.table.beans.LiveTable;
import com.klg.jclass.table.resources.LocaleBundle;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;
import usda.weru.util.Caster;
import usda.weru.util.table.WepsSpanHandler;

/**
 * Glorified copy of WepsPrintTable, but one was needed that worked with 
 * LiveTable
 * @author Ryan
 */
public class WmrmPrintTable extends JCPrintTable {
    
    private static final long serialVersionUID = 1L;
    
    protected double scaleFactor = 1.0d;
    
    protected WepsPrintCellAreaHandler cellAreaHandler;

    public WmrmPrintTable(LiveTable table, double scaleFactor) {
        this(table);
        this.scaleFactor = scaleFactor;
    }

    public WmrmPrintTable(LiveTable table) {
        super(table);
        super.margin = new Insets(36, 36, 36, 36);
    }
    
    @Override
    protected void loadSettings(JCTable table) {
        super.loadSettings(table);
        cellAreaHandler = new WepsPrintCellAreaHandler(this);
    }

    /**
     * Sets the dimensions based on the scaleFactor given
     * @param scaleFactor
     */
    public void setScaleFactor(double scaleFactor) {
        int page_width2 = (int) (page_width * scaleFactor);
        int page_height2 = (int) (page_height * scaleFactor);
        this.scaleFactor = scaleFactor;
        this.setPageDimensions(page_width2, page_height2);
    }

    /**
     * Sets the page format. Table printing only supports a single
     * page format for all pages.
     * @param format
     */
    @Override
    public void setPageFormat(PageFormat format) {
        int top = (int) format.getImageableY();
        int left = (int) format.getImageableX();
        int bottom = (int) (format.getHeight() - format.getImageableHeight() - top);
        int right = (int) (format.getWidth() - format.getImageableWidth() - left);        
        Insets margins = new Insets(top, left, bottom, right);
        setPageMargins(margins);
        this.page_format = format;
    }

    /**
     * Prints the given page using the specified page format.
     * This method is used internally by JClass LiveTable and
     * should not be used by applications.
     * @param gc
     * @param pageIndex
     * @param pageFormat
     * @return 
     */
    @Override
    public int print(Graphics gc, PageFormat pageFormat, int pageIndex) {
        if (pageIndex >= getNumPages()
                || (print_page != ALL_PAGES && pageIndex > 0)) {
            return (Printable.NO_SUCH_PAGE);
        }

        if (print_page == ALL_PAGES) {
            this.printPage(gc, pageIndex);
        } else {
            this.printPage(gc, print_page);
        }

        return (Printable.PAGE_EXISTS);
    }

    @Override
    public void printPage(Graphics gc, int page) {
        Graphics2D g = (Graphics2D) gc;
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        current_page = page;
        JCCellRange cr;
        try {
            cr = getPageCellRange(page);
        } catch (IllegalSizeException e) {
            //System.out.println(e.getMessage());
            return;
        }

        if (cr == null) {
            // No pages to print.
            return;
        }

        g.scale(scaleFactor, scaleFactor);
        printPage(g, cr);
    }

    /**
     * Returns the cell range that defines the boundary of cells that are on
     * a page. The size of the cell range considers labels and frozen
     * cells, but does not include them in the range itself.
     * @return cell range
     * @throws IllegalSizeException
     * @param page
     */
    @Override
    public JCCellRange getPageCellRange(int page) throws IllegalSizeException {
        if (getNumPages() == 0) {
            return (null);
        }
        JCCellPosition c1 = getPageStartCell(page);
        JCCellPosition c2 = getPageEndCell(page);

        return (new JCCellRange(c1.row, c1.column, c2.row, c2.column));
    }

    /**
     * Draws the table for a given cell range.
     * @param cr The cell range
     * @param gc The graphics context to use
     */
    @Override
    public void printPage(Graphics gc, JCCellRange cr) {
        cellAreaHandler.setPageRange(cr);
        CellLayoutModel clm = table.getCellLayout();

        Dimension d = new Dimension(0, 0);
        d.width = clm.getWidth(JCTableEnum.LABEL)
                + getRowLabelOffset()
                + getFrozenColumnWidth()
                + getColumnPosition(cr.end_column)
                - getColumnPosition(cr.start_column)
                + clm.getWidth(cr.end_column);

        d.height = clm.getHeight(JCTableEnum.LABEL)
                + getColumnLabelOffset()
                + getFrozenRowHeight()
                + getRowPosition(cr.end_row)
                - getRowPosition(cr.start_row)
                + clm.getHeight(cr.end_row);

        gc.setColor(Color.white);


        gc.fillRect(0, 0, page_width, page_height);

        Point p = getTableOffset();
        gc.setClip(p.x, p.y, table_width, table_height);

        // break spans that cross pages
        if (table.getSpanHandler() != null) {

            // Get the orignal spans from table. This must be done for each page
            // otherwise the previous page spans are used.
            spanHandler = new WepsSpanHandler();
            spanHandler.setTable(this);

            // copy over the ranges
            // casting is quest's fault
            Collection<JCCellRange> spans = Caster.<Collection<JCCellRange>>cast(table.getSpannedRanges());
            try {
                if (spans != null) {
                    Iterator<JCCellRange> it = spans.iterator();
                    while (it.hasNext()) {
                        JCCellRange cr3 = it.next();
                        JCCellRange sr = new JCCellRange(cr3.start_row,
                                cr3.start_column,
                                cr3.end_row,
                                cr3.end_column);
                        spanHandler.addSpannedRange(sr);

                    }
                }
            } catch (Exception e) {
            }

            // Then split them in our local spanHandler
            splitSpans(cr);
        }

        // Do frozen column headers
        int frozen_columns = getFrozenColumns();
        if (frozen_columns > 0) {
            for (int c = 0; c < frozen_columns; c++) {
                cellAreaHandler.getCellAreaRenderer().paintCell(gc, JCTableEnum.LABEL, c);

            }
        }
        // Do column headers
        for (int c = cr.start_column; c <= cr.end_column; c++) {
            cellAreaHandler.getCellAreaRenderer().paintCell(gc, JCTableEnum.LABEL, c);

        }
        // do frozen row headers
        int frozen_rows = getFrozenRows();
        if (frozen_rows > 0) {
            for (int r = 0; r < frozen_rows; r++) {
                cellAreaHandler.getCellAreaRenderer().paintCell(gc, r,
                        JCTableEnum.LABEL);

            }
        }
        // Do row headers
        for (int r = cr.start_row; r <= cr.end_row; r++) {
            cellAreaHandler.getCellAreaRenderer().paintCell(gc, r, JCTableEnum.LABEL);

        }
        // Draw frozen dead cells
        if (frozen_columns > 0 && frozen_rows > 0) {
            for (int r = 0; r < frozen_rows; r++) {
                for (int c = 0; c < frozen_columns; c++) {
                    cellAreaHandler.getCellAreaRenderer().paintCell(gc, r, c);

                }
            }
        }
        // Draw frozen columns
        if (frozen_columns > 0) {
            for (int r = cr.start_row; r <= cr.end_row; r++) {
                for (int c = 0; c < frozen_columns; c++) {
                    cellAreaHandler.getCellAreaRenderer().paintCell(gc, r, c);

                }
            }
        }
        // Draw frozen rows
        if (frozen_rows > 0) {
            for (int r = 0; r < frozen_rows; r++) {
                for (int c = cr.start_column; c <= cr.end_column; c++) {
                    cellAreaHandler.getCellAreaRenderer().paintCell(gc, r, c);

                }
            }
        }
        // Do cells
        for (int r = cr.start_row; r <= cr.end_row; r++) {
            for (int c = cr.start_column; c <= cr.end_column; c++) {
                cellAreaHandler.getCellAreaRenderer().paintCell(gc, r, c);

            }
        }

        if (getFrameBorderWidth() > 0) {
            // Calculate rectangles for frame border
            Rectangle startBounds = cellAreaHandler.getBounds(cr.start_row,
                    JCTableEnum.LABEL);
            Rectangle endBounds = cellAreaHandler.getBounds(cr.end_row,
                    JCTableEnum.LABEL);
            Rectangle rowLabelRect = new Rectangle(startBounds.x,
                    startBounds.y,
                    startBounds.width,
                    endBounds.y
                    + endBounds.height
                    - startBounds.y);
            if (frozen_rows > 0) {
                Rectangle labelBounds
                        = cellAreaHandler.getBounds(0, JCTableEnum.LABEL);
                Rectangle frozenLabelBounds
                        = cellAreaHandler.getBounds(frozen_rows - 1, JCTableEnum.LABEL);
                Rectangle frRowLabelRect = new Rectangle(labelBounds.x,
                        labelBounds.y,
                        labelBounds.width,
                        frozenLabelBounds.y
                        + frozenLabelBounds.height
                        - labelBounds.y);
                rowLabelRect = new Rectangle(Math.min(rowLabelRect.x,
                        frRowLabelRect.x),
                        Math.min(rowLabelRect.y,
                                frRowLabelRect.y),
                        Math.max(rowLabelRect.width,
                                frRowLabelRect.width),
                        rowLabelRect.height
                        + frRowLabelRect.height);
            }
            startBounds = cellAreaHandler.getBounds(JCTableEnum.LABEL,
                    cr.start_column);
            endBounds = cellAreaHandler.getBounds(JCTableEnum.LABEL, cr.end_column);
            Rectangle colLabelRect = new Rectangle(startBounds.x,
                    startBounds.y,
                    endBounds.x
                    + endBounds.width
                    - startBounds.x,
                    startBounds.height);
            if (frozen_columns > 0) {
                Rectangle labelBounds
                        = cellAreaHandler.getBounds(JCTableEnum.LABEL, 0);
                Rectangle frozenLabelBounds
                        = cellAreaHandler.getBounds(JCTableEnum.LABEL,
                                frozen_columns - 1);
                Rectangle frColLabelRect = new Rectangle(labelBounds.x,
                        labelBounds.y,
                        frozenLabelBounds.x
                        + frozenLabelBounds.width
                        - labelBounds.x,
                        labelBounds.height);
                colLabelRect = new Rectangle(Math.min(colLabelRect.x,
                        frColLabelRect.x),
                        Math.min(colLabelRect.y,
                                frColLabelRect.y),
                        colLabelRect.width
                        + frColLabelRect.width,
                        Math.max(colLabelRect.height,
                                frColLabelRect.height));
            }
            Rectangle cellRect = new Rectangle(colLabelRect.x,
                    rowLabelRect.y,
                    colLabelRect.width,
                    rowLabelRect.height);

            // draw frame borders
            CellBorderModel cellBorder = table.getFrameBorder();
            Color bg = table.getBackground();

            cellBorder.drawBackground(gc, frame_border_width,
                    JCTableEnum.BORDERSIDE_ALL,
                    rowLabelRect.x - frame_border_width,
                    rowLabelRect.y - frame_border_width,
                    rowLabelRect.width + 2 * frame_border_width,
                    rowLabelRect.height + 2 * frame_border_width,
                    bg.brighter(), bg.darker(), bg);
            cellBorder.drawBackground(gc, frame_border_width,
                    JCTableEnum.BORDERSIDE_ALL,
                    colLabelRect.x - frame_border_width,
                    colLabelRect.y - frame_border_width,
                    colLabelRect.width + 2 * frame_border_width,
                    colLabelRect.height + 2 * frame_border_width,
                    bg.brighter(), bg.darker(), bg);
            cellBorder.drawBackground(gc, frame_border_width,
                    JCTableEnum.BORDERSIDE_ALL,
                    cellRect.x - frame_border_width,
                    cellRect.y - frame_border_width,
                    cellRect.width + 2 * frame_border_width,
                    cellRect.height + 2 * frame_border_width,
                    bg.brighter(), bg.darker(), bg);
        }

        doPrintPageHeader(gc, current_page);
        doPrintBody(gc, current_page);
        doPrintPageFooter(gc, current_page);
        cellAreaHandler.setPageRange(null);

    }

    @Override
    public CellStyleModel getCellStyle(int rowIndex, int columnIndex) {
        CellStyleModel cellStyle = table.getCellStyle(rowIndex, columnIndex);
        cellStyle.setClipHints(JCTableEnum.SHOW_NONE);
        return cellStyle;
    }

    @Override
    protected void createDefaultSpanHandler() {
        try {
            SpanHandler spanHandler = new WepsSpanHandler();        //Allows labels and cells to mix.
            spanHandler.setTable(this);
            setSpanHandler(spanHandler);
        } catch (Exception e) {
            //System.out.println("SpanHandler initialization error: " + e.getMessage());
        }
    }

    /**
     * Returns the column label height.
     * @return column label height
     */
    private int getColumnLabelHeight() {
        if (column_label_display) {
            return (getCellLayout().getHeight(JCTableEnum.LABEL));
        }
        return (0);
    }

    /**
     * Returns the start position of a column.
     * @return start position of a column
     * @param column
     */
    private int getColumnPosition(int column) {
        CellLayoutModel clm = getCellLayout();
        return (clm.getColumnPosition(column));
    }

    /**
     * Returns the width of frozen columns.
     * @return width of frozen columns
     */
    private int getFrozenColumnWidth() {
        CellLayoutModel clm = getCellLayout();

        int width = 0;
        for (int c = 0; c < getFrozenColumns(); c++) {
            width += clm.getWidth(c);
        }
        return (width);
    }

    /**
     * Returns the height of frozen rows.
     * @return height of frozen rows
     */
    private int getFrozenRowHeight() {
        CellLayoutModel clm = getCellLayout();

        int height = 0;
        for (int r = 0; r < getFrozenRows(); r++) {
            height += clm.getHeight(r);
        }
        return (height);
    }

    /**
     * Sets the dimensions of a page.
     * @param page_height
     * @param page_width
     */
    @Override
    public void setPageDimensions(int page_width, int page_height) {
        if (page_width == 0 || page_height == 0) {
            page_width = 612;
            page_height = 792;
            page_resolution = 72;
        }
        //Apply scale factor
        this.page_width = (int) (page_width / scaleFactor);
        this.page_height = (int) (page_height / scaleFactor);

        setInternalDimensions();
    }

    /**
     * Sets the internal table dimensions (page dimensions minus margins).
     */
    private void setInternalDimensions() {
        if (page_width == 0 || page_height == 0) {
            // Page dimensions not yet set, don't set internal dimensions yet
            return;
        }

        int unit_conversion = (margin_unit == MARGIN_IN_INCHES) ? page_resolution : 1;
        table_width = page_width - unit_conversion * (margin.left + margin.right);
        table_height = page_height - unit_conversion * (margin.top + margin.bottom);

        CellLayoutModel clm = getCellLayout();

        // Restrict the width to the largest available size
        int working_width = table_width - getRowLabelWidth()
                - getRowLabelOffset() - getFrozenColumnWidth()
                - 4 * getFrameBorderWidth();
        for (int c = getFrozenColumns(); c < getNumColumns(); c++) {
            if (clm.getWidth(c) > working_width) {
                clm.setWidth(c, working_width);
            }
        }
        // Restrict the height to the largest available size
        int working_height = table_height - getColumnLabelHeight()
                - getColumnLabelOffset() - getFrozenRowHeight()
                - 4 * getFrameBorderWidth();
        for (int r = getFrozenRows(); r < getNumRows(); r++) {
            if (clm.getHeight(r) > working_height) {
                clm.setHeight(r, working_height);
            }
        }
    }

    /**
     * Private method returns the number of horizontal pages.
     * @return number of horizontal pages
     * @throws IllegalSizeException
     */
    private int getNumColumnPages() throws IllegalSizeException {
        int current;
        int column_pages = 1;

        int row_label_width = getRowLabelWidth();
        int frozen_column_width = getFrozenColumnWidth();

        // Can't print table if the row labels and frozen columns are larger than
        // the page width
        if (row_label_width + row_label_offset + frozen_column_width
                + 4 * frame_border_width > table_width) {
            throw new IllegalSizeException("The total width of row labels and "
                    + "frozen columns is larger than the "
                    + "page width.");
        }
        CellLayoutModel clm = getCellLayout();

        current = row_label_width + row_label_offset + frozen_column_width
                + 4 * frame_border_width;
        for (int c = frozen_columns; c < getNumColumns(); c++) {
            current += clm.getWidth(c);
            if (current > table_width) {
                current = row_label_width + row_label_offset
                        + frozen_column_width + 4 * frame_border_width
                        + clm.getWidth(c);
                column_pages++;
            }
        }
        return (column_pages);
    }

    /**
     * Private method returns the number of vertical pages.
     * @return number of vertical pages
     * @throws IllegalSizeException
     */
    private int getNumRowPages() throws IllegalSizeException {
        int current;
        int row_pages = 1;

        int column_label_height = getColumnLabelHeight();
        int frozen_row_height = getFrozenRowHeight();

        // Can't print table if the column labels and frozen rows are higher than
        // the page height
        if (column_label_height + column_label_offset + frozen_row_height
                + 4 * frame_border_width > table_height) {
            throw new IllegalSizeException("The total height of column labels "
                    + "and frozen rows is larger than the "
                    + "page height.");
        }

        CellLayoutModel clm = getCellLayout();

        current = column_label_height + column_label_offset + frozen_row_height
                + 4 * frame_border_width;
        for (int r = getFrozenRows(); r < getNumRows(); r++) {
            current += clm.getHeight(r);
            if (current > table_height) {
                current = column_label_height + column_label_offset
                        + frozen_row_height + 4 * frame_border_width
                        + clm.getHeight(r);
                row_pages++;
            }
        }
        return (row_pages);
    }

    /**
     * Private method returns the first cell on a page.
     * @return
     * @param page
     * @throws IllegalSizeException
     */
    private JCCellPosition getPageStartCell(int page) throws IllegalSizeException {
        int column_pages = getNumColumnPages();

        int rp = page / column_pages + 1;
        int cp = page % column_pages + 1;

        int column = frozen_columns;
        if (cp == 1) {
            column = frozen_columns;
        } else {
            column = getPageEndCell(page - 1).column + 1;
        }

        int row = frozen_rows;
        if (rp == 1) {
            row = frozen_rows;
        } else if (cp == 1) {
            row = getPageEndCell(page - 1).row + 1;
        } else {
            row = getPageEndCell(column_pages * (rp - 1) - 1).row + 1;
        }
        return (new JCCellPosition(row, column));
    }

    /**
     * Private method returns the last cell on a page.
     * @return
     * @param page
     * @throws IllegalSizeException
     */
    private JCCellPosition getPageEndCell(int page) throws IllegalSizeException {
        int current;
        int column_pages = getNumColumnPages();
        int row_pages = getNumRowPages();

        int rp = page / column_pages + 1;
        int cp = page % column_pages + 1;

        CellLayoutModel clm = getCellLayout();

        int column = frozen_columns;
        if (cp == column_pages) {
            column = getNumColumns() - 1;
        } else {
            int start_column = frozen_columns;
            if (cp > 1) {
                start_column = getPageEndCell(page - 1).column + 1;
            }
            int xover_width = (table_width - getRowLabelWidth()
                    - getRowLabelOffset() - getFrozenColumnWidth()
                    - 4 * getFrameBorderWidth());
            current = 0;
            for (int c = start_column; c < getNumColumns(); c++) {
                current += clm.getWidth(c);
                if (current <= xover_width) {
                    column = c;
                }
            }
        }

        int row = frozen_rows;
        if (rp == row_pages) {
            row = getNumRows() - 1;
        } else {
            int start_row = frozen_rows;
            if (rp > 1 && cp == 1) {
                start_row = getPageEndCell(page - 1).row + 1;
            } else if (rp > 1 && cp != 1) {
                start_row = getPageEndCell(column_pages * (rp - 1) - 1).row + 1;
            }
            int xover_height = (table_height - getColumnLabelHeight()
                    - getColumnLabelOffset() - getFrozenRowHeight()
                    - 4 * getFrameBorderWidth());
            current = 0;
            for (int r = start_row; r < getNumRows(); r++) {
                current += clm.getHeight(r);
                if (current <= xover_height) {
                    row = r;
                }
            }
        }

        return (new JCCellPosition(row, column));
    }

    /**
     * Returns the row label width.
     * @return row label width
     */
    private int getRowLabelWidth() {
        if (row_label_display) {
            return (getCellLayout().getWidth(JCTableEnum.LABEL));
        }
        return (0);
    }

    /**
     * Returns the start position of a row.
     * @return start position of a row
     * @param row
     */
    private int getRowPosition(int row) {
        CellLayoutModel clm = getCellLayout();
        return (clm.getRowPosition(row));
    }

    /**
     * Private method splits spans that cross pages.
     * @param cr
     */
    private void splitSpans(JCCellRange cr) {
        if (spanHandler != null) {
            Vector<JCCellRange> new_spans = new Vector<>();

            JCCellRange cr1, cr2;

            Collection<JCCellRange> col = Caster.<Collection<JCCellRange>>cast(spanHandler.getSpannedRanges());
            if (col != null) {
                Iterator<JCCellRange> it = col.iterator();
                while (it.hasNext()) {
                    cr1 = it.next();
                    cr2 = new JCCellRange(cr1.start_row, cr1.start_column,
                            cr1.end_row, cr1.end_column);
                    // span starts above the print range
                    if (cr1.start_row < cr.start_row && cr1.end_row >= cr.end_row) {
                        cr2.reshape(cr2.start_row, cr2.start_column,
                                cr.start_row - 1, cr2.end_column);
                    }
                    // span extends below the print range
                    if (cr1.start_row <= cr.end_row && cr1.end_row > cr.end_row) {
                        cr2.reshape(cr2.start_row, cr2.start_column,
                                cr.end_row, cr2.end_column);
                    }
                    // span starts left of the print range
                    if (cr1.start_column < cr.start_column
                            && cr1.end_column >= cr.start_column) {
                        cr2.reshape(cr2.start_row, cr.start_column,
                                cr2.end_row, cr2.end_column);
                    }
                    // span extends right of the print range
                    if (cr1.start_column <= cr.end_column
                            && cr1.end_column > cr.end_column) {
                        cr2.reshape(cr2.start_row, cr2.start_column,
                                cr2.end_row, cr.end_column);
                    }

                    new_spans.addElement(cr2);
                }
            }

            try {
                spanHandler.setSpannedRanges(new_spans);
            } catch (Exception e) {
            }
        }
    }

    /**
     * Notifies print listeners that the page header is being printed.
     * @param gc
     * @param page
     */
    private void doPrintPageHeader(Graphics gc, int page) {
        if (printListeners != null) {
            Graphics gc_header = gc.create();

            gc_header.setColor(Color.black);
            gc_header.setFont(getFont());

            int unit_conversion
                    = (margin_unit == MARGIN_IN_INCHES) ? page_resolution : 1;
            gc_header.setClip(unit_conversion * margin.left,
                    0,
                    table_width,
                    unit_conversion * margin.top);
            gc_header.translate(unit_conversion * margin.left, 0);

            JCPrintEvent ev = new JCPrintEvent(this, gc_header, page + 1,
                    getNumPages(),
                    JCPrintEvent.PRINT_HEADER, this);
            fireJCPrintEvent(ev);
            gc_header.dispose();
        }
    }

    /**
     * Notifies print listeners that the body of the page has been printed.
     * @param gc
     * @param page
     */
    private void doPrintBody(Graphics gc, int page) {
        if (printListeners != null) {
            Graphics gc_body = gc.create();

            gc_body.setColor(Color.black);
            gc_body.setFont(getFont());

            JCPrintEvent ev = new JCPrintEvent(this, gc_body, page + 1,
                    getNumPages(),
                    JCPrintEvent.PRINT_BODY, this);
            fireJCPrintEvent(ev);
            gc_body.dispose();
        }
    }

    /**
     * Notifies print listeners that the page footer is being printed.
     * @param gc
     * @param page
     */
    private void doPrintPageFooter(Graphics gc, int page) {
        Graphics gc_footer = gc.create();

        gc_footer.setColor(Color.black);
        gc_footer.setFont(getFont());

        int unit_conversion
                = (margin_unit == MARGIN_IN_INCHES) ? page_resolution : 1;
        gc_footer.setClip(unit_conversion * margin.left,
                page_height - unit_conversion * margin.bottom,
                table_width,
                unit_conversion * margin.bottom);
        gc_footer.translate(unit_conversion * margin.left,
                page_height - unit_conversion * margin.bottom);

        if (printListeners != null) {
            JCPrintEvent ev = new JCPrintEvent(this, gc_footer, page + 1,
                    getNumPages(),
                    JCPrintEvent.PRINT_FOOTER, this);
            fireJCPrintEvent(ev);
        }
        printPageFooter(gc, page + 1);

        gc_footer.dispose();
    }

    /**
     * Prints default footer.
     * @param gc
     * @param page
     */
    private void printPageFooter(Graphics gc, int page) {
        Graphics gc_footer = gc.create();

        int unit_conversion
                = (margin_unit == MARGIN_IN_INCHES) ? page_resolution : 1;

        gc_footer.setClip(unit_conversion * margin.left,
                page_height - unit_conversion * margin.bottom,
                table_width,
                unit_conversion * margin.bottom);
        gc_footer.translate(unit_conversion * margin.left,
                page_height - unit_conversion * margin.bottom);

        Rectangle r = gc_footer.getClipBounds();

        gc_footer.setColor(Color.black);
        gc_footer.setFont(getFont());
        String pageText = LocaleBundle.string(LocaleBundle.PRINT_PAGE_FIRST)
                + page + LocaleBundle.string(LocaleBundle.PRINT_PAGE_SECOND)
                + getNumPages();
        gc_footer.drawString(pageText, 0, r.height / 2);
    }

    /**
     * Returns the top/left corner of the table on the page.
     * @return top/left corner of the table on the page
     */
    private Point getTableOffset() {
        int unit_conversion = 1;
        if (margin_unit == MARGIN_IN_INCHES) {
            unit_conversion = page_resolution;
        }
        return (new Point(unit_conversion * margin.left,
                unit_conversion * margin.top));
    }

    static class IllegalSizeException extends IllegalStateException {

        private static final long serialVersionUID = 1L;

        protected IllegalSizeException(String s) {
            super(s);
        }

    } // IllegalSizeException

    static class WepsPrintCellAreaHandler extends PrintCellAreaHandler {

        private static final long serialVersionUID = 1L;

        public WepsPrintCellAreaHandler(JCTable table) {
            super(table);
            this.table = table;
            cellAreaRenderer = new WepsCellAreaRenderer(this);
        }

        public JCCellRange get() {
            return page_range;
        }

        public void setPageRange(JCCellRange range) {
            page_range = range;
        }

        @Override
        public WepsCellAreaRenderer getCellAreaRenderer() {
            return (WepsCellAreaRenderer) cellAreaRenderer;
        }
    }

    static class WepsCellAreaRenderer extends CellAreaRenderer {

        private static final long serialVersionUID = 1L;

        public WepsCellAreaRenderer(CellAreaHandler cellAreaHandler) {
            super(cellAreaHandler);
        }

        @Override
        protected void paintCell(Graphics g, int rowIndex, int columnIndex) {
            super.paintCell(g, rowIndex, columnIndex);
        }
    }
}
