//=============================================================================
//
//	Copyright (C) 1996-1997 by Paul S. McCarthy and Eric Sunshine.
//		Written by Paul S. McCarthy and Eric Sunshine.
//			    All Rights Reserved.
//
//	This notice may not be removed from this source code.
//
//	This object is included in the MiscKit by permission from the authors
//	and its use is governed by the MiscKit license, found in the file
//	"License.rtf" in the MiscKit distribution.  Please refer to that file
//	for a list of all applicable permissions and restrictions.
//	
//=============================================================================
//-----------------------------------------------------------------------------
// MiscTableViewPrint.M
//
//	Printing support for MiscTableScroll.
//
// FIXME: Move most of this junk up into table scroll.
// FIXME: Separate computing the number of pages from doing all the
//	other work, so that hopefully, we can do all the other work
//	only if the user proceeds with a print operation.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// $Id: MiscTableViewPrint.M,v 1.7 97/06/18 09:56:31 sunshine Exp $
// $Log:	MiscTableViewPrint.M,v $
// Revision 1.7  97/06/18  09:56:31  sunshine
// v125.9: Worked around Objective-C++ compiler crash in OPENSTEP 4.2 for NT
// when sending message to 'super' from within a category.  Unified naming.
// 
// Revision 1.6  97/04/04  05:04:03  sunshine
// 0.125.6: Replaced assert() with NSAssert() variations for portability and
// because NSAssert() raises an exception which can be caught by the
// application if need be.
// 
// Revision 1.5  97/04/01  08:02:59  sunshine
// v0.125.5: Fixed bug: As a result of autorelease mechanism, TableView
// actually gets freed *after* TableScroll, however TableView still pointed
// to the TableScroll with 'scroll' and tried to send messages to the freed
// object.  (This showed up when I fixed the bug in TableBorder where it was
// retaining its 'owner' TableScroll but never releasing it, thus leaking the
// TableScroll, TableView, etc.)
//-----------------------------------------------------------------------------
#import "MiscTableView.h"
#import "MiscTableScrollPrivate.h"
#import "MiscTableBorder.h"
#import "MiscBorderView.h"
#import "MiscGeometry.h"

extern "Objective-C" {
#import	<AppKit/NSApplication.h>
#import	<AppKit/NSImage.h>
#import	<AppKit/NSPrintInfo.h>
#import <AppKit/NSPrintOperation.h>
#import	<AppKit/NSPrintPanel.h>
}

extern "C" {
#import	<math.h>	// floor()
}

@implementation MiscTableView(Print)

//-----------------------------------------------------------------------------
// getHeight:
//-----------------------------------------------------------------------------
- (float)getHeight:(id)obj
    {
    if (obj != 0 && [obj respondsToSelector:@selector(frame)])
	return [obj frame].size.height;
    return 0;
    }


//-----------------------------------------------------------------------------
// numPagesForBorder:pageSize:
//-----------------------------------------------------------------------------
- (int)numPagesForBorder:(MiscBorderType)bt pageSize:(float)scaled_page_size
    {
    int npages = 0;

    // FIXME: Borders with uniform size slots should be much simpler
    // than this.

    MiscPixels const page_size = (MiscPixels)floor(scaled_page_size);

    NSParameterAssert( page_size > 0 );

    MiscTableBorder* const border =
		(bt == MISC_COL_BORDER ? colBorder : rowBorder);

    MiscPixels sz_sum = 0;
    MiscCoord_V const numSlots = border->count();
    for (MiscCoord_V vslot = 0; vslot < numSlots; vslot++)
	{
	MiscPixels sz = border->effectiveSize(vslot);
	if (sz <= page_size)
	    {
	    sz_sum += sz;
	    if (sz_sum > page_size)
		{
		sz_sum = sz;
		npages++;
		}
	    }
	else
	    {
	    if (sz_sum > 0)
		npages++;
	    npages += sz / page_size;
	    sz_sum = sz % page_size;
	    }
	}

    if (sz_sum >= 0)
	npages++;

    return npages;
    }


//-----------------------------------------------------------------------------
// append
//-----------------------------------------------------------------------------
static void append( MiscPixels w, MiscTSPageBreak& bk, MiscTSPageBreak bks[],
			int n, int num_breaks )
    {
    NSCParameterAssert( n < num_breaks );
    bk.size = w;
    bks[ n ] = bk;
    bk.offset += w;
    }


//-----------------------------------------------------------------------------
// border:pageSize:numPages:calcBreaks:
//-----------------------------------------------------------------------------
- (void)border:(MiscBorderType)bt
	pageSize:(float)pageSize
	numPages:(int)nPages
	clip:(BOOL)do_clip
	calcBreaks:(MiscTSPageBreak*)bks
    {
    MiscPixels const page_size = (MiscPixels)floor(pageSize);

    // FIXME: Borders with uniform size slots should be much simpler
    // than this.

    NSParameterAssert( page_size > 0 );

    MiscTableBorder* const border =
		(bt == MISC_COL_BORDER ? colBorder : rowBorder);

    MiscTSPageBreak bk;
    bk.offset = 0;
    bk.size = 0;
    bk.first = 0;
    bk.last = 0;

    int pg = 0;
    MiscPixels sz_sum = 0;

    MiscCoord_V const numSlots = border->count();
    for (MiscCoord_V vslot = 0; vslot < numSlots; vslot++)
	{
	MiscPixels sz = border->effectiveSize(vslot);
	if (sz <= page_size)
	    {
	    sz_sum += sz;
	    if (sz_sum > page_size)
		{
		bk.last = (vslot - 1);
		append( sz_sum - sz, bk, bks, pg++, nPages );
		if (do_clip) goto clip_exit;
		bk.first = vslot;
		sz_sum = sz;
		}
	    }
	else
	    {
	    if (sz_sum > 0)
		{
		bk.last = (vslot - 1);
		append( sz_sum, bk, bks, pg++, nPages );
		if (do_clip) goto clip_exit;
		}
	    bk.first = vslot;
	    bk.last = ~vslot;
	    do  {
		append( page_size, bk, bks, pg++, nPages );
		if (do_clip) goto clip_exit;
		bk.first = ~vslot;
		sz -= page_size;
		}
	    while (sz > page_size);
	    sz_sum = sz;
	    }
	}

    if (sz_sum >= 0)
	{
	bk.last = numSlots - 1;
	append( sz_sum, bk, bks, pg++, nPages );
	}

clip_exit:

    NSParameterAssert( pg == nPages );
    }


//-----------------------------------------------------------------------------
// - getImageForView:
//-----------------------------------------------------------------------------
- (NSImage*)getImageForView:(id)view
    {
    return [[[NSImage alloc] initWithData:
		[view dataWithEPSInsideRect:[view bounds]]] autorelease];
    }


//-----------------------------------------------------------------------------
// getImage:isScaled:scaleFactor:forView:
//-----------------------------------------------------------------------------
- (NSImage*)getImage:(NSRect)rect
	isScaled:(BOOL)isScaled
	scaleFactor:(double)scaleFactor
	forView:(MiscBorderView*)view
    {
    // FIXME: Do something about the scaling.
    return [[[NSImage alloc] initWithData:
		[view dataWithEPSInsideRect:rect]] autorelease];
    }


//-----------------------------------------------------------------------------
// getImages:info:
//-----------------------------------------------------------------------------
- (void)getImages:(MiscTSPageImages*)img
	info:(MiscTablePrintInfo const*)info
    {
    id delg;
    id const scroll = [self scroll];

    img->page_header = 0;
    img->page_footer = 0;
    img->col_titles = 0;
    img->row_titles = 0;

    if (pages->pageHeader != 0)
	{
	delg = [scroll responsibleDelegate:
			MiscDelegateFlags::DEL_PRINT_PAGE_HEADER];
	if (delg != 0)
	    [delg tableScroll:scroll
			willPrintPageHeader:pages->pageHeader
			info:info];
	// FIXME: Need to shrink/grow/center/position.
	img->page_header = [[self getImageForView:pages->pageHeader] retain];
	}

    if (pages->pageFooter != 0)
	{
	delg = [scroll responsibleDelegate:
			MiscDelegateFlags::DEL_PRINT_PAGE_FOOTER];
	if (delg != 0)
	    [delg tableScroll:scroll
			willPrintPageFooter:pages->pageFooter
			info:info];
	// FIXME: Need to shrink/grow/center/position.
	img->page_footer = [[self getImageForView:pages->pageFooter] retain];
	}

    if (pages->colTitles != 0)
	{
	NSRect r = info->print_rect;
	r.origin.y = 0;
	r.size.height = pages->col_titles_height;
	img->col_titles = [[self getImage:r isScaled:info->is_scaled
				scaleFactor:info->scale_factor
				forView:pages->colTitles] retain];
	}

    if (pages->rowTitles != 0)
	{
	NSRect r = info->print_rect;
	r.origin.x = 0;
	r.size.width = pages->row_titles_width;
	img->row_titles = [[self getImage:r isScaled:info->is_scaled
				scaleFactor:info->scale_factor
				forView:pages->rowTitles] retain];
	}
    }


//-----------------------------------------------------------------------------
// calcPages
//-----------------------------------------------------------------------------
- (BOOL)calcPages
    {
    BOOL ok = YES;	// FIXME: Deal with pages that are too small, etc.
    id const scroll = [self scroll];

    pages = new MiscTablePages;

    pages->pageHeader = [scroll getPageHeader];
    pages->pageFooter = [scroll getPageFooter];
    pages->page_header_height = [self getHeight:pages->pageHeader];
    pages->page_footer_height = [self getHeight:pages->pageFooter];

    pages->colTitles = [scroll colTitles];
    pages->rowTitles = [scroll rowTitles];
    pages->col_titles_height = [scroll columnTitlesHeight];
    pages->row_titles_width  = [scroll rowTitlesWidth ];

    NSRect const rFrame = [self frame];
    float total_width  = rFrame.size.width  + pages->row_titles_width;
    float total_height = rFrame.size.height + pages->col_titles_height;

    NSPrintInfo* printInfo = [NSPrintInfo sharedPrintInfo];
    NSSize const paperSize = [printInfo paperSize];

    float page_width = paperSize.width -
			[printInfo leftMargin] - [printInfo rightMargin];
    float page_height = paperSize.height -
			[printInfo topMargin] - [printInfo bottomMargin] -
			pages->page_header_height -
			pages->page_footer_height;

    int const hPagination = [printInfo horizontalPagination];
    int const vPagination = [printInfo verticalPagination];
//     int const hCentered = [printInfo isHorizontallyCentered];
//     int const vCentered = [printInfo isVerticallyCentered];

    BOOL hScaled = NO;	double hScaler = 1.0;
    BOOL vScaled = NO;	double vScaler = 1.0;
    BOOL isScaled = NO;	double scaleFactor = 1.0;

    if (hPagination == NSFitPagination && total_width > page_width)
	{
	hScaled = YES;
	hScaler = double(page_width) / double(total_width);
	}

    if (vPagination == NSFitPagination && total_height > page_height)
	{
	vScaled = YES;
	vScaler = double(page_height) / double(total_height);
	}

    if (hScaled || vScaled)
	{
	isScaled = YES;
	if (vScaler < hScaler)
	    scaleFactor = vScaler;
	else
	    scaleFactor = hScaler;
	}

    pages->info.is_scaled = isScaled;
    pages->info.scale_factor = scaleFactor;

    float scaled_page_width = page_width;
    float scaled_page_height = page_height;

    if (isScaled)
	{
	scaled_page_width = double(page_width) / scaleFactor;
	scaled_page_height = double(page_height) / scaleFactor;
	}

    scaled_page_width -= pages->row_titles_width;
    scaled_page_height -= pages->col_titles_height;


    //--- Calculate the number of rows/cols/pages -------------------------

    int ncols = 1;
    int nrows = 1;

    if (hPagination == NSAutoPagination &&
	rFrame.size.width > scaled_page_width)
	ncols = [self numPagesForBorder:MISC_COL_BORDER
			pageSize:scaled_page_width];

    if (vPagination == NSAutoPagination &&
	rFrame.size.height > scaled_page_height)
	nrows = [self numPagesForBorder:MISC_ROW_BORDER
			pageSize:scaled_page_height];

    NSParameterAssert( nrows > 0 );  NSParameterAssert( ncols > 0 );

    int const npages = nrows * ncols;


    //--- Allocate arrays -------------------------------------------------

    pages->col_breaks = (MiscTSPageBreak*)
			malloc( (ncols + nrows) * sizeof(MiscTSPageBreak) );
    pages->row_breaks = pages->col_breaks + ncols;

    MiscTSPageImages* images = (MiscTSPageImages*)
			malloc( npages * sizeof(*images) );
    pages->images = images;


    //--- Prepare print rectangles and border images ----------------------

    [self border:MISC_COL_BORDER
	    pageSize:scaled_page_width
	    numPages:ncols
	    clip:(hPagination == NSClipPagination)
	    calcBreaks:pages->col_breaks];

    [self border:MISC_ROW_BORDER
	    pageSize:scaled_page_height
	    numPages:nrows
	    clip:(vPagination == NSClipPagination)
	    calcBreaks:pages->row_breaks];


    MiscTablePrintInfo& info = pages->info;
    info.page_size = paperSize;
    info.num_print_pages = npages;
    info.num_print_rows = nrows;
    info.num_print_cols = ncols;
    info.scale_factor = scaleFactor;
    info.is_scaled = isScaled;

    int pg = 0;
    MiscTSPageImages* img = images;
    MiscTSPageBreak const* rbk = pages->row_breaks;
    for (int r = 0; r < nrows; r++,rbk++)
	{
	info.print_rect.origin.y = rbk->offset;
	info.print_rect.size.height = rbk->size;
	info.first_print_row = rbk->first;
	info.last_print_row = rbk->last;

	MiscTSPageBreak const* cbk = pages->col_breaks;
	for (int c = 0; c < ncols; c++,cbk++,img++)
	    {
	    info.print_rect.origin.x = cbk->offset;
	    info.print_rect.size.width = cbk->size;
	    info.first_print_col = cbk->first;
	    info.last_print_col = cbk->last;
	    info.print_page = ++pg;

	    [self getImages:img info:&info];
	    }
	}

    return ok;
    }


//-----------------------------------------------------------------------------
// freePages
//-----------------------------------------------------------------------------
- (void)freePages
    {
    if (pages->col_breaks != 0)
	{
	free( pages->col_breaks );
	pages->col_breaks = 0;
	}
    if (pages->images != 0)
	{
	for (int i = pages->info.num_print_pages; i-- > 0; )
	    {
	    MiscTSPageImages const& img = pages->images[i];
	    if (img.page_header != 0) [img.page_header release];
	    if (img.page_footer != 0) [img.page_footer release];
	    if (img.col_titles  != 0) [img.col_titles  release];
	    if (img.row_titles  != 0) [img.row_titles  release];
	    }
	free( pages->images );
	pages->images = 0;
	}
    delete pages;
    pages = 0;
    }


//-----------------------------------------------------------------------------
// print:
//
// *FIXME*
//	Scale from true screen measurements (92 points per inch)
//	to true printer measurements (72 points per inch).
//-----------------------------------------------------------------------------
- (void)print:(id)sender
    {
    id const scroll = [self scroll];
    id del = [scroll responsibleDelegate:MiscDelegateFlags::DEL_WILL_PRINT];
    if (del != 0)
	[del tableScrollWillPrint:scroll];

    if ([self calcPages])
	{
	[self superPrint:sender];
	[self freePages];
	}

    del = [scroll responsibleDelegate:MiscDelegateFlags::DEL_DID_PRINT];
    if (del != 0)
	[del tableScrollDidPrint:scroll];
    }


//-----------------------------------------------------------------------------
// knowsPagesFirst:last:
//-----------------------------------------------------------------------------
- (BOOL)knowsPagesFirst:(int*)first last:(int*)last
    {
    if (pages != 0)
	{
	*first = 1;
	*last = pages->info.num_print_pages;
	return YES;
	}
    return NO;
    }


//-----------------------------------------------------------------------------
// rectForPage:
//-----------------------------------------------------------------------------
- (NSRect)rectForPage:(int)n
    {
    if (pages != 0)
	{
	MiscTablePrintInfo const& info = pages->info;
	if (0 < n && n <= info.num_print_pages)
	    {
	    n--;
	    int const r = (n / info.num_print_cols);
	    int const c = (n % info.num_print_cols);
	    MiscTSPageBreak const& cbk = pages->col_breaks[c];
	    MiscTSPageBreak const& rbk = pages->row_breaks[r];
	    return NSMakeRect( cbk.offset, rbk.offset, cbk.size, rbk.size );
	    }
	}
    return NSZeroRect;
    }


//-----------------------------------------------------------------------------
// drawPageBorderWithSize:
//-----------------------------------------------------------------------------
- (void)drawPageBorderWithSize:(NSSize)borderSize
    {
    if (pages != 0)
	{
	MiscTablePrintInfo const& info = pages->info;

	int n = [[NSPrintOperation currentOperation] currentPage] - 1;

	NSParameterAssert( 0 <= n );
	NSParameterAssert( n < info.num_print_pages );

	MiscTSPageImages const& img = pages->images[n];

	NSPrintInfo* pinfo = [[NSPrintOperation currentOperation] printInfo];
	float bottom = [pinfo bottomMargin];
	float top = info.page_size.height - [pinfo topMargin];
	float left = [pinfo leftMargin];

	if (img.page_header != 0)
	    {
	    top -= pages->page_header_height;
	    [img.page_header compositeToPoint:NSMakePoint( left, top )
		operation:NSCompositeCopy];
	    }

	if (img.page_footer != 0)
	    {
	    [img.page_footer compositeToPoint:NSMakePoint( left, bottom )
		operation:NSCompositeCopy];
	    bottom += pages->page_footer_height;
	    }

	if (img.row_titles != 0)	// FIXME: Check for centering
	    {
	    [img.row_titles compositeToPoint:NSMakePoint( left, bottom )
		operation:NSCompositeCopy];
	    left += pages->row_titles_width;
	    }

	if (img.col_titles != 0)	// FIXME: Check for centering
	    {
	    [img.col_titles compositeToPoint:
		NSMakePoint( left, top - pages->col_titles_height )
		operation:NSCompositeCopy];
	    }
	}
    }


//-----------------------------------------------------------------------------
// locationOfPrintRect:
//-----------------------------------------------------------------------------
- (NSPoint)locationOfPrintRect:(NSRect)rect
    {
    // FIXME: Deal with centering.
    NSPrintInfo* const pinfo = [[NSPrintOperation currentOperation] printInfo];
    return NSMakePoint(
		[pinfo leftMargin] +		// FIXME: This is not scaled.
		pages->row_titles_width,	// FIXME: This is scaled.

		[pinfo paperSize].height -	// FIXME: This is not scaled.
		[pinfo topMargin] -		// FIXME: This is not scaled.
		pages->page_header_height -	// FIXME: This is scaled.
		pages->col_titles_height -	// FIXME: This is scaled.
		rect.size.height );		// FIXME: This is scaled.
    }


//-----------------------------------------------------------------------------
// getPrintInfo
//-----------------------------------------------------------------------------
- (MiscTablePrintInfo const*)getPrintInfo
    {
    return (pages != 0) ? &(pages->info) : 0;
    }

@end
